[HN Gopher] size_t-to-int vulnerability in Linux's filesystem layer
       ___________________________________________________________________
        
       size_t-to-int vulnerability in Linux's filesystem layer
        
       Author : jwilk
       Score  : 283 points
       Date   : 2021-07-20 13:00 UTC (9 hours ago)
        
 (HTM) web link (www.openwall.com)
 (TXT) w3m dump (www.openwall.com)
        
       | cesarb wrote:
       | This kind of issue is the reason why some more modern languages
       | like Rust or Go do not have implicit narrowing conversions. For
       | instance, on Rust, trying to simply pass an usize (Rust's
       | equivalent of size_t) to a function which expects an i32 (Rust's
       | equivalent of int) will not compile; the programmer has to write
       | "size as i32" (Rust's equivalent of "(int) size"), which makes it
       | explicit that it might truncate the value at that point.
       | 
       | (Some Rust developers argue that even "size as i32" should be
       | avoided, and "size.try_into()" should be used instead, since it
       | forces the programmer to treat an overflow explicitly at runtime,
       | instead of silently wrapping.)
        
         | pjmlp wrote:
         | Languages of the same age or older than C, also have explicit
         | narrowing, but apparently that was seen as programming with a
         | straightjacket.
        
         | mytailorisrich wrote:
         | The compiler can issue warnings for this.
         | 
         | This os why in C it is a good practice to enable all compiler
         | warnings and to have the compiler treat warnings as errors.
        
           | viraptor wrote:
           | In theory but not in practice if you're distributing your
           | apps sources.
           | 
           | If you write for C compiler Foo 8, there's a decent chance
           | Foo 9 will raise a warning which didn't exist before. Now you
           | have to handle "why doesn't this compile" issues and
           | distributions have to patch your sources to do future
           | releases. And that's ignoring bugs like GCC in the past where
           | in some versions you could not satisfy specific warnings.
        
         | titzer wrote:
         | This is a deep hole for language design.
         | 
         | I thought about this very, very carefully when designing
         | Virgil[1]'s numerical tower, which has both fixed-size signed
         | and unsigned integers, as well as floating point. Like other
         | new language designs, Virgil doesn't have any implicit
         | narrowing conversions (even between float and int). Also, any
         | conversions between numbers include range/representability
         | checks that will throw if out-of-range or rounding occurs. If
         | you want to reinterpret _the bits_ , then there's an operator
         | to _view_ the bits. But conversions that have to do with
         | "numbers" then all make sense in that numbers then exist on a
         | single number line and have different representations in
         | different types. Conversion between always preserve numbers and
         | where they lie on the number line, whereas "view" is a bit-
         | level operation, which generally compiles to a no-op.
         | Unfortunately, the implications of this for floating point is
         | that -0 is not actually an integer, so you can't cast it to an
         | int. You must round it. But that's fine, because you always
         | want to round floats to int, never cast them.
         | 
         | [1] https://github.com/titzer/virgil
        
         | baby wrote:
         | I wish clippy had a lint against downcasts specifically. I aso
         | like that rust has no "int" type
        
         | phkahler wrote:
         | This wouldn't be a problem if "int" was defined as the same
         | size as size_t. The solution is probably to change all those
         | functions to take a parameter of size_t instead of int.
         | 
         | IMHO one should always be using C99 types instead of int, but
         | Linux predates that.
         | 
         | Also, shouldn't that implicit conversion cause a compiler
         | warning?
        
           | chippiewill wrote:
           | > IMHO one should always be using C99 types instead of int,
           | but Linux predates that.
           | 
           | "Always" is a strong way of putting it, there are often times
           | where it makes sense to use the platform's "natural" word
           | sizes (which is the entire point of having `int` `long` `long
           | long` etc.)
        
             | phkahler wrote:
             | >> there are often times where it makes sense to use the
             | platform's "natural" word sizes
             | 
             | In those cases we probably don't care about the full range
             | of the larger types, so it doesn't hurt to use the smallest
             | type for a range of expected values. If it does make a
             | difference, the program will behave differently when
             | compiled on a different arch or even a different compiler.
             | 
             | But maybe "generally" instead of "always". OTOH even I am
             | guilty of using an int to loop over an array.
        
           | tialaramex wrote:
           | Linux apparently provides a similarly named set of sized
           | integer types to Rust, ie: s8 u8 s16 u16 s32 u32 s64 u64
           | 
           | But of course getting C programmers to use these integer
           | types rather than the ones they grew up with isn't easy.
        
             | foobiekr wrote:
             | I did a lot of C - a _lot_ - in the mid-90s through the mid
             | /late 2000s and have never seen any sizable C code base
             | where explicit sizes were not the norm throughout -
             | u_int32_t etc. I think this is not the problem, it's the
             | implicit conversions.
        
           | cesarb wrote:
           | > This wouldn't be a problem if "int" was defined as the same
           | size as size_t.
           | 
           | That would lead to a hole in the type sequence (char <= short
           | <= int <= long <= long long) for 64-bit targets (where int is
           | the 32-bit type while size_t is 64 bits).
           | 
           | > IMHO one should always be using C99 types instead of int,
           | but Linux predates that.
           | 
           | On the other hand, on Linux "long" has always been defined as
           | the same size as size_t, so using "long" instead of "int"
           | everywhere could also be an option.
        
           | kenniskrag wrote:
           | so you mean int_fastX_t or int_leastX_t?
        
           | pcwalton wrote:
           | > This wouldn't be a problem if "int" was defined as the same
           | size as size_t.
           | 
           | ILP64 causes a lot of problems, most notably needlessly-
           | increased memory usage and, in C, the inconvenience of
           | requesting a 32-bit type when int is 64-bit. It's rather
           | uncommon to actually need the extra 64-bit range _except_
           | when describing pointer addresses and memory /disk sizes,
           | both of which benefit from an explicit intptr_t/size_t type
           | for readability if nothing else.
        
             | saagarjha wrote:
             | ILP64 also solves a lot of problems if you don't define
             | overflow to be UB.
        
         | marcosdumay wrote:
         | I think Rust has no language construction for that, but the
         | best implementation of "size as i32" should fail on overflow.
         | 
         | In Haskell I would use an exception, and mark the function as
         | unsafe, but the stdlib seems to disagree with me here.
        
         | derefr wrote:
         | > Some Rust developers argue that even "size as i32" should be
         | avoided, and "size.try_into()" should be used instead, since it
         | forces the programmer to treat an overflow explicitly at
         | runtime, instead of silently wrapping.
         | 
         | It's important to still have the option for efficient
         | truncating semantics, though; some software (e.g. emulators)
         | needs to chunk large integers into 2/4/8 smaller ones, and
         | rotation + truncating assignment is usually the cheapest way to
         | do that.
         | 
         | But, importantly, this is a _rare_ case. _Most_ software that
         | does demoting casts does not mean to achieve these semantics.
         | 
         | So I wonder -- are there any low-level/systems languages where
         | a demoting cast with a generated runtime check gets the
         | simple/clean syntax-sugared semantics (to encourage/favor its
         | use), while truncating demotion requires a clumsier syntax (to
         | discourage its use)?
        
           | chippiewill wrote:
           | > are there any low-level/systems languages where a demoting
           | cast with a generated runtime check gets the simple/clean
           | syntax-sugared semantics (to encourage/favor its use), while
           | truncating demotion requires a clumsier syntax (to discourage
           | its use)?
           | 
           | Not exactly the same thing, but in a related area C++ does
           | this a bit.
           | 
           | In C++ you can always still do a c-style cast `(int)
           | some_var` (and the implicit casts obviously), but in general
           | you're meant to use the C++ style explicit casts like
           | `static_cast` and `const_cast`. These are generally tidy, but
           | the most powerful and dangerous of these casts is
           | deliberately awkwardly named as
           | `reinterpret_cast<int>(some_var)` rather than something
           | terse.
           | 
           | It's always easy to spot during a code review.
        
             | gumby wrote:
             | You can have your compiler warm about c-style casts
        
           | tialaramex wrote:
           | Right, this is a small infelicity in Rust, it is easier to
           | write                 let x = size as i32;
           | 
           | ... even if what you meant was closer to                 let
           | x: i32 = size.try_into().expect("We are 100% sure size is
           | small enough to fit into x");
           | 
           | But at least it isn't C or C++ where you might accidentally
           | write                 x = size;
           | 
           | ... and the compiler doesn't even warn you that size is
           | bigger than x and you need to think about what you intended.
           | 
           | It's really hard to fix this in C++. Some of the Epoch
           | proponents want to do so using epochs to get there, basically
           | you'd have a "new" epoch of C++ in which narrowing must be
           | explicit, and old code would continue to have implicit
           | narrowing so it doesn't break.
        
             | dkirill wrote:
             | > and the compiler doesn't even warn you that size is
             | bigger than x
             | 
             | That's not true tho, compiler with reasonable flags set
             | will definitely warn you and if you _really_ don 't like
             | this kind of code you can force compiler to issue an error
             | instead
        
               | tialaramex wrote:
               | Fair point although it seems "reasonable" varies from one
               | platform to another, it doesn't warn out of the box for
               | me but people have reported MSVC gets warnings here.
        
               | simias wrote:
               | What flags you have in mind? Because this code doesn't
               | generate any warning with GCC 11.1.0 with -Wall -Wextra:
               | int main(void) {             int some_int = 1234567;
               | char c = some_int;                  return c;         }
        
               | ridiculous_fish wrote:
               | -Wconversion will do it.
        
               | stjohnswarts wrote:
               | -Wconversion will catch this
        
               | aaronmdjones wrote:
               | -Wall -Wextra -Wpedantic does not enable all diagnostics.
               | 
               | This is GNU's idea of "all".
               | 
               | Contrast to Clang's -Weverything, which will.
        
               | lazide wrote:
               | It seems though that the point is made, right? Even
               | 'good' approaches miss on what should be a clear 'whoa,
               | are you sure?' type warning. There are a lot of footguns
               | wandering around in C/C++ land.
        
               | BoorishBears wrote:
               | No, the point was you want don't get a warning and it
               | will silently wrap. You can scroll up if you've
               | forgotten.
               | 
               | And it is false. My default configuration C++ project
               | created in Clion shows it very clearly, and even pesters
               | to use int32/int64 over int/long.
               | 
               | But as usual the default fallback when you're wrong about
               | C++ is "uh yeah but lotta footguns amirite"
               | 
               | As if there aren't enough that we need to start making
               | them up...
        
               | gumby wrote:
               | > This is GNU's idea of "all".
               | 
               | Unfortunately, over the years people baked the semantics
               | of -Wall into their builds so new diagnostics could not
               | be added to that flag.
               | 
               | And clang's -Weverything shows how the opposite can fail
               | as well
        
               | not2b wrote:
               | There are some very wrong-headed warning options in gcc,
               | such that turning them on and avoiding getting them will
               | make your code worse. So -Wall means 'all recommended
               | warnings'.
               | 
               | Also there are some warnings that won't be produced if
               | you compile without optimization, because the needed
               | analysis isn't performed.
        
               | rocqua wrote:
               | The reason for this decision is so that compiler upgrades
               | with -Wall and -Werror don't break builds.
               | 
               | I can see the reason behind it, but I feel that this
               | behavior is something you opt into when you use -Werror.
        
               | derefr wrote:
               | > The reason for this decision is so that compiler
               | upgrades with -Wall and -Werror don't break builds.
               | 
               | It feels like the "right thing" here would instead be for
               | the compiler to allow build scripts to reference a
               | specific point-in-time semantics for -Wall.
               | 
               | For example, `-Wall=9.3.0` could be used to mean "all the
               | error checks that GCC v9.3.0 knew how to run".
               | 
               | Or better yet (for portability), a date, e.g.
               | `-Wall=20210720` to mean "all the error checks built into
               | the compiler as of builds up-to-and-including [date]."
               | 
               | To implement this, compilers would just need to know what
               | version/date each of their error checks was first
               | introduced. Errors newer than the user's specifier, could
               | then be filtered out of -Wall, before -Wall is applied.
               | 
               | With such a flag, you could "lock" your CI buildscript to
               | a specific snapshot of warnings, just like you "lock"
               | dependencies to a specific set of resolved versions.
               | 
               | And just like dependency locking, if you have some time
               | on your hands one day, you could "unlock" the error-
               | check-suite snapshot, resolve all the new error-checks
               | introduced, and then re-lock to the new error-check-suite
               | timestamp.
        
               | antoinealb wrote:
               | I think it might be more of an headache: what if somebody
               | fixes a bug in an analyzer so that it catches things it
               | used to miss ? Should it be a breaking change ?
               | 
               | Personally i would vote for "Wall with Werror" means no
               | guarantee for your build.
        
               | wheybags wrote:
               | The real solution: leave Werror off by default, activate
               | it only during CI builds
        
               | HanyouHottie wrote:
               | MSVC has this[1]. .NET code analyzers are also
               | versioned[2].
               | 
               | [1]:https://docs.microsoft.com/en-us/cpp/error-
               | messages/compiler... [2]:https://docs.microsoft.com/en-
               | us/dotnet/fundamentals/code-an...
        
               | 130e13a wrote:
               | keep in mind though that -Weverything is not intended to
               | be used in production:
               | https://quuxplusone.github.io/blog/2018/12/06/dont-use-
               | wever...
        
               | kllrnohj wrote:
               | Clang-tidy has a check for it:
               | https://clang.llvm.org/extra/clang-
               | tidy/checks/cppcoreguidel...
        
               | rualca wrote:
               | >What flags you have in mind?
               | 
               | -Wconversion
               | 
               | https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
               | 
               | This is common knowledge for ages. Any cursory Google
               | search returns countless answers.
               | 
               | Take this post made over a decade ago.
               | 
               | https://stackoverflow.com/questions/1730255/gcc-shouldnt-
               | a-w...
        
             | nly wrote:
             | Narrowing within { } initialization is forbidden C++ now
        
             | [deleted]
        
             | glaze wrote:
             | If you initialize it like this you get a warning:
             | 
             | x = { size };
        
             | jacoblambda wrote:
             | It can impact compile time performance but Boost Safe
             | Numerics provides some nice wrappers to prevent narrowing
             | (or restrict it to specific classes of narrowing) and throw
             | warnings or errors at compile time similar to what you see
             | in Rust.
        
           | frogblast wrote:
           | In swift:                   //  a is a UInt64         let a =
           | Int.random(in: 0..<Int.max)                  //  causes a
           | fatal runtime error if out of range, halts execution
           | let b = UInt32(a)                  //  returns a UInt32?,
           | which will be nil if out of range         let c =
           | UInt32(exactly: a)                  //  another approach for
           | exact conversion:         guard let d = UInt32(exactly: a)
           | else {             //  conversion failed             //
           | handle error and return             return         }
           | // 'd' is a UInt32 without any bits lost                  //
           | always succeeds, will return a UInt32, either clamped or
           | truncated.   Truncation just cuts off the high bits.
           | let e = UInt32(clamping: a)         let f =
           | UInt32(truncatingIfNeeded: a)
        
           | londons_explore wrote:
           | It would be nice if CPU's had an instruction for "read the
           | low 8 bits of this register, and require the high bits all be
           | zeros (otherwise throw an exception)".
           | 
           | Then safety is free...
        
             | derefr wrote:
             | What does "otherwise throw an exception" mean at the CPU
             | level?
             | 
             | I know Erlang's BEAM VM has a "fail jump pointer register",
             | where instructions that can fail have relative-jump offsets
             | encoded as immediates for those instructions, and if the
             | instruction "fails" in whatever semantic sense, it takes
             | the jump.
             | 
             | But most CPUs don't have anything like that.
             | 
             | Would you want it to trap, like with integer division by
             | zero?
             | 
             | CPU traps are pretty hard to handle in most language
             | runtimes, such that most compilers generate runtime checks
             | to work around them, rather than attempting to handle them.
        
               | [deleted]
        
               | simias wrote:
               | I assume they meant throwing an exception like divisions
               | by zero usually do, i.e. a hardware trap.
               | 
               | I always thought that overflows should be checked in
               | hardware, I suppose it's not a stretch to extend that to
               | truncation. It's controversial though, and obviously
               | mostly a thought experiment anyway unless we manage to
               | convince some CPU manufacturer to extend their ISA that
               | way.
               | 
               | MIPS does have (optional) trapping overflow on signed
               | add/sub overflow, so at least there's a small precedent
               | for it.
        
               | ridiculous_fish wrote:
               | Swift checks all arithmetic by default:
               | https://swift.godbolt.org/z/rW614G5aq
               | 
               | It seems obvious that future Apple CPUs will have
               | hardware support for this, if they don't already.
        
               | saagarjha wrote:
               | I don't see this happening unless it makes it into the
               | ARM ISA.
        
               | hermitdev wrote:
               | I'm not familiar with ARM ISA, but from the godbolt
               | disassembly, it doesn't look like anything special going
               | on here - just the ASM being generated. What's happening
               | here is it just does the add, jumps on overflow flag set
               | to an invalid opcode...
        
               | saagarjha wrote:
               | The suggestion was a custom instruction or architectural
               | extension to have this happen in hardware, rather than
               | needing to write out extra code for this.
        
               | gumby wrote:
               | I don't know the terms of Apple's license with ARM, do
               | you? I'm quite interested.
               | 
               | Given that Apple was one of the original founders of ARM
               | it's quite possible that their license allows much more
               | latitude anyone else's.
        
               | saagarjha wrote:
               | Adding new instructions to userspace programs is almost
               | certainly not going to fly. All of their extensions have
               | been hidden behind an opaque API, or limited to use in
               | the kernel.
        
           | titzer wrote:
           | In Virgil, casts are written "type.!(expr)". Casts between
           | numbers check ranges and roundability (for float<->int
           | conversion). Reinterpreting the bits is written
           | "type.view(expr)" and ignores signs, looks at the raw float
           | bits, etc.
           | 
           | edit: a cast will throw an exception if it fails, in case
           | that was not clear from context.
        
           | dathinab wrote:
           | As a interesting side note:
           | 
           | The "as" operator is often considered to have been a mistake.
           | Both because of unchecked casts and because of "doing to
           | much".
           | 
           | So I wouldn't be surprised if in the (very) long term there
           | will be a rust edition deprecating `as` casts (after we have
           | alternatives to all cast done with `as`, which are: Pointer
           | casts, dyn casts/explicit coercion and truncating integer
           | casts, for some we already have alternatives for on stable
           | for other not).
           | 
           | And for all who want to not have `as` today you can combine
           | extension traits (which internally still use `as`) + clippy
           | lint against any usage of `as`.
           | 
           | EDIT: I forgot widening integer casts in the list above ;-).
        
             | eloff wrote:
             | I would prefer not to see that happen, I'm fine with as and
             | the safer options as they are currently. It would be a big
             | job to update all the code in the wild when you want to
             | move to the newer edition.
        
               | cdirkx wrote:
               | Isn't this something that could be done automatically by
               | rustfix?
        
               | wizzwizz4 wrote:
               | You don't need to do that. Rust editions are fully
               | backwards-compatible, since they can depend on code from
               | different editions.
        
               | eloff wrote:
               | I'm aware of that, which is why I specified when you want
               | to move to the newer edition.
        
               | tialaramex wrote:
               | I don't see it as a big job. Am I wrong?
               | 
               | Imagine there's a suitable narrow::<type>() function
               | introduced which has the same consequence, always
               | narrowing, if your data was too wide it may drop
               | important stuff on the floor, and narrow() just says
               | that's too bad.
               | 
               | Rust 2030 can introduce narrow::<type>(), warn for
               | narrowing as usage and then Rust 2035 can error for as.
               | The Rust 2030 -> 2035 conversion software can consume
               | code that does { x as y } and write { x.narrow::<y>() }
               | instead. This code is not _better_ but it 's still
               | working in Rust 2035 and this explicit narrow() function
               | is less tempting for new programmers than as IMO.
        
               | scns wrote:
               | Yes, BUT the correct thing to do would be more tedious to
               | write that way.
        
         | mnw21cam wrote:
         | Add Java to that list.
        
           | csharptwdec19 wrote:
           | Same for C#. Any narrowing truncation needs to be an explicit
           | cast. Widening is typically allowed implicitly, although in
           | the case of the 'decimal' (128 bit struct representing a
           | 'higher precision' floating point) type you still need an
           | explicit cast from a 'double', since there are cases where
           | that conversion can still change the value or fail (i.e.
           | Infinity/NaN)
        
             | pjmlp wrote:
             | Microsoft is considering turning on by default checked
             | arithmetic in the 2022 Visual Studio templates, by the way.
        
           | paulddraper wrote:
           | Noo! This idea is super new and was invented by Go/Rust.
        
           | irogers wrote:
           | It can catch people by surprise that Java's narrowing
           | conversions may not preserve the sign. For example, the
           | following is broken:
           | 
           | class TimestampedObject implements
           | Comparable<TimestampedObject> { long timestamp; int
           | compareTo(TimestampedObject other) { return (int)(timestamp -
           | other.timestamp); } ... }
           | 
           | ErrorProne catches this:
           | https://errorprone.info/bugpattern/BadComparable
        
         | nly wrote:
         | Long and int are the same size on Windows. The fact that these
         | sizes aren't well defined is the cause of the issue.
        
         | mgraczyk wrote:
         | That's cool! And some languages like Go don't even allow
         | implicit widening conversions:
         | https://play.golang.org/p/a5C5jsHypmu
        
           | est31 wrote:
           | Rust doesn't allow them either: https://play.rust-
           | lang.org/?version=stable&mode=debug&editio...
           | 
           | Adding .into() works though, which is the recommended method
           | if the conversion can be statically guaranteed (otherwise
           | try_into should be used, which will become easier in the 2021
           | edition as the TryInto trait will become part of the
           | prelude).
        
             | ridiculous_fish wrote:
             | into() does _not_ work from size. It 's rather frustrating
             | in practice.
             | https://stackoverflow.com/questions/62832438/why-is-rusts-
             | us...
        
               | masklinn wrote:
               | > It's rather frustrating in practice.
               | 
               | All explicit conversions, more so fallible, are
               | "frustrating in practice" especially when coming from a
               | language without those foibles.
               | 
               | But given the semantics of usize/isize, it is perfectly
               | reasonable, nay, a good thing, that they're considered
               | neither widenings nor narrowings of other numeric types.
        
               | ridiculous_fish wrote:
               | usize should be Into<u64> iff usize is <= 64 bit.
               | 
               | usize is already on that hook: `0x100000000usize` will
               | fail to compile in 32 bit, so you already risk compile
               | errors when switching archs.
               | 
               | As it is I'm just writing `as u64` which is clearly
               | worse.
        
               | eloff wrote:
               | Yeah, that is frustrating since there are no platforms
               | where that would fail today, and it's hard to imagine why
               | we would ever want one with 256bit pointers.
               | 
               | We don't even use full 64bit pointers today on x64.
        
               | pornel wrote:
               | Even worse, there are platforms with >=128-bit pointers
               | but 64-bit address space. Rust has chosen usize to be
               | uintptr_t rather than size_t, even though it mostly uses
               | it as if it was size_t. A ton of code is going to subtly
               | break when these two sizes ever differ. Rust is likely
               | already doomed on >64-bit platforms, and is going to be
               | forced to invent its own version of a LLP64 workaround.
        
               | fulafel wrote:
               | Maybe a fat pointer llvm target ala SoftBound running on
               | a machine with 128 bit bare pointers, like as/400.
        
               | est31 wrote:
               | When 16 bit computers went to 32 bit, people probably
               | thought that one wouldn't ever need 64 bit computers
               | either. That being said, by the time 128 bit run out we
               | have probably boiled earths oceans :).
               | 
               | You could think of checked memory models where half of
               | the 256 bit address is a 128 bit random key needed to
               | access some allocation, or maybe even a decryption key.
               | Similar things are done with the extra space of x64 as
               | well.
               | 
               | Also, 128 bit numbers are still quite uncommon in Rust.
               | Easy conversion of usize to them wouldn't be that useful
               | if conversions of the other number types don't work.
        
               | mgraczyk wrote:
               | The Ethereum virtual machine addresses it's storage with
               | 256 bits, so there's one wild example. Although in this
               | case you'd probably not want to use usize directly to
               | represent storage.
        
               | eloff wrote:
               | I'm not familiar with the Ethereum virtual machine, are
               | these really memory pointers? Surely it's represented
               | differently?
        
               | mgraczyk wrote:
               | EVM is a stack machine, and it has durable storage which
               | is addressed using 256 bit "pointers". So you can do
               | something like                   push x // 256 bit
               | constant         LOAD         // top of stack now
               | contains storage[x]
        
         | ronsor wrote:
         | Shout out to Zig, which requires explicit casting also.
        
         | WalterBright wrote:
         | D doesn't allow implicit narrowing conversions. The user has to
         | have an explicit cast, like `cast(int) size`. Cast is made into
         | a keyword so all those explicit conversions can be found with a
         | simple grep.
         | 
         | We consider it best practice to try and organize the types such
         | that explicit casts are minimized.
        
         | cjensen wrote:
         | C/C++ compilers commonly have warnings for narrowing
         | conversions, and separate warnings for mixing signed/unsigned
         | conversions for same-sized values.
         | 
         | While some folks aren't too fussed about warnings like this,
         | those folks generally aren't writing secure code like kernels.
         | I'm very surprised that kind of conversion was permitted in the
         | code.
        
       | mnw21cam wrote:
       | I love the little nugget in the mitigations section. You can plug
       | the hole for a normal filesystem, but then FUSE filesystems have
       | an additional problem: "if an attacker FUSE-mounts a long
       | directory (longer than 8MB), then systemd exhausts its stack,
       | crashes, and therefore crashes the entire operating system (a
       | kernel panic)."
       | 
       | If there's one place other than the kernel where truly defensive
       | programming should be applied, it is systemd.
        
         | josefx wrote:
         | What the hell is systemd doing that a 8MB long file path can
         | exhaust its stack? Is it doing some recursive parsing or is it
         | just doing something plain stupid like using a VLA to store
         | user provided data?
        
           | megous wrote:
           | Probably unbounded alloca() as always.
        
             | merb wrote:
             | was that a guess? wtf... btw. it would probably be hard to
             | make the same mistake in rust. unless you write your own
             | code for strings or use strdupa, too via libc.
             | 
             | I also do never understand why some libraries use "faster"
             | methods everywhere, unless safer ones. it's not like all
             | interfaces to systemd would need to be fast. but they
             | should be secure.
        
               | megous wrote:
               | Yes. It happened before, so it was not exactly hard to
               | guess.
               | 
               | https://capsule8.com/blog/exploiting-systemd-journald-
               | part-1...
        
             | megous wrote:
             | Yep: https://github.com/systemd/systemd/commit/b34a4f0e6729
             | de292c...
             | 
             | strdupa(input) without any length check
             | 
             | Fix is to replace it with unbounded malloc() instead of
             | checking for sane length first.
        
               | stjohnswarts wrote:
               | Good find thanks for sharing. And everyone at work gripes
               | about me carrying the size around with a lot of my
               | variables in the form of a struct. It's strictly a
               | reminder to always be checking the size since I'm
               | juggling with shotguns.
        
         | unmole wrote:
         | Wait how does a user space daemon exhausting its stack lead to
         | a kernel panic?
        
           | paulddraper wrote:
           | Because it's PID 1.
        
           | mjevans wrote:
           | Poorly designed security architecture and division of labor.
           | A more idealized init / systemd would have all of the
           | execution flow of PID 1 mathematically provably correct, and
           | correspondingly have as small a footprint as possible there.
           | All additional functions would run under one or more child
           | processes (where the bulk of systemd would execute).
        
           | monocasa wrote:
           | PID 1 is special in Unix systems. As the parent of all other
           | processes (and child to none) it's not clear to the kernel
           | what should happen when it exits.
        
           | comex wrote:
           | Because the kernel intentionally panics [1] if the init
           | process would otherwise exit in any way - whether because it
           | called exit(), it was killed, or, in this case, it crashed.
           | 
           | This is likely because Unix semantics treat the init process
           | specially: any process whose parent dies is re-parented to
           | the init process. It's not clear what should happen to these
           | processes if the init process itself went away, so the kernel
           | just gives up.
           | 
           | [1] https://elixir.bootlin.com/linux/latest/source/kernel/exi
           | t.c...
        
             | Denvercoder9 wrote:
             | I wonder if just restarting PID1 could be viable
             | alternative?
        
               | jerf wrote:
               | It's generally dangerous to restart PID1 in an enviroment
               | where, by definition, something happened to PID1 that it
               | wasn't expecting. The state of the system is now
               | unreliable, and it's exactly the sort of unreliable in
               | exactly the right place that tends to lead to whopping
               | security issues. Far too easy to end up with "Crash PID1
               | with 'blah blah blah', then when it restarts it ends up
               | doing bad things X & Y".
        
               | cameronh90 wrote:
               | Perhaps a distinction here can be made between running as
               | a server OS and a desktop OS.
               | 
               | In a server I generally want a crash immediately. But on
               | a desktop I'd rather it limp along and give me a chance
               | to finish writing my Hackernews post.
        
               | londons_explore wrote:
               | On a server I'd usually want it to limp along too...
               | Better fire an alert but keep happy customers than cause
               | a massive outage just because of someone's overly strict
               | checkfail...
               | 
               | It depends really what your server does, and what the
               | consequences of it doing the wrong thing are.
        
         | CrazyPyroLinux wrote:
         | systemd? More like systemK! But seriously, are non-systemd
         | systems not vulnerable to the FUSE portion of this?
         | (CVE-2021-33910)
        
           | saurik wrote:
           | FWIW, I feel like your comment is responding to an implicit
           | critique of systemd, but even if one was warranted I didn't
           | read that comment as implying such (as the premise would just
           | be that systemd is a key place in the stack where you would
           | need to be super careful, not that it is somehow less careful
           | than other projects... even if I might claim as such for at
           | least logging ;P); it could be that I am misinterpreting your
           | comment, though?
        
       | Faaak wrote:
       | Anyone knows how to try the PoC
       | (https://www.openwall.com/lists/oss-security/2021/07/20/1/1) ?
       | 
       | For me it crashes into the fork_userns:177
       | 
       | PS: don't need to downvote. Sometimes managers want you to prove
       | that there's a need to patch. It's dumb but it's what it is
        
         | ploxiln wrote:
         | Your linux distro may already have unprivileged user namespaces
         | disabled. See the "mitigations" section of the post, and check
         | /proc/sys/kernel/unprivileged_userns_clone
        
       | airocker wrote:
       | Can we not have one integer type in c that can grow like in js as
       | required and become bigint if too big?
        
         | wyldfire wrote:
         | Yes, arbitrary precision integers could theoretically become a
         | C language/library feature. But let's say that it gets proposed
         | and accepted and it's integrated into popular compiler
         | toolchains. The kernel wouldn't leverage it here or likely
         | anywhere else because of its cost.
         | 
         | A newly designed OS kernel could perhaps take on this kind of
         | feature. This would be the kind of OS that could be formally
         | verified and would be willing to pay that runtime cost for
         | arbitrary precision.
        
           | airocker wrote:
           | Perhaps such type of things have been put in Kernel before.
        
           | kaba0 wrote:
           | C doesn't have the abstraction power to make anything with
           | such a datatype.
        
         | dbrgn wrote:
         | Haha, yeah JS integers "can grow". Kind of. And then they bite
         | you in the worst way possible. Especially when combined with
         | cryptography (e.g. nonces).
         | 
         | Try this:                 >> const x = 288230376151711740;
         | >> x == x + 1       true
         | 
         | Or this:                 >> 2\*1024       Infinity
         | 
         | JS doesn't even have integers. It only has floats. JS is by far
         | the worst language I know of when it comes to integer support.
         | 
         | If you want a better example of arbitrary precision integers,
         | try Python, for example.
        
           | _moof wrote:
           | I was working at Twitter when we learned this the hard way.
           | We switched to a separate service for generating tweet IDs
           | (instead of MySQL sequences), which for various distributed
           | systems reasons meant IDs started taking up more bits. And
           | when we stuck those bigger IDs into JSON responses in the
           | API... well, we learned the dumbest lesson possible about the
           | intersection between JSON, JavaScript, and double-precision
           | floating-point. Later that day the "id_str" fields were born.
        
             | dbrgn wrote:
             | Yep, I can totally relate. I found multiple bugs in a
             | MessagePack JS implementation that were related to this
             | issue.
             | 
             | I also worked on a project once where unique IDs of objects
             | could get quite large (because they were random u64
             | integers). Those IDs were serialized and sent to a browser.
             | Sometimes two objects were viewed as "the same" in the
             | browser application because their IDs were truncated by the
             | floating point precision issue.
        
           | johndough wrote:
           | Neat trick.                   Math.pow(2, 53) == Math.pow(2,
           | 53) + 1
           | 
           | This is a bit clearer I think. The addition just barely
           | overflows the 53 mantissa bits of the IEEE 754 double
           | precision floating point number.
           | 
           | By the way, JS has BigInts these days, which are supported by
           | all major browsers: https://caniuse.com/bigint
           | >> const x = 288230376151711740n;         >> x == x + 1n
           | false
        
             | mirekrusin wrote:
             | 2**53 == 2**53+1
             | 
             | ^^ even more clear.
             | 
             | That's why _Number.isSafeInteger(a)_ exists.
             | 
             | Integers up to +/- 9007199254740991 (
             | _Number.MAX_SAFE_INTEGER_ ) are fine.
        
           | thayne wrote:
           | GP may have been referring to BigInt.
           | 
           | Although, C does have an equivalent to that: the GNU MP
           | library (and probably others as well).
        
             | airocker wrote:
             | I was referring to bigint not js bigint, checking for
             | overflows if optimized could be just 1 instruction.
        
         | vbezhenar wrote:
         | We can. But it would be very inefficient. There's no point to
         | use C at all, if you can accept that level of performance.
        
         | jerf wrote:
         | We don't need arbitrary-sized integers; we need exceptions on
         | overflow (or underflow, but I'll stick to overflow for the rest
         | of this post) to be the default, or similar language features
         | as appropriate.
         | 
         | I for one am tired of the chicken & egg issue of "CPUs don't
         | support efficient overflow checking because nobody uses it, so
         | it's slow" and "Overflow checking is slow because the CPU
         | doesn't support it, so nobody uses it". For all the other good
         | security work done in both software and hardware, much for
         | things far more complex than this, this seems like an
         | absolutely batshit insane oversight considering the
         | cost/benefits for fixing this.
        
           | airocker wrote:
           | CPU would raise a signal just as null pointer exception? And
           | it seems a lot of code (for eg, safeintadd metioned below)
           | assumes no exception. Would not all that code get messed up?
           | 
           | Would it be possible to just silently replace with arbitrary
           | sized integer and not break any code like safeintadd?
        
             | dahfizz wrote:
             | CPUs don't raise exceptions, that is a software concept.
             | CPUs do have traps, like for division by zero, but those
             | are not exceptions in the way you think.
             | 
             | Null pointers are also handled by the kernel, not the CPU.
             | Its called a segmentation fault because you are trying to
             | access a memory segment that the OS doesn't want you to.
        
               | airocker wrote:
               | I dont think kernel handles NPEs:
               | https://stackoverflow.com/questions/63091446/what-
               | happens-at...
        
             | jerf wrote:
             | In C, you can't "just" replace with arbitrary-sized
             | integers. They are fundamentally different memory shapes.
             | 
             | You may not be able to turn enforcement on for all code
             | immediately. There's even the rare bits of code that depend
             | on current overflow behavior. (Due to our human brains and
             | the fact that we can easily name these bits of code, making
             | the cognitively available, people often grotesquely
             | overestimate the amount of code that operates this way. I'm
             | sure it's only a matter of how _many_ zeros belong in the
             | 0.001%.) But we need this support to be available for code
             | to be turn on easily and cheaply.
             | 
             | But what really boggles my mind, again given all the
             | security work we've done, is that the reaction to this
             | remains a combination of silence and "we can't do that!",
             | when it seems to me the reaction ought to be "well _duh_
             | jerf we all know that already. " I don't get this. I don't
             | get this attitude at all. This is a _huge_ source of
             | errors, a good fraction of which are security bugs, and
             | nobody seems to care. Incomprehensible. This is, arguably,
             | the _number one_ thing that could be getting changed right
             | now to fix security issues, and I just get slack-jawed
             | "whaaaa?" in response to the idea.
             | 
             | One hundred plus one hundred is not negative fifty six!
             | Here we are trying to hold together megabytes upon
             | megabytes of security-critical code in a world where 100 +
             | 100 = -56. Is it any wonder that writing any sort of code
             | to maintain security invariants is tough in an environment
             | where 100 + 100 = -56?
        
               | airocker wrote:
               | I feel like carefully planned with compiler level
               | errors(not warnings), and some glibc non-backward
               | compatible changes. we can achieve this in software
               | layer.
        
         | gruez wrote:
         | >that can grow like in js
         | 
         | I thought js "integers" are just floating point numbers?
        
           | OnlyLys wrote:
           | I believe the poster you were replying to was talking about
           | JS's new `BigInt` type.
           | 
           | https://developer.mozilla.org/en-
           | US/docs/Web/JavaScript/Refe...
        
             | vbezhenar wrote:
             | Good example of that feature is Python. Also, AFAIR, Scheme
             | (and probably some other lisps) support it.
        
         | wyager wrote:
         | No, because that would require implicit dynamic allocation,
         | which would defeat the entire point of using C.
        
           | airocker wrote:
           | Only when limits are getting breached, otherwise it is one if
           | statement extra on every access. This happens in Java and
           | other languages.
        
             | wyager wrote:
             | It doesn't matter if it only happens occasionally. It's
             | completely inappropriate for C, which has no implicit
             | memory allocation. There's not even a clear way to include
             | implicit allocation in the semantics of C.
        
         | infamouscow wrote:
         | We can do this in software because there's virtually no limit
         | to the abstractions people make, but hardware doesn't work like
         | that. At some point in the software stack we need to draw a
         | line and say this is a 64-bit signed integer that hardware can
         | understand.
        
           | airocker wrote:
           | But should it be everywhere including the fs/virtual fs
           | layer? Could we limit it only to device drivers? Not a kernel
           | expert here and would love to hear thoughts.
        
             | dahfizz wrote:
             | This doesn't have anything to do with filesystems or
             | kernels.
             | 
             | The x86 assembly uses fixed width immediates. CPU registers
             | are a fixed width. For any code to compile and run, it
             | needs to make decisions about how large stack frames need
             | to be, and how much heap memory to allocate.
             | 
             | This was the parent's point about abstractions. You can
             | make a library that pretends to be a variable sized
             | integer, but to implement such a library you need to make a
             | decision about how much space to allocate and the width of
             | variables in order to compile. There is no getting away
             | from how the hardware works.
        
       | Zababa wrote:
       | > Unfortunately, this size_t is also passed to functions whose
       | size argument is an int (a signed 32-bit integer), not a size_t.
       | 
       | Is this the type of things that could be caught by a linter or
       | strict compilation rules? This seems to be to be a failure of the
       | type system.
        
         | gruez wrote:
         | AFAIK most compilers by default will output a warning in this
         | case.
        
           | usefulcat wrote:
           | GCC's -Wconversion has some issues. For example, good luck
           | getting gcc to /not/ emit a warning for this code, in C or
           | C++. I have yet to find the appropriate cast to avoid a
           | warning. Clang does not warn for this.
           | typedef struct {             unsigned value : 4;         } S;
           | void foo(S* s, unsigned value) {             // error:
           | conversion from 'unsigned int' to 'unsigned char:4' may
           | change value             s->value = value;         }
           | 
           | I mean, I guess I can see the rationale.. it's just annoying
           | to have to resort to using pragmas to turn off -Wconversion
           | whenever I need to assign to a bitfield.
        
             | Denvercoder9 wrote:
             | Does it still warn if you do `s->value = value & 0x0F`?
             | That seems like a reasonable alternative to pragmas if it
             | works.
        
           | im3w1l wrote:
           | Too bad that most projects are so full of integer size and
           | signedness warnings that people get warning fatigue and just
           | completely ignore them.
        
           | loeg wrote:
           | GCC and Clang (the predominant Linux and Mac compilers)
           | mostly don't warn by _default_ , you need -Wall or other
           | flags (-Wextra, -Weverything, or specific flags like
           | -Wconversion).
        
           | jeffbee wrote:
           | I don't see any warnings for this narrowing parameter
           | conversion.                 #include "stddef.h"
           | short foo(short a) { return a % 42; }              size_t
           | bar(void) {           size_t sz = ~0UL;           return
           | foo(sz);       }
           | 
           | https://godbolt.org/z/3ec9v8Pa4
        
             | xeeeeeeeeeeenu wrote:
             | It warns if you add -Wconversion. Unfortunately, that flag
             | generates lots of false positives (at least in gcc), so
             | using it isn't always a good idea.
        
               | jeffbee wrote:
               | It isn't ever a good idea. C programmers are just
               | supposed to know about the conversion and promotion
               | rules, every time they add a line, even though the rules
               | are actually insanely complicated. Compiler warnings
               | can't overcome this because programmers have no way of
               | just discovering one of GCC's 726 warning flags, only a
               | few of which are enabled by -Wall and -Wextra, most of
               | which are way too noisy to serve a useful purpose.
        
               | ototot wrote:
               | In my own small projects, I always add -Wconversion to
               | build configuration. I think the false positive is
               | affordable when you start from small piece of code.
        
               | morio wrote:
               | What kind of false positives are you seeing with gcc?
               | 
               | Personally I have never seen gcc spitting out a false
               | positive. IMO it's always a good idea to explicitly
               | downcast even if you know that it's 'safe'. That way
               | someone else will see instantly what's going on. The fact
               | that Rust requires it should tell us something.
        
               | xeeeeeeeeeeenu wrote:
               | For example, I've just reported this:
               | https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101537
               | 
               | You can find more cases in the bugtracker. To be fair, it
               | seems many of them were fixed in recent releases.
        
             | saurik wrote:
             | Yeah; AFAIK you need something like clang-tidy's
             | cppcoreguidelines-narrowing-conversions check (which
             | everyone should definitely be using). (edit: But I'm wrong!
             | That check is apparently similar to the -Wconversion
             | mentioned by someone else.)
        
             | gruez wrote:
             | On MSVC, default project settings I get:
             | warning C4267: 'argument': conversion from 'size_t' to
             | 'short', possible loss of data
             | 
             | https://godbolt.org/z/nYeWT7zv6 (/W3 is the default warning
             | level when creating a new project)
             | 
             | I saw these warnings so often that I assumed that _every_
             | compiler had them.
        
           | nly wrote:
           | They can't warn by default, because on some platforms
           | long->int isnt a narrowing conversion.
        
             | aaronmdjones wrote:
             | The compiler knows what platform it's compiling the code
             | for, though.
        
         | ainar-g wrote:
         | At this point, you can--and probably should--consider C and
         | "C-with-analysers" two different languages. If you use static
         | (and dynamic, if you have tests) code analysis, making software
         | without these issues is way easier, and doing so without these
         | tools is essentially impossible. Both because of the C language
         | itself as well as because of the culture of "bytes go brrr" and
         | "just use a debugger" that a lot of middle-level C programmers
         | have in my experience.
        
         | kllrnohj wrote:
         | Yes, it is the type of thing caught by a linter or strict
         | compilation rules.
         | 
         | https://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidel...
         | 
         | But strict compilation rules (eg, clang's -Weverything) mainly
         | only work if you treat them as errors (so -Werror), and then
         | some of those strict are then also questionable at best, and
         | just outright annoyingly wrong at worst. For example, unused
         | parameter warnings on virtual methods are a waste of time to
         | deal with. It's not a symptom of a bug most of the time, so it
         | being an error just generates workaround churn or you end up
         | just disabling the warning and then maybe that bites you the
         | few times it would have pointed out an actual issue.
         | 
         | Beyond the blanket ones like clang's -Weverything, it can
         | otherwise be a job to keep up with compiler upgrades and the
         | _vast_ number of warning options they have.
        
       | habibur wrote:
       | > deep directory structure whose total path length exceeds 1GB
       | [...]
       | 
       | Oh, I didn't know Linux supports GB long path name. On Windows
       | it's limited to something like MAX_PATH_LENGTH which was defined
       | as 200+ chars when I worked on it.
        
         | Already__Taken wrote:
         | Windows lifted that limit a few years ago
        
           | paavohtl wrote:
           | AFAIK the limit has been lifted since Windows NT (so since
           | the 90s), but only if you use the obscure NT path prefix
           | (\\\?\\).
        
           | divingdragon wrote:
           | IIRC you still need to use a special flag to enable it,
           | right?
        
           | dragonwriter wrote:
           | > Windows lifted that limit a few years ago
           | 
           | IIRC, its a mess because it was lifted inconsistently for
           | different access methods (APIs, and as a consequence UI/CLI
           | methods that depend on them), and at least for some in ways
           | which also are or were dependent on how paths are expressed.
           | 
           | So it is, or at least has historically been after it was
           | first "lifted", a minefield of inconsistent, surprising
           | behaviors with plenty of gotchas if you didn't treat it as if
           | it were still a limit.
        
         | formerly_proven wrote:
         | Windows goes to 260 chars, but the underlying system supports
         | something like 64K, which means you can have files on Windows
         | which are largely untouchable by built-in tools (Windows
         | Explorer, file dialogs etc.)
        
       | tinus_hn wrote:
       | Why does the compiler not warn if you use a 64 bit unsigned
       | integer when a 32 bit signed integer is required?
        
       | snvzz wrote:
       | $ ls -l /boot/vmlinuz-linux -rw-r--r-- 1 root root 9464864 Jul 16
       | 12:59 /boot/vmlinuz-linux
       | 
       | That's over 9MB, running in supervisor mode.
       | 
       | 2021, and people are still surprised every time a kernel bug with
       | security implications is found.
       | 
       | Maybe it is time to look at different OS designs. Large companies
       | such as Google (Fuchsia) or Huawei (HarmonyOS) have begun to pick
       | up on this.
        
       | melbourne_mat wrote:
       | I wonder if we're going to see some more of Torvald's excellent
       | management technique?
       | 
       | >PATCH 3.12 108/142] fs/seq_file: fallback to vmalloc allocation
       | 
       | >Signed-off-by: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
       | 
       | Oh maybe not this time :-)
        
       | veltas wrote:
       | B doesn't have these issues because it only has one integer size,
       | take that C.
        
         | spijdar wrote:
         | Well, for that matter the original C on PDP-11 wouldn't have
         | either, it also only had one integer size. We should all just
         | blame ISO C ;-)
        
       | drummer wrote:
       | C++ engineers check out: Cppcon 2018: Safe numerics by Robert
       | Ramey https://m.youtube.com/watch?v=93Cjg42bGEw
        
       | kvathupo wrote:
       | Cached mirror -
       | https://webcache.googleusercontent.com/search?q=cache:LwH96X...
       | 
       | I'm clueless about security: where does this fall on the scale of
       | non-issue to critical? It strikes me as tending towards the
       | latter, given that it enables unprivileged users to become root.
       | Any insight into past Linux Kernel vulnerabilities that were
       | severe?
        
         | vbezhenar wrote:
         | Local root escalation is very common vulnerability. It's almost
         | pointless to expect that attacker will not be able to escalate
         | his privileges given enough time, if he got user account
         | access. One weak layer of defense at most.
        
         | McGlockenshire wrote:
         | Pulling this attack off requires enough access to the machine
         | to either run the unprivileged commands needed to create the
         | exploit condition or to upload a binary/script that runs
         | unprivileged that in turn creates the exploit condition.
         | 
         | If the attacker already has that level of unauthorized access,
         | you're already doomed.
        
           | tptacek wrote:
           | Attacks like this break multitenant computing environments.
           | It's not a threat to your desktop computer or your phone. But
           | it can be a very big deal for hosting environments.
           | 
           | It also breaks sandboxing. To whatever extent you're trying
           | to run programs that are somehow jailed, so you can download
           | and run them without worrying about them taking over your
           | system, kernel LPEs break those assurances.
        
       | verytrivial wrote:
       | I honestly think all the automatic type promotion and conversion
       | rules of the C family should be officially classified as "cute",
       | namely an example of a childish simplification of a serious
       | issue. I'm a C++ programmer of 20+ years experience and I have
       | never, NEVER, caught myself thinking "gee, I'm glad I don't need
       | to cast this." You ALWAYS think it, you just don't TYPE it, and
       | that is the utterly wrong metric to optimise for. My 2c anyway.
        
         | MauranKilom wrote:
         | I've had my fair share of annoyance from situations like "I
         | have between one and four hard-coded insertions into this
         | vector, but the compiler yells at me if I try to store the
         | resulting size in an int".
         | 
         | Also, tangentially related is the signed/unsigned business
         | which tends to get in the way frequently. For example, OpenMP
         | 2.0 (the only OpenMP version you get to use with MSVC)
         | _requires_ loop indices to be signed, but both
         | std::vector::size and std::vector::operator[] deal with
         | unsigned integers. Casts guaranteed!
        
       | ryandrake wrote:
       | I've always thought implicit parameter conversion in general
       | (narrowing or otherwise) was fraught at worst, and code smell at
       | best. If some function takes a size_t, why are you passing
       | something other than a size_t to it? If your eventual call into
       | "code you don't control" takes type X, make sure the value that
       | you eventually pass is type X all the way through the code you do
       | control. Even casting is kind of the lazy way out. I used to be
       | pretty dogmatic about this and more than a few times got called a
       | pedantic nitpicker (size_t is just like an int, bro! Don't worry
       | about it--we got to ship in a week!). You can probably find
       | serious bugs in any C or C++ software project simply by turning
       | on the implicit cast warnings.
        
         | _kst_ wrote:
         | And it's not just parameter passing. It can apply to anything
         | that acts like an assignment. That includes assignment (of
         | course), parameter passing, and returning a value from a
         | function.
        
       | Deukhoofd wrote:
       | I see in the mail that Red Hat sent out patches to resolve this.
       | Are those patches already merged, or is this a CVE about a live
       | exploit?
        
         | GrayShade wrote:
         | Still not fixed in the mainline kernel, it seems.
        
           | megous wrote:
           | It's fixed in 5.13.4.
        
             | wolf550e wrote:
             | This is the fix, it seems:
             | 
             | https://git.kernel.org/pub/scm/linux/kernel/git/stable/linu
             | x...
        
       | userbinator wrote:
       | _Our exploit requires approximately 5GB of memory and 1M inodes_
       | 
       | ...so basically 32-bit systems are totally unaffected (and I
       | believe size_t and int are the same size there anyway), but I
       | think bugs like this are easily prevented by simply imposing sane
       | limits --- there is zero reason to even consider allowing a path
       | more than a few K in length, and IMHO even that is overly
       | generous.
       | 
       | While I know there's a lot of hate for Windows' traditional
       | 260-char limit, I personally haven't run into it as a developer
       | except by accident (e.g. runaway recursion) and it's very
       | comforting to know that the limit is there so code will fail
       | before it consumes the disk or memory entirely.
        
         | saagarjha wrote:
         | > I think bugs like this are easily prevented by simply
         | imposing sane limits
         | 
         | In general, no. Attackers are clever and can usually find their
         | way around arbitrary limits when a bug exists. Sometimes such a
         | restriction might stop them, but more often than not they'll
         | bypass it some other way.
        
         | mishac wrote:
         | > I personally haven't run into it as a developer
         | 
         | Clearly not a nodejs developer then! npm's insanely nested
         | dependency graph caused me to hit the 260 character limit
         | relatively regularly. (though this was several years ago, so
         | maybe they have mitigations for that now)
        
           | pfraze wrote:
           | IIRC this is partly why node_modules moved to a flat
           | structure
        
         | MauranKilom wrote:
         | We've had trouble (and had to occasionally shorten module names
         | to something dumb like mdlWthVclsRmvd) because some part of
         | some toolchain would create a path like                   C:/cu
         | rrent/working/directory/that/is/reasonably/deep/testrun/YYYY_MM
         | _DD_hh_mm_ss/code/moduleName/src/build/bin/../../../../../../ou
         | tputs/moduleName/YYYY_MM_DD_hh_mm_ss/outputFolder/moduleName_te
         | strun_results.txt
         | 
         | or some garbage like that (you get the picture). Yes, half the
         | path was taken by descending into some directory that it went
         | out of again straight away. Test runs would fail because of the
         | 260 char limit unless we cut down the module name length.
         | (Thankfully this did not need to be done in the code itself,
         | just in the test run invocation.)
        
       | jeffbee wrote:
       | A fun fact is there are 196 instances of `int buflen` in
       | Torvalds' tree today, 95 instances of `int namelen`, and 13
       | instances of `int pathlen`, also 3000+ `int size`.
        
       | ddoubleU wrote:
       | Afaik this should work on Android for some time now too right?
       | 
       | Got unrootable old (but still fully working) phone there, might
       | try to play with it.
        
         | pitaj wrote:
         | I'd love to see that. Please share if you end up doing so.
        
         | spijdar wrote:
         | ... do Android kernels normally build with and allow non-
         | privileged users to make namespaces? I'd be really surprised.
        
       | cannabis_sam wrote:
       | I'm still amazed that we allow compilation of so obviously faulty
       | programs.
       | 
       | It's like you sent a 1kg package through the postal service, and
       | then the recipient gets an envelope containing a piece of
       | cardboard from the original packaging.. And everyone involved is
       | somehow A-OK with all of this.
       | 
       | If your programming language silently converts between types (in
       | any direction), just to accommodate the programmer, instead of
       | them specifying what they actually want to compute, you simply
       | have failed as a programming language designer.
        
         | andyjohnson0 wrote:
         | > you simply have failed as a programming language designer
         | 
         | That's some hubris. The C language is 49 years old. Dennis
         | Ritchie made reasonable design decisions for the time he found
         | himself in. I think we should be understanding of that, and the
         | network effects that lead to large parts of the world's
         | critical software infrastructure being implemented in C. I
         | don't think he failed at anything.
         | 
         | I used to be a C developer. I know how easy it is shoot your
         | foot off in C. I think that, arguably, as an industry we should
         | think twice before building more big and/or critical systems in
         | C. There are better tools now.
         | 
         | But we are where we are and it's important to understand how we
         | got here. Castigating our predecessors as failures does them a
         | disservice.
        
           | kaba0 wrote:
           | Though one should mention that most languages can evolve and
           | overcome some of their problems, eg. see PHP which used to be
           | objectively a shitty language and nowadays it is somewhat
           | usable - C underwent basically no improvements in that
           | ridiculous timeframe. So it is not against the original
           | creator but everyone responsible for the language since then.
        
           | cannabis_sam wrote:
           | >The C language is 49 years old.
           | 
           | That's my point, no shade on K&R though.
        
             | cat199 wrote:
             | meanwhile, lisp.
             | 
             | age is orthogonal to good design.
        
       | greesil wrote:
       | https://org.cs.pub.ro/dragos.tarcatu/llvmlinux/commit/058504...
       | 
       | m->size must be of type size_t. It's slightly mind-blowing to me
       | that casting to a smaller unsigned int can cause a vulnerability.
       | But I guess unintended behavior (not undefined) can do that.
        
         | anyfoo wrote:
         | It's very common. All that needs to happen, as just one
         | example, is the variable of the smaller type being used as an
         | index into an array. You might get an out of bounds access, or
         | even just an access to the wrong element.
        
       ___________________________________________________________________
       (page generated 2021-07-20 23:00 UTC)