tcache check

struct tcache_perthread_struct *key

tcache_entry中增加了一项struct tcache_perthread_struct *key,将chunk放入tcache后,会将key修改为tcache

typedef struct tcache_entry
{
    struct tcache_entry *next;
    +  /* This field exists to detect double frees.  */
    +  struct tcache_perthread_struct *key;
} tcache_entry;

free的时候,如果发现keytcache就要进行double free的检查,这是因为key位于bk的位置,而tcache也不清除chunk,存在很小的可能碰巧为tcache

+    if (__glibc_unlikely (e->key == tcache && tcache))
+      {
+       tcache_entry *tmp;
+       LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
+       for (tmp = tcache->entries[tc_idx];
+            tmp;
+            tmp = tmp->next)
+         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.  */
+      }

因为很少发生,这里的检查也比较彻底,会检查该tc_idx中的每一项是否和当前要释放的堆块相同。这个check可以通过改写key来绕过。

pointer mangling

高版本的libc引入了pointer mangling,对tcachefastbin记录的fd指针进行了加密处理。具体的加密采用了异或操作,首先需要理解,0与任何数异或之后还是任意数本身,任何数异或自身为0,(这里的异或操作即可以是一位也可以是多位),因此当一个被加密的数异或两次另一个数,就会重新得到被加密的数。

具体的,两个重要的宏函数

#define PROTECT_PTR(pos, ptr) \
((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
#define REVEAL_PTR(ptr)  PROTECT_PTR (&ptr, ptr)

首先是PROTECT_PTR(pos, ptr),将pos右移12位,由于possize_t类型的,右移高位补0,然后与被加密数ptr异或。
接下来是解密函数REVEAL_PTR(ptr),将ptr自身的地址与ptr执行PROTECT_PTR(pos, ptr)操作。

PROTECT_PTR(pos, ptr)目前有三次引用,在tcache中只有一次。
e->next = PROTECT_PTR (&e->next, tcache->entries[tc_idx]);
tcache_put时,e->next即头插法新entry需要保存的指针,从原本的tcache->entries[tc_idx]变成了其与自身地址的异或关系。相应的,在tcache_get中通过tcache->entries[tc_idx] = REVEAL_PTR (e->next);恢复之前的tcache->entries[tc_idx]
不难可以发现,tcache->entries[tc_idx]中记录的entry是未加密的,但链表后续的指针都是加密后的。

posted @ 2023-01-04 12:01  benoqtr  阅读(85)  评论(0编辑  收藏  举报