free(3) const void *

Russ Allbery eagle@eyrie.org
Fri Jan 26 18:09:35 GMT 2024


Alejandro Colomar <alx@kernel.org> writes:

> Since a `const void *` will accept anything that a `void *` would accept
> (and more), how about changing the prototype of free() in glibc as an
> extension to the language?

> I'd like to refor free(3) to be:

> 	void free(const void *p);

Maybe this way of explaining the objection will help.  Right now, if you
pass a const pointer into a function, you have some assurance from that
prototype (assisted by compiler diagnostics) that this function will not
modify *or invalidate* that pointer and you can continue using that
pointer after that call.  In other words, while C does not have full
Rust-style lifetime tracking, the const marker on a function approximately
indicates that the caller is not passing ownership of the pointer to that
function and the function call will not affect the pointer.

With this prototype, I believe you will create a situation where someone
could write a function that takes a const pointer and then calls free() at
the end of that function call on this passed parameter, and there would be
no warning from the compiler either when compiling that function or when
compiling a function that calls it and then uses the pointer afterwards
(at least unless there is enough inlining that a compiler can put all the
pieces together).

Unless I'm missing something, the more specific annotation indicating that
free() is a deallocator doesn't help because it doesn't propagate up the
call stack.  It will catch use after free within the same function, but it
won't catch the case where someone passes a const pointer down a whole
chain of functions that take const pointers, and then the function at the
bottom of that call stack frees the pointer, unless someone carefully
annotates every function in that call stack with that annotation.  When
this is done in error, someone obviously wouldn't do that.

The unique functionality of const here is that it *does* propagate up the
call stack via compiler warnings, in that you can't pass a const pointer
to a function with a non-const prototype without a compiler warning or an
explicit cast.  That provides some assurance of those lifetime properties.

In that view of the meaning of const, free() is definitely not const,
since it invalidates the pointer you pass into it and thus by definition
takes ownership of the pointer.  I realize this is not the precise
standard definition of const, but it is definitely what a lot of
real-world C code uses const to mean, and right now it basically works.

-- 
Russ Allbery (eagle@eyrie.org)             <https://www.eyrie.org/~eagle/>


More information about the Libc-alpha mailing list