Live data from Hacker News

size_t-to-int vulnerability in Linux’s filesystem layer

openwall.com

31–40 of 280 posts

Re: size_t-to-int vulnerability in Linux’s filesystem layer

#31
post #4

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?

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.

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.

Re: size_t-to-int vulnerability in Linux’s filesystem layer

#32
post #7

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 va…

> 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)?

Re: size_t-to-int vulnerability in Linux’s filesystem layer

#33
post #15

Earlier quoted context omitted.

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

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.

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.

Re: size_t-to-int vulnerability in Linux’s filesystem layer

#34
post #7

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 va…

Shout out to Zig, which requires explicit casting also.

Re: size_t-to-int vulnerability in Linux’s filesystem layer

#36
post #26

Earlier quoted context omitted.

That's cool! And some languages like Go don't even allow implicit widening conversions: https://play.golang.org/p/a5C5jsHypmu

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).

into() does not work from size. It's rather frustrating in practice. https://stackoverflow.com/questions/62832438/why-is-rusts-us...

Re: size_t-to-int vulnerability in Linux’s filesystem layer

#37
post #7

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 va…

Add Java to that list.

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)

Re: size_t-to-int vulnerability in Linux’s filesystem layer

#38
post #7

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 va…

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

Re: size_t-to-int vulnerability in Linux’s filesystem layer

#39
post #32
post #7

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 va…

> 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 a…

In Virgil, casts are written "type.!(expr)". Casts between numbers check ranges and roundability (for floatint 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.

Re: size_t-to-int vulnerability in Linux’s filesystem layer

#40
post #15

Earlier quoted context omitted.

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

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.

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.
Post reply on HN