Low-Level Software Security for Compiler Developers
llsoftsec.github.io
llsoftsec.github.io
It mentions, regarding sanitizers (and presumably other safety tooling): "They are often too expensive to run in production mode, as they tend to increase execution time and memory usage."
I'm surprised we don't have better, performance-neutral hardware support for safety constructs like bounds checking.
Legacy x86 had a BOUND instruction but apparently it sucked and was so little used AMD didn't even bother to implement it... MPX exists but alas still has overheads in practice that can approach 50%... seems we can't match Lisp machines of decades ago that did it properly (in parallel) and for free [1].
1: https://stackoverflow.com/questions/40752436/do-any-cpus-hav...
However there are cooperative ISA features -- between compiler and CPU -- these can be used to constrain untrusted attackers. See ARM PAC, e.g.
but the whole point is that you just cant hand any integer to the hardware and ask it to fetch.
It isn't necessary. Languages that have properly bounded arrays just emit the code and optimize it. E.g. if a loop steps a variable from i from 0 to N-1, accessing arr[i], the arr[i] bounds check can be moved out of the loop: if N-1 is within bounds, it's good to go.
I think the tooling being alluded to here is for checking bounds in programs that not only don't check it themselves, but don't pass around the information which makes it possible to know the size of an array.
The tooling has to build its own data structure to track objects and instrument the binary to intercept accesses; all of that is expensive.
Modern languages have for value in arr, arr.forEach or equivalent. Such code makes it easier for compiler writers to make this optimization.
Swift even chose to remove for loops that step a variable (https://github.com/apple/swift-evolution/blob/main/proposals...), and I don’t think Rust has them, either.
I think that’s the right decision. In modern languages, classical for loops are a code smell.
And of course, golang goes the other way. https://go.dev/tour/flowcontrol/1:
Go has only one looping construct, the for loop.
The basic for loop has three components separated by semicolons:
- the init statement: executed before the first iteration
- the condition expression: evaluated before every iteration
- the post statement: executed at the end of every iteration
Those features were modern 40 years ago, even Visual Basic (pre-.NET) already had them.
for (x in array)
print(x)
->
const __local0017 = array.len;
for (var __local0018 = 0; __local0018 < __local0017; __local++) {
const x = array[__local0018];
print(x);
}
The middle and back ends will infer that __local0018 doesn't exceed __local0017 which is the length of array and get rid of the bound check.BTW this isn't just an experiment or research - some android developers have this enabled today and Android 14 beta has a user-configurable option to enable it.
https://docs.oracle.com/cd/E53394_01/html/E54815/gqajs.html
https://developer.apple.com/documentation/security/preparing...
https://msrc.microsoft.com/blog/2022/01/an_armful_of_cheris/
Because we have come to the realisation that only "C Machines" can fix this issue, as even if Safe C would come tomorrow, the existing software wouldn't be rewritten anyway.