I am trying to track use-after-free errors in C.
And my question is that, if I have the code like this:
A * ptrA = malloc(sizeof(A));
A * aliasA = ptrA;
// do something '
free(ptrA)
// some other code
B * ptrB = malloc(sizeof(B)); // assume this return same pointer as ptrA
//trying to use aliasA here
Just wondering if the use of aliasA is a UAF error?
If it is, what is going wrong here?
To clear the question, I think it is better to add a small example:
int main(){
int *ptr = (int *)malloc(4);
*ptr = 5;
int *ptr2 = ptr;
printf("%d\n", *ptr);
free(ptr);
int *new_ptr = malloc(4);
*new_ptr = 66;
printf("%d\n", *ptr2);
return 0;
}
And the output is:
5
66
(I checked ptr
and new_ptr
in S2E: http://s2e.systems/ and these two pointers actually point to the same address. After freeing ptr, the same address is allocated to new_ptr.)
From the output above, it seems like the use of ptr2
gives the same output as new_ptr
.
When I wrote my solution to detect UAF error, I record the information of pointers. The pointers' values are stored as uint64_t
and a boolean type flag
is to declare whether the pointer is alive.
Therefore, I guess a problem occurs when the new_ptr
and ptr
point to same address because once malloc()
is called the flag
for new_ptr
will turn true
. After that, when I use ptr
, I can not detect this UAF error because this address is marked alive.
Thanks in advance!
If you don't dereference the pointer aliasA
, it depends on the definition of "use after free" whether this constitutes "use after free". For example CWE-416 doesn't talk about just using the pointer value, but about dereferencing it, i.e. using the object that was freed.
However, the C standard says that even using the pointer value has undefined behaviour (Appendix J.2):
The behavior is undefined in the following circumstances:
[...]
- The value of a pointer that refers to space deallocated by a call to the free or realloc function is used (7.22.3).
This is because the value of the pointer becomes indeterminate:
The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.
Therefore the following code has undefined behaviour:
A *ptrA = malloc(sizeof(A));
A *aliasA = ptrA;
free(ptrA);
A *ptrB = malloc(sizeof(A));
if (aliasA == ptrB) { // undefined behaviour, as it might be a trap
printf("We were given the same pointer");
}
The comparison there is meaningless and the compiler is given the freedom to do whatever it pleases with its optimization, as aliasA
doesn't need to contain a valid value any more. It is totally OK for a compiler to even to set a trap into aliasA
which would cause your program to abort at the if
statement with a diagnostics message. Or it can be that these could seem to point to the same address even though they were given distinct addresses of memory, or vice versa.
This is a UAF error.
In practice, it is very unlikely that the second malloc()
call returns the same address as aliasA
, as the behavior is not guaranteed. You should not rely on it.
You may ask: Can I compare them to make sure they have the same address? Well, no. Comparing may make some sense to you, but it is undefined behavior, because pointer equality comparison is valid only if the two pointers point to different elements in the same array. In this case the two allocated blocks surely aren't the same array, so comparison between those pointers isn't well-defined.
At last, you shouldn't rely on the address returned by malloc()
. All you can do is use it within the amount of memory requested when calling.
In practice, Thiru Shetty's answer is a good way to pursue.
The aliasA
is UAF error, it may hold the same value as ptrA
but it's not guaranteed and it's recommended to re-assign the freed pointer (both aliasA and ptrA) to NULL
to avoid dangling pointer.
In your case, it's better to assign NULL
after you free
:
free(ptrA);
free(aliasA);
ptrA = NULL;
aliasA = NULL;