Just how delicate are freed pointers?

L

Leor Zolman

pete said:
Leor said:
David Scarlett wrote:

Is the following code safe?

No.

Hmmm. Assuming a was valid before the free() and b still is at the
point of the comparison, I don't get why you think this is unsafe.
Personally, it seems strange to test the value of a pointer after it
has been freed (and your version is more conventional in that sense),
but since we're only testing the /pointer/ and not trying to access
the memory it is pointing to, why wouldn't the OP's code be "safe"?
-leor


N869
7.20.3 Memory management functions
[#1]
The value of a pointer
that refers to freed space is indeterminate.

This sentence is absent from the final C99 Standard,
presumably because it makes no sense. (If the pointer's
value is indeterminate, how can that pointer be said to
refer to anything? But if the pointer actually refers to
something, how can its value be called indeterminate?)

It sort of moved to 6.2.4/2:

"The value of a pointer becomes indeterminate when
the object it points to reaches the end of its lifetime."

-leor
 
K

Keith Thompson

Stephen L. said:
It's not the value of "a" which was valid before
the call, it's the validity of what "a" pointed to
before/after the call to `free()' that has changed.

This is a subtle but important distinction and
the last sentence of 7.20.3 supports this IMHO.

(For clarity, I'm going to use "p" rather than "a".)

Yes, it's an important distinction (not all that subtle in my opinion,
but I won't argue the point). It's a distinction that we understand
quite well.

After a call to free(p), the memory that "p" pointed it can no longer
be accessed, so any reference to *p invokes undefined behavior. (You
might get away with it, but I think there are real-world
implementations on which such an attempt can cause a trap of some
sort, perhaps a segmentation fault.) I think we agree on this point.

It is also the case that the value of p itself becomes indeterminate,
even if the bits composing that value don't change. Any reference to
an indeterminate value invokes undefined behavior. You're very likely
to get away with it on most real-world implementations, but a
conforming implementation could cause a trap on the following code:

free(p);
p;
 
S

Stephen L.

Keith said:
(For clarity, I'm going to use "p" rather than "a".)

Yes, it's an important distinction (not all that subtle in my opinion,
but I won't argue the point). It's a distinction that we understand
quite well.

After a call to free(p), the memory that "p" pointed it can no longer
be accessed, so any reference to *p invokes undefined behavior. (You
might get away with it, but I think there are real-world
implementations on which such an attempt can cause a trap of some
sort, perhaps a segmentation fault.) I think we agree on this point.

Yes, I agree.
It is also the case that the value of p itself becomes indeterminate,

This is where I read "the value of `p' as a pointer becomes
indeterminate".
Replace "value" with "worth" and that's how it strikes me.
even if the bits composing that value don't change. Any reference to
an indeterminate value invokes undefined behavior. You're very likely
to get away with it on most real-world implementations, but a
conforming implementation could cause a trap on the following code:

free(p);
p;

My question is simply, how does the compiler
_know_ p's value has been `free()'d (I'm not
trying to be silly). Suppose the call to `free()'
was made by some other function, such that -

some_function_which_calls_free(p);
p;

Also, the OP never mentioned dereferencing `p',
only seeing if his _shadow_ pointer variable
pointed to the same object as `p' once did -
a simple arithmetic comparison.

I haven't seen this much activity in a thread
for a long time!


Stephen
 
C

Christian Bau

"Stephen L. said:
My question is simply, how does the compiler
_know_ p's value has been `free()'d (I'm not
trying to be silly). Suppose the call to `free()'
was made by some other function, such that -

some_function_which_calls_free(p);
p;

If you want to get an impression what an implementation of a programming
language can do, try to find a book about the Java Virtual Machine
specification (you can probably find it at Sun's webpage). You'll find
objects that magically disappear when nobody points to them anymore, and
even pointers that magically turn to null pointers behind your back (and
only behind your back; these pointers are fine as long as you keep an
eye on them).

To answer your question: The call to free () can change something in the
hardware so that p; will crash. Or the compiler replaces the statement
p; with something like "__check_pointer_valid (p); ", and the function
__check_pointer_valid could have complete access to all static and
extern variables, complete knowledge about all local variables including
variables kept in registers, and complete knowledge about all allocated
memory (that is the easy part).
 
T

Thomas stegen

Stephen said:
My question is simply, how does the compiler
_know_ p's value has been `free()'d (I'm not
trying to be silly).

