5 ms·
Exactly. The classic example is invalid pointer deref. It’s too costly to check every deref against every allocation (outside of special debug modes). So the sy
by grandempire 2y ago
Exactly. The classic example is invalid pointer deref. It’s too costly to check every deref against every allocation (outside of special debug modes). So the system usually can only detect if there is a virtual memory page fault, in which case it can crash.
In embedded systems without virtual memory there is no validity checking at all. Or it could periodically check (random sampling).
So the standard makes the reasonable choice to leave it undefined. If you can detect it and crash, that’s great. If not and you accidentally overwrite your programs instructions, anything can happen.
Rust only avoids UB in so far as it relies on default clang behavior on modern hardware.
- steveklabnik 2y agoRe Rust, that’s simply not true. Rust relies on compile time validity checks that have nothing to do with virtual memory.
- grandempire 2y agoYou confused two parts. I am not saying that Rust has the same pointer deref, I’m saying that if you try to specify rust you will find parts that implicitly rely on clang or hardware default. In other words the behavior is unspecified.
- steveklabnik 2y agoThe borrow checker has a formal proof and it is completely independent from hardware.
- deleted 2y ago[deleted]
- grandempire 2y agoGatorade gives you electrolytes.
- jenadine 2y ago> Rust only avoids UB in so far as it relies on default clang behavior on modern hardware. That's not true. Rust behaviour avoids UB by not compiling with invalid reference deref because it checks life time. Clang's behaviour is likely a crash but anything can still happen. It is literally undefined.
- grandempire 2y ago> Clang's behaviour is likely a crash but anything can still happen. You act as if clang itself is random. Clang will do and handful of things none of them which will melt your computer. A given implementation may choose to do anything. Clang and gcc on major operating systems do reasonable things. > It is literally undefined. Ok but I’m explaining why undefined is the right thing.
- jenadine 2y agoThe code generated by clang can do anything. Ok, clang will not directly melt my computer because clang just generates code, but imagine this: if (temperature_too_high) lower_temperature(); if somewhere else the code access invalid pointer, clang may decide to remove this condition altogether because it thinks it must be dead code, for example. And running this code will melt the computer even though the programmer thought this wouldn't be possible.
- grandempire 2y agoThat’s not true. A conforming implementation may chose to melt your computer - and you can choose to not use that implementation. But clang and gcc will never melt your Linux. Once again, a real life example where that is relevant is an embedded device with no memory protection. If you overwrite a buffer you can overwrite the OS code - breaking your computer and requiring you to flash the memory. It seems there is a lot of misguided accusations going around about who misunderstands UB.
- eru 2y ago
- eru 2y ago> Exactly. The classic example is invalid pointer deref. It’s too costly to check every deref against every allocation (outside of special debug modes). So the system usually can only detect if there is a virtual memory page fault, in which case it can crash. You could still define what happens. Eg you could define that whatever error might happen because of invalid pointer deref can't travel back in time. (UB famously can travel back in time: UB at any time during the execution makes the whole execution UB.)
- dzaima 2y agoUB doesn't allow arbitrary time travel; any time travel you see should be explainable by happening to run code that undoes what was done previously, and where it's not possible to explain it as such it's a compiler bug (ref: wg14 member: https://news.ycombinator.com/item?id=40836898 https://news.ycombinator.com/item?id=40836898). This is now clearly standardized, but even without that it's pretty trivial to see how compilers would generally abide by this - if you call an external function that does arbitrary things, it may exit(), so the call can't be optimized out even if it's followed by UB. (printf & co are effectively arbitrary calls for these purposes for multiple reasons). And all other behavior is just writing things to memory, which can be undone by happening to write what was there before. (ok there's also volatile, and a bug in clang (not gcc though): https://github.com/llvm/llvm-project/issues/102237 https://github.com/llvm/llvm-project/issues/102237)
- eru 2y agoCheck the ensuing discussion under the comment you linked to. Some limited time travel is still allowed for UB.
- dzaima 2y agoThere's still no time travel in any of those posts; some may require an explanation of "the compiler replaced the UB division with a call to 'bar'" or something, but "calling 'bar'" is still a subset of the "anything" that UB can result in. If there's a specific example you think doesn't follow that, I'd appreciate a specific reference. (and singron's mention of SIGSEGV handling is entirely irrelevant; the C standard only cares about the abstract machine; in general real memory state can temporarily appear in arbitrarily weird states in practice if interrupted by a signal even without UB, e.g. https://godbolt.org/z/91ModhbEY https://godbolt.org/z/91ModhbEY will have all a[0..15] written even if there's a handled trap on the write to b[0] (or otherwise an interrupt between the two SIMD writes) (using restrict for simplicity, but taking an 'int n' argument and using that for the loop bound will result in ~the same thing without restrict, just with less direct assembly); the same applies even to Rust, though of course with Rust you shouldn't be able to have a situation where you trap on a safe write; could still have a random interrupt happen in the middle though)