How [can a bit pattern passed to free() become invalid]? It's still
the same address, if it's valid it's valid, if not then not. It's the
same bit pattern.
One canonical example -- which I believe to be *the* example that
got this put in the C standard, although I am just guessing --
occurs on the Intel 80x86 ("x86").
On this machine, pointers are 32 bits long, but composed of two
pieces called the "segment" and "offset". Ordinary "int"s, of
course, are only 16 bits long, so pointers must be loaded into
special pointer registers. The x86 is rather odd in that these
"pointer registers" are really two half-registers, a "segment
register" and an ordinary 16-bit integer register. The "offset"
portion of a 32-bit pointer is loaded into the ordinary register,
while the "segment" is loaded into one of the special segment
registers. (In keeping with the register-poor nature of this CPU,
there are only three segment registers, "cs", "ds", and "es" --
code, data, and "extra" segment -- although later CPUs add two more
called "fs" and "gs".)
Although this CPU is rather slow and klunky, it does have a nice
security feature that catches all kinds of programming errors.
This is: each "segment" value can be automatically tested for
validity each time a new segment number is loaded into a segment
register. This means that the underlying system (library and/or
operating system) can catch improper accesses to memory, such as
the use of a pointer after that pointer is freed.
Naturally, any C compiler where the system is intended to get
the right answer, even if that takes more time, *uses* this feature.
This means that, in the sequence:
q = p;
...
free(p);
x = q->member;
the attempt to access "q->member" is caught at runtime. But
it also means that even something like:
free(p);
q = p; /* or: if (p == q) */
may trigger a runtime fault, if the compiler chooses to copy or
compare p to q by loading p's value -- which was valid before
free(), but is no longer -- into a segment/offset register pair.
Luckily for virus-writers, C programmers and compiler-writers seem
to prefer getting the wrong answer as fast as possible. Actually
*using* this segmentation feature causes them to detect this wrong
answer, while getting right answers slightly slower, which most
consider unacceptable. So in practice, users of the 80x86 do not
see this behavior, even though it would stop so many software bugs.
(Note that these same segment registers also allow the system to
prevent execution of data as if it were code, stopping most of the
remaining virus-mechanisms. Again, this would prevent getting the
wrong answer as fast as possible, so it is not much used.)