3 ms·
If you check for null pointer before you dereference, then no the compiler cannot elide the check. If you check after dereferencing it, yes it can. But in this
by knorker 1y ago
If you check for null pointer before you dereference, then no the compiler cannot elide the check.
If you check after dereferencing it, yes it can. But in this case why would you not check before dereferencing? It's the only UB-free choice.
- menaerus 1y agoYes, it can. Why would you be checking the pointer for nullptr after you have dereferenced it? It makes no sense at all, so, compiler indeed can elide the nullptr check before dereferencing the ptr exactly because it is free to _always_ assume that the program is free of UB. To be more precise GCC says "eliminate useless checks for null pointers" and what I am saying that you can never be sure what in your code ended up being "useless check" vs "useful check" according to the GCC dataflow analysis. Linux kernel is a famous example for disabling this code transformation because it is considered harmful. And there's nothing harmful with the nullptr check from your example.
- knorker 1y ago> Why would you be checking the pointer for nullptr after you have dereferenced it? It makes no sense at all Right. It's UB. And that's why the optimization in question is about removing that check. The only reason the optimization is valid for a C compiler to do, is that it can assume dereferencing a null pointer lands you in UB land. I'm sorry, either you are terrible at trying to explain things, or you have thoroughly misunderstood what all this is about. GCC cannot, under any circumstances or with any flags, remove an "if (ptr == NULL)" that happens before dereferencing the pointer. What this flag is about, and what the kernel bug you mentioned (at least I think you're referring to this one) is about, was a bug that went "int foo = ptr->some_field; […] if (ptr == NULL) { return -EINVAL; }". And GCC removed the post-deref null pointer check, thus making the bug exploitable. From the help text: > if a pointer is checked after it has already been dereferenced, it cannot be null. after. Only applies after. A check before dereferencing can never be removed by the compiler. Obviously.
- 1718627440 1y agoI think where you are talking past each other is, that one is talking about temporal after and the other about causal after. The null check can be eliminated if the dereference happens temporally after, but causally before: if (ptr == NULL) { ... } ... int foo = ptr->some_field;
- knorker 1y agoAh yes, unless that conditional returns, UB indeed has time travelling properties. (I mean, spec wise. Manifesting as the compiler "reasoning" that "well I could legally put that load before the check, and that means it's not null, which means I don't need the check") But this brings us back to the article: Why does the author say that there's no way to check for NULL in the free function? Maybe they are hinting at something completely unrelated to what we're saying here? If that's where we failed to communicate, then that makes sense. Thanks, stranger!
- menaerus 1y agoI found the old discussion here on HackerNews although I haven't seen it before. I only found it now because I wanted to double-check my hypothesis and see if my understanding is really off. Seems like not: https://news.ycombinator.com/item?id=42387013 https://news.ycombinator.com/item?id=42387013 https://godbolt.org/z/aPcr1bfPe https://godbolt.org/z/aPcr1bfPe
- knorker 1y agoThat godbolt illustrates what I've been saying, yes. The godbolt example is exactly what I meant when I said "If you check after dereferencing it, yes it can [remove the check]". In fact we can make the example even shorter: https://godbolt.org/z/8fv4GKMse https://godbolt.org/z/8fv4GKMse And here's one with a "time travelling" removal of a branch because of UB: https://godbolt.org/z/PjYzqKxs4 https://godbolt.org/z/PjYzqKxs4 Remove the memcpy, and it adds the comparison back in: https://godbolt.org/z/r1GTvGnnf https://godbolt.org/z/r1GTvGnnf This one is fun too: Explicitly crash on null pointer: https://godbolt.org/z/sGzhK7zM4 https://godbolt.org/z/sGzhK7zM4 As this comment (https://news.ycombinator.com/item?id=45442103 https://news.ycombinator.com/item?id=45442103) says, it looks like we may have just been talking past each other. When I said "If you check for null pointer before you dereference, then no the compiler cannot elide the check." I meant like: if (ptr != NULL) { *ptr = 123; } Which is obviously safe and the compiler cannot remove the check. Not: if (ptr != NULL) { [… something else …] } *ptr = 123; The latter is UB, and UB is allowed to time travel.
- lelanthran 1y ago> Yes, it can. I don't think so. If it could, then this code would reliably crash: char *mystr = strdup (oldstr); if (mystr) *mystr = 0; // Truncate string That never crashes.
- menaerus 1y agohttps://godbolt.org/z/aPcr1bfPe https://godbolt.org/z/aPcr1bfPe