lun4t1c@home:~$

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。