Null Pointer Dereferencing Causes Undefined Behavior
Andrey Karpov
1

Please revisit your argumentation

Hi, your argumentation as presented here was unable to convice me, and I’ll explain why. I’d like you to please arg over my points since it goes against your thesis, or tell me whether I’m right in my points and show they don’t enter in conflict with yours. I’m sorry for not checking the previous discussion.

So first, my initial thesis, based on common sense, and I think it’s what’s in the subconscious of the programmers that defend the validity of null pointer dereferencing is this:

“Null pointer dereferencing is OK, otherwise the C and C++ languages would be constrained in accessing the first few bytes of memory in a given system, for the common case of an implementation that attributes 0 to the null pointer, or whatever byte in the address space if the null pointer is not 0.
Should the C/C++ languages prevent a programmer of accessing an object that may be stored in whatever specific address in the address space, or use it as basis for address one elsewhere?”

Here, I’d like to quote Walter Oney, regarding a system’s behavior about that (not that it’s well based in any standard):

More About NULL Pointers:
While we’re on the subject of invalid pointers, note that a NULL pointer is (a) an invalid user-mode pointer in Windows XP and (b) a perfectly valid pointer in Windows 98/Me. If you use a NULL pointer directly, as in *p, or indirectly, as in p->StructureMember, you’ll be trying to reference something in the first few bytes of virtual memory. Doing so in Windows XP will cause a trappable access violation. Dereferencing a NULL pointer in Windows 98/Me will not, of itself, cause any immediately observable problem. I once spent several days tracking down a bug that resulted from overstoring location 0x0000000C in a Windows 95 system. That location is the real-mode vector for the breakpoint (INT 3) interrupt. The wild store didn’t show up until some infrequently used application did an INT 3 that wasn’t caught by a debugger. The system reflected the interrupt to real mode. The invalid interrupt vector pointed to memory containing a bunch of technically valid but nonsensical instructions followed by an invalid one. The system halted with an invalid operation exception. As you can see, the eventual symptom was very far removed in space and time from the wild store.

The same kind of history follows from the exploit you mention.

Now, I’d like to address what in your argumentation seems not well connected. This is what should be better clarified in your article, because as it is, the arguments, quotations, and the like, don’t clearly target the issue, but instead, other ones.

6.3.2.3: If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

A standard should be followed strictly, what this means is exactly what it says. It does not say anything about the inability of dereferencing a null pointer, it just says that a null pointer constant converted to a pointer type won’t compare equal to a pointer to any object or function. That’s it. So following from this I’ll do understand that any checks against nullptr (of a pointer that can be inferred to be addressing an object or function) are spurious and may be wiped out by compiler optimization. It does not say anything about undefined behavior either for now.

When “an lvalue does not designate an object when it is evaluated, the behavior is undefined” (C99 6.3.2.1 “Lvalues, arrays, and function designators”):
An lvalue is an expression with an object type or an incomplete type other than void; if an lvalue does not designate an object when it is evaluated, the behavior is undefined.
So, the same idea in brief:
When -> was executed on the pointer, it evaluated to an lvalue where no object exists, and as a result the behavior is undefined.

This argumentation holds not solely for the null pointer address value, it holds for any address that’s not valid to access or which doesn’t hold an object or function.

Connecting the two is the important part, which could be made more explicit. Quote 1 asserts no object/function can be pointed by the null pointer value, Quote 2 asserts that dereferencing a non object is UB, hence dereferencing the null pointer is UB.

What you don’t cover is the situation where the platform leaves information in the same address value as the implementation attributes the null pointer.

If the language/implementation is interfering on the access of the entire access space, does it turn one unable to have uniform access to it?

Also, doesn’t undefined behavior also cover implementation defined behavior so that this situation can be circumvented?.

The only thing one can conclude from the quotations is that there may be undefined behavior as you have been picture it (e.g. object code elimination).

Regarding exploitations by manipulating page-zero, it’s not a language’s role to foresee systems that may be exploitable through any specific address, the issue here lies in the language itself with the additional rules in brings about nullptr (which is gives room for code elimination and what-not).

Show your support

Clapping shows how much you appreciated Francisco Lopes’s story.