chux chux - 2 months ago 8
C Question

Is `free(a_comparable_pointer)` well defined or UB?

Certainly allocating and freeing the same pointer is well defined ...

void *p1 = malloc(42);
assert(p1);
...
free(p1);


... and converting though
intptr_t/uintptr_t
creates a comparable pointer (as in "compare equal" C11 7.20.1.4) when these integer types exist, although not necessarily the same bit pattern. One could say
p2
and
p3
have the same value, but may have differing representations.

void *p2 = malloc(42);
assert(p2);
uintptr_t u2 = (uintptr_t) p2;
...
void *p3 = (void *) u2;

// Specified to evaluate true C11 7.20.1.4
if (p2 == p3) ...

// Maybe true, maybe false
if (memset(&p2, &p3, sizeof p2) == 0) ...


Now to
free()
via a comparable pointer.


The
free
function causes the space pointed to by
ptr
to be deallocated, ... if the argument does not match a pointer earlier returned by a memory management function ... behavior is undefined. C11dr ยง7.22.3.3 3


void *p4 = malloc(42);
assert(p4);
uintptr_t u4 = (uintptr_t) p4;
...
void *p5 = (void *) u4;
...
free(p5); // UB or not UB?


So the title question appears to come done to:

Is "comparable" sufficient to "match" concerning
free()
?

I suspect
free(p5)
is undefined behavior (UB), but am not certain. No particular application in mind - just trying to understand the corners of C, no rush.

Answer

References are to N1570, the latest publicly available draft of the 2011 ISO C standard (C11).

void *p4 = malloc(42);
assert(p4);
uintptr_t u4 = (uintptr_t) p4;
...
void *p5 = (void *) u4;
...
free(p5);  // UB or not UB?

The definition of uintptr_t guarantees that the pointer values p4 and p5 compare equal; more tersely, p4 == p5. From the definition of == for pointers in 6.5.9p6, we know that p4 and p5 point to the same object (since we've already established that the value of p3 is not a null pointer).

This doesn't guarantee that they have the same representation. The standard says very little about the representation of pointers (except that they have a representation), so it's entirely possible that p4 and p5 have different representations. The standard explicitly permits this in 6.2.6.1p4:

Two values (other than NaNs) with the same object representation compare equal, but values that compare equal may have different object representations.

(That may or may not actually be possible, depending on the implementation.)

Now the question is, what exactly is passed to free?

Function calls are described in 6.5.2.2. Paragraph 4 says:

In preparing for the call to a function, the arguments are evaluated, and each parameter is assigned the value of the corresponding argument.

(Emphasis added.)

So what free() sees is not the object representation of p5 (which might differ from the object representation of p4), but the value of p5, which is guaranteed to be the same as the value of p4. It's likely that the mechanism by which that value is passed might involve making a copy of the object representation, but all the standard says is that the value is passed, and free must treat any distinct representations of the same value in the same way.

Comments