That is really irrelevant. The standard makes it quite clear
that the value of the pointer is indeterminant and that the
use of such values result in undefined behaviour.

A compiler does not need to do anything to make a program
with undefined behaviour to go wrong. Accessing a wild pointer
might cause a segfault for example, the compiler did nothing.
The code generated is exactly the same (except for the code
that sets the value of the pointer) as if you did not have
a wild pointer. Yet you still segfault.

The notion that a compiler somehow detects undefined behaviour
and then does something dangerous is really rather misguided.

The rationale is probably that for some implementation it does
matter and that this restriction makes things much easier for
implementors. Or it is unnecssary, but is caught by some catch
all construct in the standard. Still, it is undefined behaviour.

Something isn't illegal because the police catches you.
 
K

Keith Thompson

Stephen L. said:
Keith Thompson wrote: [...]
even if the bits composing that value don't change. Any reference to
an indeterminate value invokes undefined behavior. You're very likely
to get away with it on most real-world implementations, but a
conforming implementation could cause a trap on the following code:

free(p);
p;

My question is simply, how does the compiler
_know_ p's value has been `free()'d (I'm not
trying to be silly). Suppose the call to `free()'
was made by some other function, such that -

some_function_which_calls_free(p);
p;

The compiler very likely doesn't "know" that p's value has been
free()'d.

It's possible, in a conforming implementation, for the runtime system
to keep track of all allocated memory (stack, heap, static storage,
whatever), so it knows, for any given pointer value, whether it points
to a memory location which the current program is allowed to
reference. Such an implementation could check, on each reference to
an address value, whether the address is either a null pointer or a
pointer to a valid memory location, and cause a trap if it isn't.
This tracking and checking could be implemented either in software
(for an aggressive debugging implementation) or in hardware.

IBM's AS/400 has been cited as a system that does some of this kind of
thing (pointers are some kind of descriptor, not physical memory
addresses); I don't remember whether it does this particular check.

It's also possible (and, as it happens, more likely) for an
implementation to ignore the issue altogether. If a program
references, or even dereferences, an invalid pointer value, it invokes
undefined behavior. There is no requirement for the implementation to
do anything in particular. That's what "undefined behavior" means.
Also, the OP never mentioned dereferencing `p',
only seeing if his _shadow_ pointer variable
pointed to the same object as `p' once did -
a simple arithmetic comparison.

A comparison between two pointers is a pointer comparison, not an
arithmetic comparison. One of the most important things to remember
is that pointers are not numbers, even though they may share some of
the characteristics of numbers (they're made of bits and you can
perform some very limited arithmetic operations on them).

But for example:

int *p = malloc(sizeof *p); /* assume malloc() succeeds */
int *q = p;

free(p);

After the call to free(), any reference to the value of either p or q
(including a simple pointer equality comparison) will invoke undefined
behavior.
 
C

CBFalconer

Keith said:
.... snip ...

But more commonly, I think, code that checks the value of x after
calling free() is buggy. Setting x to NULL is as likely to mask
the bug as to fix it.

Not necessarily. I have a mechanism to delete entries in hashlib,
for example. To do so I extract a pointer from the hash table,
and set the entry to a known value (DELETED). This can't be NULL
because that would foul the chaining, but that is the only
reason. The removed pointer may or may not be passed to free
(that is up to the code that called the delete routine). However
the previous home of that pointer will be involved in future
pointer comparisons.
 
C

CBFalconer

Stephen L. said:
My question is simply, how does the compiler
_know_ p's value has been `free()'d (I'm not
trying to be silly). Suppose the call to `free()'
was made by some other function, such that -

some_function_which_calls_free(p);
p;

It doesn't. The run-time system may know, but it doesn't have to
either. The point is that the action is illegal.
 
D

Dan Pop

In said:
Which is an argument for always using:

#define FREE(x) do {free(x); x = NULL;} while (0)

ensuring we can always use x normally in equality comparisons.

That's a ludicrously baroque version of:

#define FREE(x) (free(x), x = NULL)

Furthemore, my macro has the semantics of a function call and can be
used anywhere a function call is legal, yours looks like a function but
expands to a statement.

Dan
 
D

Dan Pop

In said:
I suppose it could be useful for something like this:

while (some_condition) {
if (some_other_condition) {
FREE(x);
break;
}
}
if (x == NULL) { /* check whether x was free'd */
blah;
}

But more commonly, I think, code that checks the value of x after
calling free() is buggy. Setting x to NULL is as likely to mask the
bug as to fix it.

Yes and no. If one is very disciplined about his usage of pointers,
he can adopt the convention that bad pointers are *immediately* nullified.
So, whenever a pointer is non-null, it can be assumed to be a good
pointer. This is especially useful for functions that check the validity
of their parameters: there is no way to portably and safely detect that
you have received a bad pointer value, unless you're using such a
convention.

OTOH, this macro is only a small part of such an approach: ALL the other
pointers that might be pointing in the freed block must be nullified and
there is no way of automating this operation. This is what makes this
approach quite fragile. I wouldn't advocate it, as I don't advocate
functions checking the validity of their parameters in the first place,
but some people seem to love such things...

Dan
 
J

Joona I Palaste

Dan Pop said:
In said:
Dan Pop said:
CBFalconer <[email protected]> scribbled the following:
Splutter. Hogwash. Bullshit. Merde. Schiesse. :) I think
you mean that such a change is one of the many undefined
behaviours available.

You mean "scheisse". If there is "ei" or "ie" in a German word, you
can depend on native English speakers to spell it wrong. Likewise if
there is "ou" or "uo" in a Finnish word, you can depend on Swedes to
spell it wrong.
And you SHOULD add an [OT] tag in the subject line when making off topic
comments/corrections!

So should you. =)
Nope: my post was topicality related and, therefore, topical ;-)

It's a fair cop, guv.
 
J

James Kanze

(e-mail address removed) (Dan Pop) writes:


|> >Stephen Sprunk wrote:
|> >>
|> >... snip ...

|> >> Nope, it's valid on some implementations and allowed by the
|> >> Standard. See the previous thread for citations.

|> >Which is an argument for always using:

|> > #define FREE(x) do {free(x); x = NULL;} while (0)

|> >ensuring we can always use x normally in equality comparisons.

|> That's a ludicrously baroque version of:

|> #define FREE(x) (free(x), x = NULL)

|> Furthemore, my macro has the semantics of a function call and can be
|> used anywhere a function call is legal, yours looks like a function
|> but expands to a statement.

And of course, neither really works:

char *array[N] ;
int i = 0 ;
while ( i < N ) {
FREE( array[ i ++ ] ) ;
}
 
D

Dan Pop

In said:
(e-mail address removed) (Dan Pop) writes:


|> >Stephen Sprunk wrote:
|> >>
|> >... snip ...

|> >> Nope, it's valid on some implementations and allowed by the
|> >> Standard. See the previous thread for citations.

|> >Which is an argument for always using:

|> > #define FREE(x) do {free(x); x = NULL;} while (0)

|> >ensuring we can always use x normally in equality comparisons.

|> That's a ludicrously baroque version of:

|> #define FREE(x) (free(x), x = NULL)

|> Furthemore, my macro has the semantics of a function call and can be
|> used anywhere a function call is legal, yours looks like a function
|> but expands to a statement.

And of course, neither really works:

char *array[N] ;
int i = 0 ;
while ( i < N ) {
FREE( array[ i ++ ] ) ;
}

Neither really works when *misused*. The point in spelling macros in
upper case is to warn the user that they are macros and not functions.

Dan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,163
Latest member
Sasha15427
Top