5 ms·
For me, the canonical UB example is a buffer overflow. No matter how you define UB, in practice a buffer overflow can result in for example, a system crash or -
by PeterisP 5y ago
For me, the canonical UB example is a buffer overflow. No matter how you define UB, in practice a buffer overflow can result in for example, a system crash or - given appropriate very specific input data - encrypting all the files on your hard drive for a ransom.
Requiring compilers to restrict UB to something similar to unspecified behavior (where the behavior is not specified by the standard, but a C implementation must pick a behavior and document its choice) would require C compilers to prevent arbitrary code execution given a buffer overflow in C code, i.e. ensure that a buffer overflow in C code does not ever result in actual buffer overflow on the physical machine. This seems implausible to achieve - given the tradeoffs chosen for C, once UB hits, it really is undefined as it can (not in all, but in some) scenarios result in executing absolutely arbitrary machine code depending on the data provided to the program.
On the other hand, if you'd just want to say that the compiler can't assume that UB won't happen for optimizations, then many optimizations are impossible because theoretical UB is literally everywhere where basic arithmetic happens because of the possibility of an integer overflow - if UB would be required to do "whatever the hardware does" then it means that you can't even reorder basic chains of arithmetic instructions and have to execute every operation in order as-written to have the appropriate behavior in case of overflow.
- barrkel 5y agoThe trouble here is porting the maximalist disaster of one scenario to another scenario simply because the same phrase is used to describe them. Because a buffer overflow may lead to arbitrary control flow escape via ROP or other hijacking of the instruction pointer, it does not follow that e.g. signed overflow is similarly dangerous. Signed overflow, if not guarded against, may enable buffer overflow, but the deeper irony is that detecting when signed overflow has already occurred is close to impossible if the compiler is also aware that the check implies that it has occurred. Thus, code designed for safety, guarding against buffer overflows by detecting signed overflow, ends up not protecting against buffer overflow, simply because signed overflow is described using the same name as buffer overflow.
- PeterisP 5y agoYes, now if the proposal would be not to redefine the semantics of UB (as the post to which I replied suggested) but rather about specifying that buffer overflows are UB1 and integer overflows are UB2, and UB2 means something different from UB1 (perhaps identical to implementation-specified behavior, perhaps something different), then that could be reasonable.
- layer8 5y agoIt goes further than that. The debates around UB a more about whether the compiler can assume that there are no buffer overflows and perform optimizations based on that. For example, if you have a local variable `char buffer[16]`, and there is an access `buffer[i]`, should the compiler be allowed to derive that 0 <= i < 16, and perform optimizations on other uses of `i` based on that "knowledge"? If all bets are off for `buffer[i]` when i < 0 or i >= 16, why shouldn't it? But some argue that the compiler shouldn't, exactly because such automated formal reasoning can lead to arbitrarily wide reaching effects. The problem is that it's hard to see where a middle ground could be.
- rectang 5y agoThe result is a perpetually unstable situation fostering inevitable errors when the programmer is surprised by the compiler. There's no remedy except to move away from C to more tightly specified languages.
- pjmlp 5y agoThe problem is ecosystems like UNIX based platforms, that will never move to something else. The only solution is to throw them away, e.g. Android style.
- throwaway2048 5y agoAndroid threw away a unix based platform? News to me.
- pjmlp 5y agoIt did, ask termux guys. The Linux kernel is an implementation detail. There is nothing UNIX related exposed to userspace. Java and Kotlin applications, while NDK APIs are clearly defined, https://developer.android.com/ndk/guides/stable_apis https://developer.android.com/ndk/guides/stable_apis Or Treble Project for that matter, https://source.android.com/devices/architecture/hidl https://source.android.com/devices/architecture/hidl Try to use private APIs (which is what the Linux underpinnings are) and the application might be blessed with being killed by the OS.
- a1369209993 5y ago> given appropriate very specific input data That's the operative words there. Compilers are not required to ensure that, in sufficiently perverse circumstances, undefined behaviour never results in demons flying out of you nose. They are, however, required to not actively put said demons there themselves, because that's part of what distinguishes a programming language implementation from a piece of malware masquerading as a programming language implementation.
- saagarjha 5y agoCompilers never actively try to put demons in your program. However, they do occasionally end up making constructs that result from a reasonable (if not the most well-thought-out) chain of decisions that end up looking like demons to the untrained eye.
- a1369209993 5y agoI didn't say "try"; if it quacks like demon, it's a demon, and the implementation is (whether the standards commitee likes it or not) required not to actively put it into programs that didn't already have it[0], deliberately or otherwise. 0: like this one: int* zero = 0; int bad = *zero; // maybe crash lol if(!zero) abort(); // definitely crash lol printf("Uln nasaloth geb hai!\n");
- d110af5ccf 5y agoIt just isn't that simple in practice. Modern compilers automatically apply a plethora of different rules in sequence when transforming code. This results in a long chain of transformations where each operation is perfectly reasonable when considered on its own. Unfortunately, for certain inputs, the sum total occasionally appears demonic. The current state of the art is such that there is no way to reliably prevent this that doesn't also severely reduce the optimization abilities of modern compilers. Realistically, adding such language to the standard would (at best) result in worse optimizations by default and additional compiler flags to reenable the more aggressive "nonstandard" ones. (Such flags already exist for various arithmetic operations.)