c++segmentation-faultundefined-behaviordereferencenullptr

Does the runtime dereference of a nullptr always result in Segmentation Fault?


tl;dr

What happens if "at runtime" a pointer p such that p == nullptr is dereferenced and its "pointee" is read from/written to?

Does that imply 100% a segmentation fault because the memory address 0 can't just be read from/written to?


The reason why I wrote "at runtime", is that I'm aware that the compiler will assume that p != nullptr when it sees *p (with p raw pointer), so some *p can just disappear due to UB, and effectively at runtime no nullptr is dereferenced at that point and the program keeps going though the alleys of undefined behavior.

For instance, given this TU,

int* bar();

int foo(int* i) {
    return *i;
}

int work() {
    int* b = bar();
    int f = foo(b);
    return b != nullptr ? 0 : (f + *bar());
}

the compiler can assume bar() =! nullptr just by seeing that its return value is dereferenced by foo's body, which can only be done if bar() != nullptr. Indeed the function work is compiled down to

work():
        sub     rsp, 8
        call    bar()
        xor     eax, eax
        add     rsp, 8
        ret

where the value returned by bar() is never actually derenferenced, even if *bar() is in the source code.

But foo (imagine it alone in its own TU, so there's no UB going on) is compiled down to

foo(int*):
        mov     eax, DWORD PTR [rdi]
        ret

so if this TU is linked against another one that calls foo(nullptr), then the mov instruction will definitely try to read from the address in rdi which is whatever nullptr is compiled to, I guess 0.

Does that mean that Segmentation Fault is unavoidable? (I'm not saying I want to avoid segv, quite the opposite, I'm just wondering if things can go so wrong that there's no segv and the program keeps going who knows where!)


(¹) I'm aware that the issue of whether dereferencing a null pointer is UB is still unresolved, but this is not a duplicate of my previous question.


Solution

  • You are mixing several concerns into one question here which amplifies the confusion in this matter. You should take care to distinguish:

    So in short: You won't know what happens by looking at the C++ code. You are, in principle, able to know what will happen when looking at the generated binary, given that you understand the behavior of your target platform well enough.

    To answer the literal headline of your questions: there do exist hardware/OS configurations in the real world (in particular in embedded systems) where dereferencing the null address will not be caught by either the hardware or the OS, so you cannot rely on this always resulting in program termination by the environment.

    In practice, should you care? Not unless you need to reason about the behavior of a pre-generated binary which you are unable to change. From a C++ perspective it is easy enough to perform a null check before dereferencing and (reliably) terminating the program manually if that is the desired outcome. Reasoning about program behavior after the actual dereferencing occurred is not sensible from a C++ perspective.