2 ms·
Ah, I see, it's trivial out-of-bounds array access, and the compiler's inability to notice that. The cause of innumerable data corruption bugs, crashes, and exp
by nine_k 2y ago
Ah, I see, it's trivial out-of-bounds array access, and the compiler's inability to notice that. The cause of innumerable data corruption bugs, crashes, and exploits.
I wish C did not have implicit pointer aliasing; it already has explicit aliasing via `union`. (And a different handling of nulls. And no UD. And a pony. Sadly, I don't have a time machine to travel back to 1969.)
- jkrejcha 2y agoI mean yes in this case the problem is you're doing a dereference out-of-bounds, which makes a program not strictly conforming. The problem tends to be is that the real world is much more (essentially) complex and the domain of C is that which needs to be able to deal with that essential complexity, which includes things like type punning and int-to-ptr casts and the like which makes programs not strictly conforming. As it stands, a lot of these rules make it impossible to implement a lot of... C libraries... in ISO C, let alone things like operating system kernels and the like. > And no UD I assume this means undefined behavior? But in any case, undefined behavior is okay to have in a language, it's good for when there are no portable semantics for a particular operation or failure mode[1]. It's generally essential (although some undefined behaviors would definitely be better served being implementation defined or unspecified). It is also okay for programmers to eschew this portability (so if they want to narrow their focus from "anything that can run strictly conforming C" to a superset that targets say a specific memory model or set of processors). [1]: For example, at an extreme example, what happens if the CPU starts burning up? Obviously the standard can't describe such semantics for how a program should behave in the face of this.
- nine_k 2y agoI agree, type punning exists, this is why I mentioned `union`, invented to make some type punning easier. An explicit mechanism like that is essential. Doing it implicitly is, to my mind, too dangerous to be beneficial. Same with undefined behavior. I'm fine with implementation-dependent behavior (like what size is `int`, etc). But modern compilers see UD and feel free to do anything, even removing the code in question. Such things should not be silent, or even a warning. Instead it should be something like `unsafe` sections in Rust: "I know what I'm doing, the usual safety assumptions are irrelevant here, do what I literally say". Doing weird things is sometimes required e.g. to handle memory-mapped hardware registers, etc. But such areas should be clearly delineated, for they are usually few. They also tend to not be portable. A CPU burning up, hardware malfunctioning, etc is usually a situation where all bets are off. An unanticipated pointer aliasing in a trivial program running on perfectly working hardware is a different case.
- jkrejcha 2y agoSo worth noting that the original code actually exhibits no undefined behavior as written (only q is accessed). I erred in my original argument by looking at the "first optimization" being applied. I'd still argue that the third optimization would be incorrect here, if a program can determine that p+1 = q and replacing q with p+1 is legal, then it obviously knows that those two locations alias (kinda by definition of being able to replace that) and therefore can't apply the 3rd optimization.
- fuhsnn 2y ago>I'd still argue that the third optimization would be incorrect here That's my take as well, to apply it more defensively, any writes cannot be proven in-bounds should be considered clobbering q's state.