Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Comments on Is it UB to read value of heap pointer after freeing?
Parent
Is it UB to read value of heap pointer after freeing?
I've read in a guide that "the use of indeterminate memory for anything, including apparently harmless comparison or arithmetic, can have undefined behavior if the value can be a trap representation for the type."
The included quote from C11, section 6.2.4 §2, says:
The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.
The reason I'm mentioning the quote here is partially because I don't have a copy of the C standard.
Is reading the value of a pointer returned by a heap allocation function after its deallocation truly an instance of undefined behavior? (For example, debuggers may read and output the address to various I/O destinations: printer, screen, disk, network, etc.)
Consider the following program:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
void *pointer = malloc(1);
if (pointer == NULL) return 1;
free(pointer);
printf("%p\n", pointer); /* Reading the address. Undefined behavior? */
return 0;
}
Does the specification for this differ across ANSI C and other versions of ISO C?
Post
It may or may not be undefined behavior:
- Depending on if the ABI of the given target system has trap representations for pointers and the value we ended up now happens to be a trap representation.
- Otherwise it is unspecified behavior and will work fine, but print garbage.
(C23 renamed trap values "non-value representations".)
Be aware that there is a lot of misinformation and confusion over this out on the Internet, so you must dismiss anyone who cannot give you direct quotes from the C standard. There are lots of people subjectively claiming "it is undefined behavior because I say so"...
This is a recurring "language lawyer" question.
Contrary to popular belief, it is not UB to read an indeterminate value, it is unspecified behavior. With some special exceptions, details and C standard sources are given here.
The pointer variable was initialized so it is not indeterminate for that reason. Thus it doesn't matter that it has automatic storage duration or if its address was taken etc. It became indeterminate after the free call.
The pointer value in itself may be a trap representation for that given pointer type, in which case the program invokes undefined behavior. (Which is highly unlikely in practice since nothing happened that changed the pointer lvalue itself.)
Otherwise, the pointer becomes an indeterminate value which is unspecified. These kind of "wobbly values" have been up for debate a couple of times in the C committee and the conclusion were pretty much that the value need not be consistent if read several times, or the compiler might give the same garbage value every time - we don't know and we can't assume anything about it.
Unfortunately, some bad compilers chose to be so-called "low quality implementations" in this case. That is, the compiler chose to crash and break programs needlessly when it doesn't have to. Example: clang 15 miscompiles code accessing indeterminate values. From clang 15 for x86, the compiler chose to become bats*** crazy and just cut program generation halfways, giving the programmer a half program to execute with seg fault as guaranteed outcome, which is obviously useless. That compiler remains just as broken as of today.
However, the specific example of the question (which is far less nasty than the one I cooked up in the SO post) actually runs even in clang v22 x86, printing a garbage value. x86_64 does at least to my knowledge not have any trap representations for void* or other object pointers.
EDIT
To clarify to everyone why accessing the pointer variable itself while it is indeterminate cannot reasonably be UB, lets consider this:
int* p=NULL;
{
int var=1;
if(something)
p=&var;
}
if(p == NULL)
....
After the inner scope ends, p might point to a variable no longer existing - the value of p is indeterminate. Now if this was UB, then we couldn't do the final if(p == NULL) check or the program might crash on that line. That's not sensible, it would break the whole language.
Even more obvious:
int* p = malloc(n);
free(p);
p = NULL;
If using p while the value is indeterminate, you wouldn't even be able to write bog standard code like the above, because the assignment involves a value computation of the left operand p before it is written to (C23 6.5.17.1).
Summary: if accessing the pointer while it's value is indeterminate, that pointer variable would turn into some kind of explosive mine that might crash the program if we ever try to reuse it again. We would never be able to verify or re-use pointer variables.

1 comment thread