The thing is though that even with array bounds checking built into the language, out of bounds access due to programming error can still be attempted. Only this time it's safer because an attacker can't use the bug (which still exists) to access memory outside of bounds. In any case, the program still doesn't work as intended (has bugs) because the programmer has attempted, or allowed the attempt, to access out of bounds memory.
Writing safe code is better than depending on safety features. Writing safe code is possible in any programming language, the only things required are good design principles and discipline (i.e. solid engineering).
Right. Also it might it sound like array-to-pointer decay is forced onto the programmer. Instead, you can take the address of an array just fine without letting it decay. The type then preserves the length.
That's implementation defined behavior, not undefined behavior. Undefined behavior explicitly refers to something the compiler does not provide a definition for, including "safe defaults."
Modern C still promptly decays an array to a pointer, so no array bounds checking is possible.
D does not decay arrays, so D has array bounds checking.
Note that array overflow bugs are consistently the #1 problem with shipped C code, by a wide margin.
The thing is though that even with array bounds checking built into the language, out of bounds access due to programming error can still be attempted. Only this time it's safer because an attacker can't use the bug (which still exists) to access memory outside of bounds. In any case, the program still doesn't work as intended (has bugs) because the programmer has attempted, or allowed the attempt, to access out of bounds memory.
Writing safe code is better than depending on safety features. Writing safe code is possible in any programming language, the only things required are good design principles and discipline (i.e. solid engineering).
> no array bounds checking is possible.
This isn’t strictly true, a C implementation is allowed to associate memory-range (or more generally, pointer provenance) metadata with a pointer.
The DeathStation 9000 features a conforming C implementation which is known to catch all array bounds violations. ;)
> The DeathStation 9000 features a conforming C implementation which is known to catch all array bounds violations. ;)
That actually really does exist already with CHERI CPUs, whose pointers are tagged with "capabilities," which catch buffer overruns at runtime.
https://tratt.net/laurie/blog/2023/two_stories_for_what_is_c...
https://msrc.microsoft.com/blog/2022/01/an_armful_of_cheris/
Right. Also it might it sound like array-to-pointer decay is forced onto the programmer. Instead, you can take the address of an array just fine without letting it decay. The type then preserves the length.
6 replies →
A worked example: https://github.com/pizlonator/llvm-project-deluge/blob/delug...
"The DeathStation 9000"
The what now?
5 replies →
You'd be surprised: Zig has one UB (Undefined Behaviour) that C doesn't have!
In release fast mode, unsigned overflow/underflow is undefined in Zig whereas in C it wraps.
:-)
Of course C has many UBs that Zig doesn't have, so C is far less safe than Zig, especially since you can use ReleaseSafe in Zig..
UB is does not automatically make things unsafe. You can have a compiler that implements safe defaults for most UB, and then it is not unsafe.
That's implementation defined behavior, not undefined behavior. Undefined behavior explicitly refers to something the compiler does not provide a definition for, including "safe defaults."
2 replies →
Well Zig has ReleaseSafe for this.. ReleaseFast is for using these UBs to generate the fastest code.
By definition UB cannot be safe.
2 replies →
Does C automatically wrap? I thought you need to pass `-fwrapv` to the compiler to ensure that.
Unsigned overflow wraps. Signed overflow is undefined behavior.
2 replies →
-fwrapv is for signed integer overflow not unsigned.
1 reply →
Modern C is barely any different than older C. The language committee for C is extremely conservative, changes tend to happen only around the edges.
There's finally a way to safely add two signed numbers, without tricky overflow checks that may trigger UB themselves!