Post

[glibc 2.29] ptmalloc2 분석(4)

개요

이전 포스터에 이어서 glibc 2.29 버전에서 패치된 사항을 분석하고자 합니다. 새로운 개념이 등장한 것은 아니지만 많은 부분에서 수정이 이루어져서 포스터를 작성합니다. 앞선 포스터를 먼저 보시는 것을 추천드립니다!

unsorted bin 검증

_int_malloc

__libc_malloc 함수에서 패치가 없기 때문에 _int_malloc 함수만 설명드리겠습니다. 우선 해당 함수에서 unsorted_bin에서 할당 받는 부분에 검증이 추가되었습니다. 해당 내용은 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)");

위의 조건문을 분석하면 다음과 같은 검증이 생겼습니다.

  1. chunk의 크기가 2*SIZE_SZ보다 작거나 같고 시스템 메모리보다 큰 경우(기존에 있던 검증)
  2. 할당받으려는 chunk의 다음 chunk의 크기가 2*SIZE_SZ보다 작거나 같고 시스템 메모리보다 큰 경우(기존에 있던 검증)
  3. 할당받으려는 chunk의 크기가 다음 chunk의 prev_size와 다른 경우
  4. 할당받으려는 chunk의 이전 chunk 멤버중 bk가 현재 chunk인 경우(Double Free Bug)
  5. 할당받으려는 chunk의 다음 chunk 멤버중 prev_inuse가 1인 경우

arena top 검증

_int_malloc

또한, arenatop에서 할당 받을 경우 아래와 같은 검증이 추가되었습니다.

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

tcache 검증

_int_free

__libc_free 함수 또한 패치가 없기 때문에 바로 _int_free 함수를 분석하겠습니다. 우선 아래의 검증이 추가되었습니다.

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
28
if (tcache != NULL && tc_idx < mp_.tcache_bins)
{
	/* Check to see if it's already in the tcache.  */
	tcache_entry *e = (tcache_entry *) chunk2mem (p);

	/* 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.  */
	  }

	if (tcache->counts[tc_idx] < mp_.tcache_count)
	  {
	    tcache_put (p, tc_idx);
	    return;
	  }
}

위의 로직을 분석해보면 tcachefree하는 과정에서 기존에 없던 double free bug를 검증하는 것을 확인할 수 있습니다.

tcache 함수

추가적으로 tcache에서 패치가 있었습니다.

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

  /* Mark this chunk as "in the tcache" so the test in _int_free will
     detect a double free.  */
  e->key = tcache;

  e->next = tcache->entries[tc_idx];
  tcache->entries[tc_idx] = e;
  ++(tcache->counts[tc_idx]);
}

/* Caller must ensure that we know tc_idx is valid and there's
   available chunks to remove.  */
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]);
  e->key = NULL;
  return (void *) e;
}

위와 같이 tcacheput하고 get하는 과정에서 key라는 멤버가 추가된 것을 확인할 수 있습니다. 이를 토대로 Double Free Bug를 검증하게 됩니다.

fastbin 검증

_int_free

1
2
if (__glibc_unlikely (chunksize(p) != prevsize))
  malloc_printerr ("corrupted size vs. prev_size while consolidating");

fastbin 크기가 아니고 mmap으로 할당된 chunk가 아닌 chunk를 해제하는 과정에서 현재 chunk의 크기와 다음 chunk 멤버 중 prev_size의 크기가 일치하는지 확인하는 검증이 추가되었습니다.

결론

glibc 2.29에서 주요하게 변경된 부분은 다음과 같습니다.

  • unsorted_bin 검증 추가
  • tcache Double Free Bug 검증 추가(tcache→key 추가)
This post is licensed under CC BY 4.0 by the author.