Glibc-2.29 新增的防护机制

下面我介绍几种在 glibc-2.29 中增加的防护机制,由于本人才疏学浅, 可能仅是冰山一角。

文章对照的是 glibc-2.27 和 glibc-2.29 。

free

tcache

最明显的就是tcache

来自源码:glibc-2.29/malloc/malloc.c:4197

/* This test succeeds on double free.  However, we don't 100%
    trust it (it also matches random payload data at a 1 in
    2^<size_t> chance), so verify it's not an unlikely
    coincidence before aborting.  */
if (__glibc_unlikely (e->key == 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.  */
  }

这里会对tcache链表上的所有chunk进行对比,检测是否有重复,这让原本在glibc-2.27glibc-2.28肆虐的tcache double free攻击很难实施,但是鉴于tcache的特性,tcache的利用还是要比其他的bins方便很多。

chunk extend

chunk extend也是很好用的攻击方式之一,但是在glibc-2.29中增加了新的检查,这将会增大chunk extend的难度。

来自源码:glibc-2.29/malloc/malloc.c:4334

/* consolidate backward */
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);
}

glibc-2.29中新增了prevsize的检查机制,在合并的时候会判断prev_size和要合并chunksize是否相同。

malloc

unsorted bin

原本这里仅仅有size检查。

来自源码:glibc-2.29/malloc/malloc.c:3742

bck = victim->bk;
size = chunksize (victim);
mchunkptr next = chunk_at_offset (victim, size);

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)");

然后在glibc-2.29中,增加了如下检查:

  1. 对下一个相邻chunk的size检查
  2. 对下一个相邻chunk的prev_size进行检查
  3. 检查unsorted bin双向链表的完整性,对unsorted bin attack可以说是很致命的检查
  4. 对下一个chunk的prev_inuse位进行检查

这么多的检查,将使得unsorted bin更难利用。

下面这个检查早在glibc-2.28就有了,这里提一下,主要是防护unsorted bin attack

来自源码:glibc-2.29/malloc/malloc.c:3976

/* remove from unsorted list */
if (__glibc_unlikely (bck->fd != victim))
  malloc_printerr ("malloc(): corrupted unsorted chunks 3");
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);

top chunk

对于top chunk增加了size检查,遏制了House of Force攻击。

来自源码:glibc-2.29/malloc/malloc.c:4111

victim = av->top;
size = chunksize (victim);

if (__glibc_unlikely (size > av->system_mem))
  malloc_printerr ("malloc(): corrupted top size");