tcache
# 关于 tcache
基本特性
-
Tcachebins 遵循 FILO 原则。
-
malloc 申请被放入 Tcachebins 中的 chunk 后,chunk 的 fd 指针不会被清空。利用这一点,有时可以泄露堆地址。
-
每个不同 size 的 tcache 链表默认最多能存放 7 个 chunk。
-
当 tcache 存满后,被 free 的 chunk 会被放入 Fastbins 或 Unsorted bin。
-
size 大于 0x408 的 chunk 被释放时会直接进入 unsorted bin。
-
tcache 在被 free 后不会清除 inuse 标志,不会被合并。
-
申请一个 chunk 时,如果该 chunks 的 size 在 tcache 的范围内,则首先会从 Tcachebins 中查找是否有符合条件的 chunk。
-
申请一个 chunk 时,如果该 chunks 的 size 在 tcache 的范围内,且对应的 Tcachebins 为空,则会从其他 bins 中查找是否有满足条件的 chunk。
tcache_put 和 tcache_get 函数的源码
static void
tcache_put (mchunkptr chunk, size_t tc_idx)
{
tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
assert (tc_idx < TCACHE_MAX_BINS);
e->next = tcache->entries[tc_idx];
tcache->entries[tc_idx] = e;
++(tcache->counts[tc_idx]);
}
static void *
tcache_get (size_t tc_idx)
{
tcache_entry *e = tcache->entries[tc_idx];
assert (tc_idx < TCACHE_MAX_BINS);
assert (tcache->entries[tc_idx] > 0);
tcache->entries[tc_idx] = e->next;
--(tcache->counts[tc_idx]);
return (void *) e;
}
tcache_entry 和 tcache_perthread_struct 结构体
/* We overlay this structure on the user-data portion of a chunk when the chunk is stored in the per-thread cache. */
typedef struct tcache_entry
{
struct tcache_entry *next;
} tcache_entry;
/* There is one of these for each thread, which contains the per-thread cache (hence "tcache_perthread_struct"). Keeping overall size low is mildly important. Note that COUNTS and ENTRIES are redundant (we could have just counted the linked list each time), this is for performance reasons. */
typedef struct tcache_perthread_struct
{
char counts[TCACHE_MAX_BINS];
tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;
static __thread tcache_perthread_struct *tcache = NULL;
-
tcache_perthread_struct 是整个 tcache 的管理结构。
-
tcache_perthread_struct 最先分配,它保存在堆段底部。
-
在 tcache_perthread_struct 结构体中,entries 数组中保存的结构体的 size 随 idx 的增加而增加。
-
tcache_entry 通过单链表连接 chunk 结构体。
-
tcache_entry 中的 next 指针指向下一个相同大小的 chunk 的 user data。
# 利用手法
tcache dup
- 利用原理
tcache_put 函数的检查过于宽松,以至于连续将同一 chunk 释放两次也不会造成程序崩溃。
- 具体利用
对同一 chunk 进行连续两次释放,再次申请该 chunk 后修改其 fd 指针指向目标内存,之后再经过两次同样的申请,便可在目标内存处得到一块可操作的 chunk。
# tcache bin 与 unsorted bin
- 当 tcache bin 与 unsorted bin 重合时,根据两者的特性及对 tcache dup 的利用,可以申请位于 libc 中的 chunk。具体可参见 ciscn_2019_final_3’s Write up。