mirror of
https://sourceware.org/git/glibc.git
synced 2025-07-29 11:41:21 +03:00
malloc: Improved double free detection in the tcache
The previous double free detection did not account for an attacker to use a terminating null byte overflowing from the previous chunk to change the size of a memory chunk is being sorted into. So that the check in 'tcache_double_free_verify' would pass even though it is a double free. Solution: Let 'tcache_double_free_verify' iterate over all tcache entries to detect double frees. This patch only protects from buffer overflows by one byte. But I would argue that off by one errors are the most common errors to be made. Alternatives Considered: Store the size of a memory chunk in big endian and thus the chunk size would not get overwritten because entries in the tcache are not that big. Move the tcache_key before the actual memory chunk so that it does not have to be checked at all, this would work better in general but also it would increase the memory usage. Signed-off-by: David Lau <david.lau@fau.de> Reviewed-by: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
This commit is contained in:
committed by
Wilco Dijkstra
parent
e83207c6e6
commit
eff1f680cf
@ -3226,21 +3226,24 @@ tcache_available (size_t tc_idx)
|
||||
/* Verify if the suspicious tcache_entry is double free.
|
||||
It's not expected to execute very often, mark it as noinline. */
|
||||
static __attribute__ ((noinline)) void
|
||||
tcache_double_free_verify (tcache_entry *e, size_t tc_idx)
|
||||
tcache_double_free_verify (tcache_entry *e)
|
||||
{
|
||||
tcache_entry *tmp;
|
||||
size_t cnt = 0;
|
||||
LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
|
||||
for (tmp = tcache->entries[tc_idx];
|
||||
tmp;
|
||||
tmp = REVEAL_PTR (tmp->next), ++cnt)
|
||||
for (size_t tc_idx = 0; tc_idx < TCACHE_MAX_BINS; ++tc_idx)
|
||||
{
|
||||
if (cnt >= mp_.tcache_count)
|
||||
malloc_printerr ("free(): too many chunks detected in tcache");
|
||||
if (__glibc_unlikely (!aligned_OK (tmp)))
|
||||
malloc_printerr ("free(): unaligned chunk detected in tcache 2");
|
||||
if (tmp == e)
|
||||
malloc_printerr ("free(): double free detected in tcache 2");
|
||||
size_t cnt = 0;
|
||||
LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
|
||||
for (tmp = tcache->entries[tc_idx];
|
||||
tmp;
|
||||
tmp = REVEAL_PTR (tmp->next), ++cnt)
|
||||
{
|
||||
if (cnt >= mp_.tcache_count)
|
||||
malloc_printerr ("free(): too many chunks detected in tcache");
|
||||
if (__glibc_unlikely (!aligned_OK (tmp)))
|
||||
malloc_printerr ("free(): unaligned chunk detected in tcache 2");
|
||||
if (tmp == e)
|
||||
malloc_printerr ("free(): double free detected in tcache 2");
|
||||
}
|
||||
}
|
||||
/* No double free detected - it might be in a tcache of another thread,
|
||||
or user data that happens to match the key. Since we are not sure,
|
||||
@ -3428,7 +3431,7 @@ __libc_free (void *mem)
|
||||
|
||||
/* Check for double free - verify if the key matches. */
|
||||
if (__glibc_unlikely (e->key == tcache_key))
|
||||
return tcache_double_free_verify (e, tc_idx);
|
||||
return tcache_double_free_verify (e);
|
||||
|
||||
if (__glibc_likely (tcache->counts[tc_idx] < mp_.tcache_count))
|
||||
return tcache_put (p, tc_idx);
|
||||
|
Reference in New Issue
Block a user