pom*_*ing 5 c malloc glibc heap-memory heap-corruption
我在 linux 上玩了很长时间的二进制开发,最近我正在写一些基于ptmalloc 的堆开发笔记,所以我回去查看我过去解决的安全挑战中的一些有效负载,令人惊讶的是他们没有不再工作了。
比如基本的double free corruption(不是fastbin)
char *chunk1 = malloc(0xc0);
free(chunk1);
free(chunk1);
Run Code Online (Sandbox Code Playgroud)
我希望看到类似的东西
*** Error in `main': double free or corruption (top): 0x0000000000c85010 ***
Run Code Online (Sandbox Code Playgroud)
但是没有,什么也没有发生,程序正常退出。
为此,我去检查了与我的机器相对应的 glibc 源代码Debian GLIBC 2.27-2,发现malloc.c.
void *
__libc_malloc (size_t bytes)
{
...
#if USE_TCACHE
/* int_free also calls request2size, be careful to not pad twice. */
size_t tbytes;
checked_request2size (bytes, tbytes);
size_t tc_idx = csize2tidx (tbytes);
MAYBE_INIT_TCACHE ();
DIAG_PUSH_NEEDS_COMMENT;
if (tc_idx < mp_.tcache_bins
/*&& tc_idx < TCACHE_MAX_BINS*/ /* to appease gcc */
&& tcache
&& tcache->entries[tc_idx] != NULL)
{
return tcache_get (tc_idx);
}
DIAG_POP_NEEDS_COMMENT;
#endif
Run Code Online (Sandbox Code Playgroud)
在USE_TCACHE也出现在许多其他地方。
然后我又回到我的程序上面,发现chunk1并没有放在small bin而是在tcache_entry。
的目的是TCACHE什么?我搜索了很多,仍然很困惑。
小智 7
线程本地缓存(tcache)是 glibc 中的性能优化。不幸的是,正如您后来发现的那样,它是以牺牲安全性为代价的,并使某些攻击变得更加容易。
来自https://sourceware.org/glibc/wiki/MallocInternals#Thread_Local_Cache_.28tcache.29
虽然这个 malloc 知道多个线程,但这几乎就是它的意识范围 - 它知道有多个线程。此 malloc 中没有代码可以针对 NUMA 架构进行优化、协调线程局部性、按核心对线程进行排序等。假设内核能够很好地处理这些问题。
每个线程都有一个线程局部变量,用于记住它上次使用的 arena。如果该 arena 正在使用,而线程需要使用它,则线程将阻塞以等待 arena 空闲。如果线程之前从未使用过竞技场,那么它可能会尝试重用未使用的竞技场、创建新的竞技场或选择全局列表中的下一个竞技场。
每个线程都有一个每线程缓存(称为 tcache),其中包含一小部分块的集合,无需锁定竞技场即可访问这些块。这些块存储为单链表数组,如 fastbins,但链接指向有效负载(用户区域)而不是块头。每个 bin 包含一个大小的块,因此数组按块大小(间接)索引。与 fastbin 不同,tcache 受到每个 bin 中允许的块数量 (tcache_count) 的限制。如果 tcache bin 对于给定的请求大小为空,则不会使用下一个较大的块(可能会导致内部碎片),而是使用正常的 malloc 例程,即锁定线程的区域并从那里开始工作。