Hacker News new | ask | show | jobs
by ndr 959 days ago
And it looks like it was a buffer overflow:

https://blog.qualys.com/vulnerabilities-threat-research/2021...

Would Rust prevent this?

5 comments

Discussed on HN:

https://news.ycombinator.com/item?id=25919235 (321 comments)

> Would Rust prevent this?

This is often hard to say.

In a very literal sense, you could write this same code, in unsafe Rust, so one could argue that Rust does not prevent it.

Some may argue that if this program was written in Rust in the first place, "concatenate all command line arguments into one big string for processing" wouldn't be the way you'd go about escaping command line arguments. The issue here is about misplacing a null terminator, Rust strongly prefers a "start + length" style of representing strings instead of null terminators, so you'd never really end up in this situation in Rust in the first place.

I'm sure there's other ways to evaluate the situation as well. Which one you find compelling is up to you.

Yes buffer overflows are one of the explicitly addressed vulnerabilities of Rust's bounds checker, which is always on, if memory serves. I haven't touched Rust in a year.
You can get around the bounds checker with unsafe code. But yes, by default an overflow should result in a panic and program termination.
You can, but unsafe code is discouraged in general, even given a slight performance cost.

For performance-insensitive, security-critical code, there really shouldn't be any such code in the entire program—and it would be easy to verify that with a presubmit.

In an ideal world, no, there shouldn't. But `unsafe` is not just a performance hack; people do find things that they legitimately need to do that Rust can't statically verify. Probably the most trivial example is interacting with code that is not itself written in Rust.

This does imply that some of the stronger claims about Rust's level of static safety guarantees that float around on the Internet can't really be true unless substantially everything you might want to do has a version that's been completely written in Rust. Whether you feel that means that achieving the desired level of safety implies you've still got to rely on some dynamic analysis tools just to be sure probably depends on how much safety you really want, and how much faith you're willing to place in the skills of the authors of the libraries you use.

And even then, if we really want to go least common denominator, if you're running your program on Windows or a Unix or basically any other OS that isn't Redox, then you've got unsafe code executing every time Rust's own standard library needs to make a syscall to achieve something.

Which I don't say by way of criticizing rust Rust. It's got to live in the same crappy world we all have to live in, and it's arguably doing a better job of de-crappifying it than any other systems programming language. I'm just trying to illustrate how an unqualified statement along the lines of "there shouldn't be any unsafe code in the entire program" is kind of a self-strawman, precisely because Rust has to live in said crappy world, and I think that it might be unsafe to lose sight of that fact.

This comment reminds me of “What you're referring to as Linux, is in fact, GNU/Linux, or as I've recently taken to calling it…”
Look into the crates you use and you’ll find tons of unsafe code, especially around custom data structures doing buffer pointer arithmetic and stuff. If it’s wrapped in a safe interface, you’d never know.
Yes, unless you use unsafe code.
Yes it would be prevented by the borrow checker.
The borrow checker does not prevent out of bounds access of arrays (or vectors or whatever you want to call them).

The borrow checker is intended to protect against "temporal memory unsafety". It can tell you that you are using something that has already been freed, or something that could be freed while you are using it for example.

Bounds checking is a "spatial memory unsafety" problem, it has nothing to do with borrowing and exclusive references.

Bounds checking is a trivial problem, for an array that has N length, like a char[N], something tried to access a value past the end of the array (like char[11] if N=10).

Rust doesn't really do anything special here and protecting against buffer overflows does not require any novel technology.

An implementation of bounds checking is as simple as an "assert(I >= 0 && I < N)", where I is the index and N is the length of the array.

In C this is difficult to do because arrays or are just pointers (or decay to) and pointers do not carry any information about the length. Keeping a separate variable containing the length around but this apparently is too unergonomic since virtually all C software does not check every array access in all parts of the program.

In Rust, its very rare to use raw pointers to work with "arrays". Instead there is a "slice" type that models the concept of a contiguous sequence of values in memory. The important part is that the slice type is a "fat pointer", and the fat pointer contains the length or the array. The slice type is able to check every access in all parts of program.

So all slice accesses are checked by default. and you can't "turn it off". If you really want to disable bounds checking for some reason, there is an unsafe "get_unchecked" function.

There are some sequence types other than slices, arrays for example (array is a specific type here). They are still checked but they don't have to store the length information because it's encoded into it's type.

The Vec type is another one. It is a resizable "array", and it stores 3 things, a pointer to the allocation, the capacity and the length. That's mostly not relevant here though. It checks every access like the other types.

Bounds checking is a very easily preventable error. It should not be happening in $CURRENT_YEAR.

Does it not provide some protection against a buffer overflow?
Indirectly, e.g. by ensuring that you can't modify an array while iterating over it. That sort of thing.
Not by the borrow checker. It would be prevented by bounds checking on slices, arrays, strings, etc.