5 ms·
The array access example shows why this isn't generally possible -- the programmer hasn't written any bounds-checking code or assertions (they just wrote x[y]).
by cyphar 7y ago
The array access example shows why this isn't generally possible -- the programmer hasn't written any bounds-checking code or assertions (they just wrote x[y]). Should the compiler emit bounds-checks? If yes, your code is now slower but there isn't an easy way to disable this "feature" (if x is heap-allocated or passed as a pointers, how will the compiler know what kind of bounds check is sufficient?). If no, you have undefined behaviour and crashes.
Even Rust made a mistake with restricting undefined behaviour -- they accidentally allowed creating references to fields in packed structures (resulting in possibly unaligned references which is undefined behaviour in Rust) in safe code. These days you get a warning telling you to use unsafe or copy the value, but they have yet to make it an error. For comparison, C also gives you a warning for the same problem (or an error with -Werror).
I also want to point out that modern compilers have different sanitisers (-fsanitize=...) which cause your program to abort() on memory unsafety or other violations. It's effectively a better valgrind which is compiled into your program.
- AaronFriel 7y agoI would argue that most of the time, yes, the compiler should emit bounds checks and only in performance sensitive code should they be removed (or where the compiler can prove the bounds check holds for a range). I don't know how anyone who has studied the history of vulnerabilities and security issues and code would think this is a hard tradeoff: > If yes, your code is now slower but there isn't an easy way to disable this "feature" (if x is heap-allocated or passed as a pointers, how will the compiler know what kind of bounds check is sufficient?). > If no, you have undefined behaviour and crashes. The latter choice has resulted in how many millions or billions in losses, perhaps even lives lost?
- cyphar 7y agoThe problem is that if you want to permit users to do things like dereference pointers and do pointer arithmetic (a requirement for a C-like language) there is no way for the compiler to ensure (even at runtime, because the pointer might not be to memory managed by the language) that the pointer dereference is valid. Okay, maybe you can add a small overhead to array accesses (which is what Rust did), but you can't stop undefined behaviour entirely. Rust has plenty of undefined behaviour (most can only be triggered through unsafe -- which is the whole argument behind the language -- but as I mentioned there are some cases where even they made a mistake and made an operation safe when it should've have been).
- AaronFriel 7y agoThe benefit of "unsafe" blocks is that the surface area to audit is dramatically reduced, so that yes, if you want high performance pointer mangling and arithmetic, you can do it, but your code will require more attention. In C/C++, undefined behavior and unsafe operations abound and it is nigh impossible to isolate what is "important" to review from a security standpoint.
- saagarjha 7y agoAddress Sanitizer is actually a worse Valgrind, because it misses certain bugs and makes up for it by being faster.