TrustInSoft / tis-interpreter

An interpreter for finding subtle bugs in programs written in standard C
565 stars 28 forks source link

false positive dangling pointer #127

Open albertnetymk opened 7 years ago

albertnetymk commented 7 years ago
#include <stdlib.h>

int main()
{
  void *p = malloc(1);
  uintptr_t p_prime = (uintptr_t) p;
  free(p);
  (void) (p_prime == 0);
  // (void) (p == NULL);
  return 0;
}

The commented line is reading a freed pointer, so it's illegal. However, I would expect reading p_prime to be fine, for it's just an integer.

PS: http://trust-in-soft.com/dangling-pointer-indeterminate/ is a great read.

pascal-cuoq commented 7 years ago

The applicable clause is 6.2.4:2 in C11 (a similar clause has existed since C89). In particular:

The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.

You probably don't want to use an indeterminate value in a comparison. In C89, it is plainly undefined behavior. In later standards the situation is complicated but C compilers treat some uses of indeterminate values as if they were undefined behavior anyway regardless of arguments about types not having trap representations(discussion).

I cannot show an example where comparing a pointer to 0 after the lifetime of the pointed object produces unexpected result, but I can show an example where comparing pointers to objects that were alive at the same time past their lifetimes does produce unexpected result, where the behavior can only be justified by an agressive reading of 6.2.4:2 on the part of the compiler makers. Here is another example involving dynamic allocation through realloc.

In summary, the particular pattern free(p); p == 0; is actually undefined behavior (or at least uses an indeterminate value in a computation if you want to argue that that's different), and comes too close to patterns that compilers optimize to make an exception and not warn about it.

I agree that the conversion to uintptr_t, taking place while p is pointing to a live block, fixes the value of the non-null address of that block. Warning about the comparison of that value after the lifetime of the pointed block is an unfortunate side-effect of the way memory is currently modeled.