我使用的是带有 glibc 的 Linux 64 位
ldd (Ubuntu EGLIBC 2.19-0ubuntu6.15) 2.19
这是我的代码
int main (int argc , char** argv){
char *first , *second;
first = malloc(16);
second = malloc(16);
free(first);
free(second);
}
问题是我在互联网上研究过,当调用 free 时,它会清除下一个块的
prev_in_use
位,下面是第一次调用 free
后这两个块的内存区域。
gdb-peda$ x/20gx 0x602010-16
0x602000: 0x0000000000000000 0x0000000000000021
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000021
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000020fc1
0x602050: 0x0000000000000000 0x0000000000000000
现在的问题是我研究了下一个相邻块的 prev_in_use 位被清除,并且下一个块的
prev_size
将包含此释放块的大小,但从上面的输出来看,prev_in_use
位都没有被清除,大小也没有被清除。该块写入下一个块prev_size
字段。
问题是什么?任何帮助将不胜感激。
问题是 glibc 比这稍微复杂一点。它使用多种方法来存储空闲块。在您显示的示例中,由于您正在使用的块的大小,它们将进入 tcache。
glibc 的问题是其 malloc 实现(ptmalloc2 算法)在不同版本之间差异很大。您应该包含您正在使用的 glibc 版本。不管怎样,假设你的 ubuntu 是最新的,你可能使用的是 glibc 2.35。您可以在下面看到
_int_free()
的代码,它是实现 free() 主要功能的代码。
static void
_int_free (mstate av, mchunkptr p, int have_lock)
{
// [... declaration of variables and a couple of security checks]
#if USE_TCACHE
{
size_t tc_idx = csize2tidx (size);
if (tcache != NULL && tc_idx < mp_.tcache_bins)
{
/* Check to see if it's already in the tcache. */
tcache_entry *e = (tcache_entry *) chunk2mem (p);
/* This test succeeds on double free. However, we don't 100%
trust it (it also matches random payload data at a 1 in
2^<size_t> chance), so verify it's not an unlikely
coincidence before aborting. */
if (__glibc_unlikely (e->key == tcache_key))
{
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)
{
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");
/* If we get here, it was a coincidence. We've wasted a
few cycles, but don't abort. */
}
}
if (tcache->counts[tc_idx] < mp_.tcache_count)
{
tcache_put (p, tc_idx);
return;
}
}
}
#endif
在进行一些表面检查之后,该函数立即查看该块的大小是否使其有资格放置在 tcache 中。这是通过 csize2tidx (意思是“tcache 索引的块大小”)完成的,这是因为 tcache 有多个 bin,一个用于不同的块大小,所以这个宏的作用是获取一个大小并告诉 tcache bin 的索引该大小对应,然后我们检查该索引是否确实存在,这有效地设置了块在放入 tcache 之前可能具有的大小限制,该限制在 x64 中和典型配置中为 1024。