Post

[glibc 2.33] ptmalloc2 분석(7)

개요

이전 포스터에 이어서 glibc 2.33 버전에서 패치된 사항을 분석하고자 합니다. Memory Tagging이 생기면서 많은 부분에서 수정이 이루어져서 포스터를 작성합니다. 앞선 포스터를 먼저 보시는 것을 추천드립니다!

Memory Tagging

malloc/malloc.c 소스코드에 다음과 같은 로직이 추가된 것을 확인할 수 있습니다. 만약, Memory Tagging을 사용한다면 사용되는 전역 변수와 매크로 함수가 추가된것을 확인할 수 있습니다.

💡 아래와 같은 로직이 추가되었지만, 최종적으로 실행되는 함수인 __default_tag_region, __default_tag_nop 함수에 별다른 구현이 없는 것을 확인할 수 있습니다.

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
#ifdef USE_MTAG

/* Default implementaions when memory tagging is supported, but disabled.  */
static void *
__default_tag_region (void *ptr, size_t size)
{
  return ptr;
}

static void *
__default_tag_nop (void *ptr)
{
  return ptr;
}

static int __mtag_mmap_flags = 0;
static size_t __mtag_granule_mask = ~(size_t)0;

static void *(*__tag_new_memset)(void *, int, size_t) = memset;
static void *(*__tag_region)(void *, size_t) = __default_tag_region;
static void *(*__tag_new_usable)(void *) = __default_tag_nop;
static void *(*__tag_at)(void *) = __default_tag_nop;

# define TAG_NEW_MEMSET(ptr, val, size) __tag_new_memset (ptr, val, size)
# define TAG_REGION(ptr, size) __tag_region (ptr, size)
# define TAG_NEW_USABLE(ptr) __tag_new_usable (ptr)
# define TAG_AT(ptr) __tag_at (ptr)

여기서 Memory Tagging에 대한 설명은 아래와 같습니다.

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/* Memory tagging.  */

/* Some systems support the concept of tagging (sometimes known as
   coloring) memory locations on a fine grained basis.  Each memory
   location is given a color (normally allocated randomly) and
   pointers are also colored.  When the pointer is dereferenced, the
   pointer's color is checked against the memory's color and if they
   differ the access is faulted (sometimes lazily).

   We use this in glibc by maintaining a single color for the malloc
   data structures that are interleaved with the user data and then
   assigning separate colors for each block allocation handed out.  In
   this way simple buffer overruns will be rapidly detected.  When
   memory is freed, the memory is recolored back to the glibc default
   so that simple use-after-free errors can also be detected.

   If memory is reallocated the buffer is recolored even if the
   address remains the same.  This has a performance impact, but
   guarantees that the old pointer cannot mistakenly be reused (code
   that compares old against new will see a mismatch and will then
   need to behave as though realloc moved the data to a new location).

   Internal API for memory tagging support.

   The aim is to keep the code for memory tagging support as close to
   the normal APIs in glibc as possible, so that if tagging is not
   enabled in the library, or is disabled at runtime then standard
   operations can continue to be used.  Support macros are used to do
   this:

   void *TAG_NEW_MEMSET (void *ptr, int, val, size_t size)

   Has the same interface as memset(), but additionally allocates a
   new tag, colors the memory with that tag and returns a pointer that
   is correctly colored for that location.  The non-tagging version
   will simply call memset.

   void *TAG_REGION (void *ptr, size_t size)

   Color the region of memory pointed to by PTR and size SIZE with
   the color of PTR.  Returns the original pointer.

   void *TAG_NEW_USABLE (void *ptr)

   Allocate a new random color and use it to color the user region of
   a chunk; this may include data from the subsequent chunk's header
   if tagging is sufficiently fine grained.  Returns PTR suitably
   recolored for accessing the memory there.

   void *TAG_AT (void *ptr)

   Read the current color of the memory at the address pointed to by
   PTR (ignoring it's current color) and return PTR recolored to that
   color.  PTR must be valid address in all other respects.  When
   tagging is not enabled, it simply returns the original pointer.
*/

주석에 대한 설명은 아래와 같습니다.

일부 시스템은 메모리 위치를 세부적으로 태그하는 개념을 지원합니다. 각 메모리 위치에는 색상(보통 무작위로 할당)이 부여되고 포인터도 색상이 지정됩니다. 포인터가 참조될 때 포인터의 색상이 메모리의 색상과 다른지 확인하고, 다르면 접근이 실패합니다.

따라서 아래와 같이 __libc_malloc 함수 로직 중 tcache에서 할당하는 로직에 Memory Tagging이 추가되었습니다.

1
2
3
4
5
6
7
8
9
DIAG_PUSH_NEEDS_COMMENT;
if (tc_idx < mp_.tcache_bins
    && tcache
    && tcache->counts[tc_idx] > 0)
  {
    victim = tcache_get (tc_idx);
    return TAG_NEW_USABLE (victim);
  }
DIAG_POP_NEEDS_COMMENT;

tcache 검증 추가

_int_free 함수에서 아래와 같은 검증이 추가된 것을 확인할 수 있습니다.

1
2
if (cnt >= mp_.tcache_count)
  malloc_printerr ("free(): too many chunks detected in tcache");

만약, tcache_count보다 cnt 변수의 값이 크거나 같으면 조건문을 만족시켜 예외처리 되는 것을 확인할 수 있습니다.

결론

glibc 2.33 버전에서 많은 부분이 변경되지 않았지만, tcache chunk 개수에 대한 검증이 추가되었습니다. 또한, 향후 사용하기 위해 Memory Tagging 코드도 삽입된 것을 확인할 수 있습니다.

This post is licensed under CC BY 4.0 by the author.