It locked up my machine, and my fan started spinning! Then I saw Ubuntu apport at 100% CPU!
OK well I guess the CPU usage is not that surprising because it’s a huge 4 GB string.
But it did segfault after awhile.
The vulnerable code was released in January 2011, so it took well over a decade for this vulnerability to be found
Is this an integer overflow problem? (i.e. integer overflow causing buffer overflow) Would it happen with a naive Rust port? (I think you can still have integer overflow in Rust)
Yep. The default behaviour in rust is just as nonsensical and broken as c—a massive oversight, and mistaken concession to the ‘zero-cost abstractions’ crowd.
I’m eager to attack Rust when I can, but it’s a systems programming language. The overhead would make it a non starter in places where its memory and concurrency safety brings a lot of value.
If you want to do maths then you should probably use Haskell or something.
The default in rust is to panic on under/overflows in debug builds but removing it in release, for exactly the speed reasons you mentioned. There is one difference though: Rust array/slice indices areunsigned, which already prevents some problems. Note also that if LLVM can’t decide whether your array/vec/.. access is always valid, it’ll add a comp-time length check to your access. (Which again can surprise people because it’s adding overhead.) What ever you do as a language, you’ll do it wrong..
I’m eager to attack C when I can, but it’s a systems programming language. The overhead of bounds checking would make it a non starter in places where its relative safety and expressiveness bring a lot of value over assembly.
Because overflow is not a safety issue, it is sensible to allow opting into modular arithmetic where desirable (as one does in common lisp).
Integer overflow is defined to “panic or wrap” in rust, and defined to strictly panic in debug builds (with the current compiler they strictly wrap in release builds, but that is allowed to change to panicing in the future).
Panic here means either throw an exception or abort the program, more or less at the compilers discretion (there are rules, but they aren’t important for this discussion). You’re culturally expected not to catch that exception, but you can if you really want to.
So in a naive rust port (and most any rust port where you didn’t write pretty obviously incorrect code such as incorrectly using a.wrapping_add(b) ) it would crash safely when compiled with debug assertions.
In a naive rust port, and really anything but a highly optimized rust port, you would also be bounds checking all array accesses (all array accesses in safe rust code are bounds checked, even when debug assertions are turned off, you have to drop down to the unsafe superset to avoid that). So even though you would still get a wrapping integer in release builds, you wouldn’t get a buffer overflow, just a panic. That said, I can definitely see a highly optimized rust port using unsafe code to forgo bounds checks and no one noticing the issue.
Uhhhh crazy !!! I just tried the Python example on my machine
It locked up my machine, and my fan started spinning! Then I saw Ubuntu apport at 100% CPU!
OK well I guess the CPU usage is not that surprising because it’s a huge 4 GB string.
But it did segfault after awhile.
Is this an integer overflow problem? (i.e. integer overflow causing buffer overflow) Would it happen with a naive Rust port? (I think you can still have integer overflow in Rust)
Depends on how it’s coded. Rust ints provide a number of options
ex.
https://doc.rust-lang.org/std/primitive.u64.html#method.checked_add
https://doc.rust-lang.org/std/primitive.u64.html#method.overflowing_add
https://doc.rust-lang.org/std/primitive.u64.html#method.saturating_add
https://doc.rust-lang.org/std/primitive.u64.html#method.wrapping_add
C/C++ has all of this too, if you choose to use them.
I thought they were only gcc and clang extensions
Although not sure if there are multiple rust implementations that have all those options
I think the problem is that most hash functions wouldn’t use them anyway, regardless of language, if it only takes care of bugs with 2^32 input sizes
It would be nice if the blog post analyzed the bug more and the fix for it, to see if this is relevant
Yep. The default behaviour in rust is just as nonsensical and broken as c—a massive oversight, and mistaken concession to the ‘zero-cost abstractions’ crowd.
I’m eager to attack Rust when I can, but it’s a systems programming language. The overhead would make it a non starter in places where its memory and concurrency safety brings a lot of value.
If you want to do maths then you should probably use Haskell or something.
The default in rust is to panic on under/overflows in debug builds but removing it in release, for exactly the speed reasons you mentioned. There is one difference though: Rust array/slice indices are unsigned, which already prevents some problems. Note also that if LLVM can’t decide whether your array/vec/.. access is always valid, it’ll add a comp-time length check to your access. (Which again can surprise people because it’s adding overhead.) What ever you do as a language, you’ll do it wrong..
I’m eager to attack C when I can, but it’s a systems programming language. The overhead of bounds checking would make it a non starter in places where its relative safety and expressiveness bring a lot of value over assembly.
Because overflow is not a safety issue, it is sensible to allow opting into modular arithmetic where desirable (as one does in common lisp).
Integer overflow is defined to “panic or wrap” in rust, and defined to strictly panic in debug builds (with the current compiler they strictly wrap in release builds, but that is allowed to change to panicing in the future).
Panic here means either throw an exception or abort the program, more or less at the compilers discretion (there are rules, but they aren’t important for this discussion). You’re culturally expected not to catch that exception, but you can if you really want to.
So in a naive rust port (and most any rust port where you didn’t write pretty obviously incorrect code such as incorrectly using
a.wrapping_add(b)
) it would crash safely when compiled with debug assertions.In a naive rust port, and really anything but a highly optimized rust port, you would also be bounds checking all array accesses (all array accesses in safe rust code are bounds checked, even when debug assertions are turned off, you have to drop down to the unsafe superset to avoid that). So even though you would still get a wrapping integer in release builds, you wouldn’t get a buffer overflow, just a panic. That said, I can definitely see a highly optimized rust port using unsafe code to forgo bounds checks and no one noticing the issue.
apport is the crashdump handler and it would be scanning through that 4GB of zeroes in Python code, yes.