# libc-cmp : libc-2.27 AND libc-2.31# Tcache 方面# Tcache 结构体数据类型
1 2 3 4 5 typedef struct tcache_perthread_struct { uint16_t counts[TCACHE_MAX_BINS]; tcache_entry *entries[TCACHE_MAX_BINS]; } tcache_perthread_struct;
counts 数组由 char 变成了 uint16_t,估计是为了防止元素被 Hacker 利用整数溢出为负数导致每个 bin 的 chunk 个数在之后的 counts 和 TCACHE_FILL_COUNT(数值 7)大小判断导致失误从而在满的 Tcache bin 中插入新的 chunk,从而防止一些 hack 行为,同时保证 Tcache 的执行效率(毕竟 Tcache 的加入是为了加快程序运行,Tcache bin 中放的 chunk 太多了也就失去了它存在的意义)。
# Tcache 的 put get2.31 的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 static __always_inline void tcache_put (mchunkptr chunk, size_t tc_idx) { tcache_entry *e = (tcache_entry *) chunk2mem (chunk); e->key = tcache; e->next = tcache->entries[tc_idx]; tcache->entries[tc_idx] = e; ++(tcache->counts[tc_idx]); } static __always_inline void *tcache_get (size_t tc_idx) { tcache_entry *e = tcache->entries[tc_idx]; tcache->entries[tc_idx] = e->next; --(tcache->counts[tc_idx]); e->key = NULL ; return (void *) e; }
2.27 的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 static __always_inline 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 __always_inline 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; }
可以看到去掉了 assert () 的内容。应该是为了防止重复判断导致运行效率下降。
# _int_malloc 函数检查条件增加原本取出 chunk 的时候只有这几行的检查:
1 2 3 4 if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0 ) || __builtin_expect (chunksize_nomask (victim) > av->system_mem, 0 )) malloc_printerr ("malloc(): memory corruption" );
现在变成了这一大坨:
1 2 3 4 5 6 7 8 9 10 11 12 13 if (__glibc_unlikely (size <= 2 * SIZE_SZ) || __glibc_unlikely (size > av->system_mem)) malloc_printerr ("malloc(): invalid size (unsorted)" ); if (__glibc_unlikely (chunksize_nomask (next) < 2 * SIZE_SZ) || __glibc_unlikely (chunksize_nomask (next) > av->system_mem)) malloc_printerr ("malloc(): invalid next size (unsorted)" ); if (__glibc_unlikely ((prev_size (next) & ~(SIZE_BITS)) != size)) malloc_printerr ("malloc(): mismatching next->prev_size (unsorted)" ); if (__glibc_unlikely (bck->fd != victim) || __glibc_unlikely (victim->fd != unsorted_chunks (av))) malloc_printerr ("malloc(): unsorted double linked list corrupted" ); if (__glibc_unlikely (prev_inuse (next))) malloc_printerr ("malloc(): invalid next->prev_inuse (unsorted)" );
可以看到取出 chunk 时对 chunk 的检查更加仔细,报错更加详细。 1. 检查当前 chunk 的 size 2. 检查 next chunk 的 size 3. 检查下一个 chunk 记录的 prev_size(就是记录的当前 chunk 的 size)是否相同。不相同就说明被篡改了 4. 检查双链表是否正常链接 5. 检查当前 chunk 是否被 next chunk 标记为 in use。
从 unsorted bin 取出 chunk 的时候检查了链表完整性:
1 2 3 if (__glibc_unlikely (bck->fd != victim)) malloc_printerr ("malloc(): corrupted unsorted chunks 3" );
取出 unsorted bin 中的 chunk 将要放入 large bin 增加了两个检查,检查了链表的完整性:
1 2 3 4 5 if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd)) malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)" ); if (bck->fd != fwd) malloc_printerr ("malloc(): largebin double linked list corrupted (bk)" );
使用 top chunk 的时候检查了 top chunk 的 size
1 2 if (__glibc_unlikely (size > av->system_mem)) malloc_printerr ("malloc(): corrupted top size" );
# _int_free 新条件检查chunk 向后合并的时候检查了当前 chunk 记录的 prev_size 和后面的 chunk 记录的 size 是否相同,若不同说明被篡改,报错:
1 2 3 4 5 6 7 8 9 if (!prev_inuse(p)) { prevsize = prev_size (p); size += prevsize; p = chunk_at_offset(p, -((long ) prevsize)); if (__glibc_unlikely (chunksize(p) != prevsize)) malloc_printerr ("corrupted size vs. prev_size while consolidating" ); unlink_chunk (av, p); }