Finally a mainstream C/C++/ObjC compiler manages to catch up with compilers for PL/I, Pascal, Ada and other languages that support bounds checking.
Should probably be a safe x86_64 ABI (x86_64_safe?) as well for libraries that enforce bounds safety.
I think we're better off moving away from the mindset of pointers as memory addresses without any associated type (or alignment) or bounds, even (perhaps especially?) for low level systems programming. There's no reason why I/O space addresses (for example), machine registers, etc. can't have types or bounds.
The problem with all the other bounds checked languages is that they aren't binary compatible with existing C :D
More seriously, the core issue is that [Obj-]C[++] conflates a pointer to an object (in the memory sense) with a pointer to an array of many of those objects. As a result they have to transfer counts/sizes independently, null terminate, or be hopeful, etc. Sensible languages chose to actually distinguish address of object from array of object, and as a result they can have a different structure. Technically C++ does this (new T[...] gives you a pointer to memory that has a preceding count) but because it interoperates with C transparently you don't know if a given T* is a C array (no length), a C++ array (has a length), or an individual object (no length). On the plus side that means you can pass a tuple of "address of a local variable" and '1' to a function without heap allocating an array, on the down side gestures at the software security landscape.
Similarly the problem with other previous attempts to add bounds checking to C is that they didn't consider "must be able to be adopted by system frameworks without breaking existing ABI", which is what this attempts to do. The caveat is that it does have to include `unsafe` annotations and builtins to deal with APIs that don't have any actual bounds information (a classic example might be something like `gets(char*)`).
Everyone does basically agree with you: the key safety of the modern "safe" system languages is that "memory is just a bag of bits" is not safe. Hence the strict typing, strict maintenance of lifetime (GC, refcounting, lifetime's as part of the type system), weird concepts like bounds checking (:D). Doing this at the actual hardware level (as you're implying with registers) doesn't really work - there's too much variation in how languages work, how languages work changes over time, and it's also extremely expensive - in cpu time, memory overhead, and silicon area. Organizations have tried it in the past and I don't recall any actually being successful, except as examples of why "CISC is bad" :D
Arrays in the safe languages have length, arrays in C do not (which is the root source of this problem).
Switching to one of those other languages would mean there was an ABI break as existing C code would cease to work, as they would be passing C array/pointer to code that believes those arrays will have a length. That's the kind of ABI incompatibility that comes up.
Not really, in some implementations there are two pointers, start and end location.
The ABI break stands, however nothing prevents C to finally come up with additional types, language or as library vocabulary types, it is about time after 50 years of failing at it.
end-start is the length - I didn't think given the context I needed to be that precise. The important thing is that the ABI for an array does not match C's because it has additional data that C doesn't provide, so can't interoperate with C.
"C can get new types" isn't a solution to the problem, this RFC does add such (indexable and bidi_indexable), but that does nothing to help existing code.
If a solution to bounds checking in C means you can't recompile an existing library with bounds safety, without breaking existing users of those libraries it's insufficient, because it means that it can't be used for the huge amount of existing C. If a project is new then it can just use a safe language in the first place.
Precision matters, when end pointers are used there is no limit for the string/array size, as it is traditional argument the C folks use against bounds checked data structures.
"C can get new types" is definitly a solution, one where new types can be incremently used and with time transition existing code bases to the new types.
By your own words, "C can get compiler annotations" is not a solution for the problem as well, someone has to write the code that uses those annotions, hardly any different from using new types.
> Doing this at the actual hardware level (as you're implying with registers) doesn't really work - there's too much variation in how languages work, how languages work changes over time, and it's also extremely expensive - in cpu time, memory overhead, and silicon area. Organizations have tried it in the past and I don't recall any actually being successful, except as examples of why "CISC is bad" :D
That is precisely what our research does, and Arm have built an experimental prototype, Morello, that is real silicon (4 cores, 2.5 GHz, based on the Neoverse N1 seen in various high-end Arm server offerings, including AWS's Graviton2). Our belief is that it does work and that it isn't extremely expensive.
Kind of, MSVC has had this for ages since WindowsXP SP 2, check SAL.
And as proven by your lack of knowledge, and others discussing this, not many people are aware of it outside Redmond, thus barely used in practice other than Microsoft own teams.
> There's no reason why I/O space addresses (for example), machine registers, etc. can't have types or bounds.
Machine registers are usually typed, i.e. there are floating-point registers with specialized instructions and integer registers with their own instructions.
Super cool. They mention code size impact (large) but not runtime impact, as far as I can see? I wonder what the throughput cost is. Guess it's OK if they plan to deploy this in iOS.
22 comments
[ 5.3 ms ] story [ 78.3 ms ] threadShould probably be a safe x86_64 ABI (x86_64_safe?) as well for libraries that enforce bounds safety.
I think we're better off moving away from the mindset of pointers as memory addresses without any associated type (or alignment) or bounds, even (perhaps especially?) for low level systems programming. There's no reason why I/O space addresses (for example), machine registers, etc. can't have types or bounds.
More seriously, the core issue is that [Obj-]C[++] conflates a pointer to an object (in the memory sense) with a pointer to an array of many of those objects. As a result they have to transfer counts/sizes independently, null terminate, or be hopeful, etc. Sensible languages chose to actually distinguish address of object from array of object, and as a result they can have a different structure. Technically C++ does this (new T[...] gives you a pointer to memory that has a preceding count) but because it interoperates with C transparently you don't know if a given T* is a C array (no length), a C++ array (has a length), or an individual object (no length). On the plus side that means you can pass a tuple of "address of a local variable" and '1' to a function without heap allocating an array, on the down side gestures at the software security landscape.
Similarly the problem with other previous attempts to add bounds checking to C is that they didn't consider "must be able to be adopted by system frameworks without breaking existing ABI", which is what this attempts to do. The caveat is that it does have to include `unsafe` annotations and builtins to deal with APIs that don't have any actual bounds information (a classic example might be something like `gets(char*)`).
Everyone does basically agree with you: the key safety of the modern "safe" system languages is that "memory is just a bag of bits" is not safe. Hence the strict typing, strict maintenance of lifetime (GC, refcounting, lifetime's as part of the type system), weird concepts like bounds checking (:D). Doing this at the actual hardware level (as you're implying with registers) doesn't really work - there's too much variation in how languages work, how languages work changes over time, and it's also extremely expensive - in cpu time, memory overhead, and silicon area. Organizations have tried it in the past and I don't recall any actually being successful, except as examples of why "CISC is bad" :D
source?
I'm pretty confident the author of the comment is colleagues with the RFC author.
Switching to one of those other languages would mean there was an ABI break as existing C code would cease to work, as they would be passing C array/pointer to code that believes those arrays will have a length. That's the kind of ABI incompatibility that comes up.
The ABI break stands, however nothing prevents C to finally come up with additional types, language or as library vocabulary types, it is about time after 50 years of failing at it.
"C can get new types" isn't a solution to the problem, this RFC does add such (indexable and bidi_indexable), but that does nothing to help existing code.
If a solution to bounds checking in C means you can't recompile an existing library with bounds safety, without breaking existing users of those libraries it's insufficient, because it means that it can't be used for the huge amount of existing C. If a project is new then it can just use a safe language in the first place.
"C can get new types" is definitly a solution, one where new types can be incremently used and with time transition existing code bases to the new types.
By your own words, "C can get compiler annotations" is not a solution for the problem as well, someone has to write the code that uses those annotions, hardly any different from using new types.
That is precisely what our research does, and Arm have built an experimental prototype, Morello, that is real silicon (4 cores, 2.5 GHz, based on the Neoverse N1 seen in various high-end Arm server offerings, including AWS's Graviton2). Our belief is that it does work and that it isn't extremely expensive.
Mainframes and micros that are still around.
iOS and newer Android models using PAC and MTE.
https://onsitego.com/blog/mediatek-dimensity-9000-smartphone...
"MediaTek Dimensity 9000 uses Armv9 technology for unparalleled performance"
https://www.arm.com/company/news/2021/12/mediatek-dimensity-...
And as proven by your lack of knowledge, and others discussing this, not many people are aware of it outside Redmond, thus barely used in practice other than Microsoft own teams.
Machine registers are usually typed, i.e. there are floating-point registers with specialized instructions and integer registers with their own instructions.
https://support.apple.com/guide/security/memory-safe-iboot-i...