Re: How to know the memory pointed by a ptr is freed?

From: RCollins (rcoll_at_nospam.theriver.com)
Date: 08/20/04


Date: Thu, 19 Aug 2004 17:38:49 -0700


Keith Thompson wrote:

> RCollins <rcoll@nospam.theriver.com> writes:
>
>>Flash Gordon wrote:
>
> [...]
>
>>>You failed to include stdlib.h which is required for malloc. Your
>>>compiler was required to generate a diagnostic for this line if invoked
>>>in ANSI/ISO mode.
>>
>>Let's not nit-pic the trivialities; this is example code, not
>>a lesson for a newbie.
>
>
> If you don't want trivialities nit-picked, I humbly submit that you've
> come to the wrong newsgroup. 8-)}

I think you are right ...

>
> [...]
>
>>>BANG. The pointer might be loaded in to an address register causing
>>>the
>>>program to crash. The C standard does not allow you to read the value of
>>>the pointer after you have freed it.
>>
>>Er ... no. Why would the contents of "ptr" be placed into an address
>>register? I would expect the address of "ptr" (i.e., &ptr) to be
>>loaded into an address register, and the contents of "ptr" to be
>>loaded into a data register.
>
>
> Why *wouldn't* the contents of "ptr" be placed into an address
> register?
>
> If you're going to assume characteristics that are common to most
> real-world implementations but aren't guaranteed by the standard, you
> might as well just save the value as a pointer:
>
> int *ptr = malloc(whatever);
> int *before = ptr;
> free(ptr);
> if (ptr == before) { /* warning: undefined behavior */
> printf("bits didn't change\n");
> }
> else {
> printf("bits changed\n");
> }
>
> Or, for that matter:
>
> printf("bits didn't change\n"); /* 8-)} */
>
> If you're going to avoid undefined behavior, there's not much point in
> going halfway. memcpy()ing the value to an array of unsigned char
> avoids UB; casting to unsigned long doesn't.
>
>
>>Also, you can read the contents of "ptr" after a call to free(), but
>>you cannot read the contents of the memory *pointed to* by "ptr".
>
>
> That's incorrect.
>
> In most real-world implementations, you can successfully read both the
> contents of ptr and the memory pointed to by ptr after a call to
> free(). It's undefined behavior, but most implementations don't do
> anything to make it fail. An address is an address, memory is memory,
> and you can still read it even if you no longer "own" it. (In some
> cases, if the memory is actually returned to the OS and removed from
> the program's visible memory space, a reference to the deallocated
> memory might actually trap; a reference to just the pointer value is
> less likely to do so.)
>
> But as far as the standard is concerned, attempting to refer to the
> allocated memory after the call to free() invokes undefined behavior
> (as you know) *and* the pointer value itself becomes indeterminate
> (even if the bits don't change), so any attempt to refer to that value
> will also invoke undefined behavior. Even this:
>
> free(ptr);
> if (ptr == NULL) { ... }
>
> or this:
>
> free(ptr);
> unsigned long foo = (unsigned long)ptr;
>
> invokes undefined behavior. It happens that the typical result of
> this undefined behavior is to read the value of ptr, but the standard
> allows nasal daemons.
>
> Why is it defined this way? Because, for example, a conforming
> implementation might implement any reference to a pointer value
> (including a comparison to NULL or a conversion to unsigned long) by
> loading the pointer value into an address register, and the act of
> loading the value might do a validity check, and trap if it's invalid.
> Such a trap is not required, of course, but the point is that the
> standard doesn't render such an implementation non-conforming.
>
> There could be other reasons why referring to the value of ptr might
> cause problems; the address register thing is just one possible
> example.
>

Thanks for the explanation; that was very clear and well presented.
And yet a nagging question keeps going around in my mind ... you
say that a construct of the form

unsigned long foo = (unsigned long)ptr;

can lead to UB (since the compiler may perform some interpretation
of ptr), and yet

unsigned char foo[4]; /* just making an assumption for this example */
memcpy(foo, &ptr, sizeof(ptr));

does _not_ invoke UB. Why not? Isn't the compiler here able
to perform the same interpretation of "ptr" as it did on the
cast to an unsigned long?

-- 
Ron Collins
Air Defense/RTSC/BCS
"I have a plan so cunning, you could put a tail on it and call it a weasel"


Relevant Pages

  • Re: use delete to destroy primitive/object types but memory is not freed
    ... the safest approach is to think of a pointer as an opaque ... The call to mallocallocates memory space for a double object; ... We then assign a value to the double object that ptr points to, ... The C runtime system has reserved that chunk of memory, ...
    (comp.lang.c)
  • Re: How to know the memory pointed by a ptr is freed?
    ... The pointer might be loaded in to an address register causing ... In most real-world implementations, you can successfully read both the ... It's undefined behavior, ... An address is an address, memory is memory, ...
    (comp.lang.c)
  • Re: Malloc/Free - freeing memory allocated by malloc
    ... >> Both the following lines would invoke undefined behavior. ... The memory allocated by x is on the function stack, ... The value of a pointer that refers to freed space is indeterminate. ...
    (comp.lang.c)
  • Re: pass by Reference/value ???
    ... the code is totally bogus and exhibits undefined behavior. ... And the local copy of `s' has gone out of scope, causing a memory leak. ... uninitialized pointer, undefined behavior is invoked. ... It's an urban legend. ...
    (comp.lang.cpp)
  • Re: How to know the memory pointed by a ptr is freed?
    ... > And what about the 37 copies made of that pointer after it was ... the value of "ptr" ... pattern stored in "ptr", or does it mean that we cannot under any ... that we gave that memory back to the system and any access to it ...
    (comp.lang.c)