Hacker News new | ask | show | jobs
by menaerus 263 days ago
No, because optimizing compilers are free to elide the check. https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#ind...
3 comments

I'm not quite familar with this flag, but this

>so that if a pointer is checked after it has already been dereferenced, it cannot be null.

sound to me that if i've never deref the pointer anytime before(e.g the null check is at the beginning of function), the compiler won't remove this check.

Since the compiler will merge/fold what it appears to be a different logic sections of your code into a single one, you can never be sure what the release build codegen looks like unless you read the assembly.
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.

Yes, 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.

> 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.

I 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;
> 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.
Not on all platforms! If you’re writing portable code targeting a lot of embedded platforms then you don’t want to rely on this optimization.
It's a platform-agnostic optimization in case of GCC so if your embedded Linux toolchain is based on GCC, and most of them are, it's pretty much the case that it will have this optimization turned on by default.

> This option is enabled by default on most targets. On AVR and MSP430, this option is completely disabled.

Yes and if you’re targeting AVR, an extremely popular 8 bit micro, then it’ll be turned off.
Yes, but `*ptr == NULL` is just plain wrong ... it should be `ptr == NULL` ... but that test is redundant since `free` is required to do it.