malloc.c Analysis
2019-09-30 / nonetype

본 문서에서 분석하는 malloc.c(glibc2.23) 소스 코드는 여기에서 다운로드 받을 수 있습니다.


목차

1. What’s Heap?

2. The Heap Chunk

3. Management of Chunk & Bin

4. Core Functions

5. Error Checks


1. What’s Heap?

간략하게 설명하면, 프로그래머가 가변적으로 사용 가능한 메모리 영역이다.
이는 프로그래머가 유저 입력(또는 외부 입력)에 따라 유연하게 메모리를 사용할 수 있도록 도와준다.

아래와 같이 #include <stdlib.h>를 선언함으로써 malloc()free()함수로 동적 메모리를 사용할 수 있게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[]) {
int* ptr = malloc(32);
printf("malloc(32): [%p]\n", ptr);
strcpy(ptr, "ABCD");
printf("value: [%s]\n", ptr);
free(ptr);
printf("free(ptr) success!\n");
return 0;
}

glibc에서는 아래와 같이 malloc()free() 함수를 설명한다.

malloc.c:525

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
/*
malloc(size_t n)
Returns a pointer to a newly allocated chunk of at least n bytes, or null
if no space is available. Additionally, on failure, errno is
set to ENOMEM on ANSI C systems.

If n is zero, malloc returns a minumum-sized chunk. (The minimum
size is 16 bytes on most 32bit systems, and 24 or 32 bytes on 64bit
systems.) On most systems, size_t is an unsigned type, so calls
with negative arguments are interpreted as requests for huge amounts
of space, which will often fail. The maximum supported value of n
differs across systems, but is in all cases less than the maximum
representable value of a size_t.
*/
void* __libc_malloc(size_t);
libc_hidden_proto (__libc_malloc)

/*
free(void* p)
Releases the chunk of memory pointed to by p, that had been previously
allocated using malloc or a related routine such as realloc.
It has no effect if p is null. It can have arbitrary (i.e., bad!)
effects if p has already been freed.

Unless disabled (using mallopt), freeing very large spaces will
when possible, automatically trigger operations that give
back unused memory to the system, thus reducing program footprint.
*/
void __libc_free(void*);
libc_hidden_proto (__libc_free)

malloc()free() 함수는 개발자와 OS 사이에 효율적인 heap memory 관리를 가능하게 해주지만, 개발자가 할당한 메모리의 사용이 끝나면 정확하게 free() 함수를 통해 메모리를 해제해 줘야 하고, 해제한 메모리의 포인터는 즉각 폐기해야 한다. 이는 추후에 설명할 Use After Free 취약점과 관련이 있다.


2. The Heap Chunk

청크는 Heap 메모리 관리를 위한 Heap 메모리 영역의 단위이다.
Heap 청크의 구조체는 아래와 같이 정의되어 있다.

malloc.c:1105

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
This struct declaration is misleading (but accurate and necessary).
It declares a "view" into memory allowing access to necessary
fields at known offsets from a given base. See explanation below.
*/

struct malloc_chunk {

INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */

struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;

/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};

실제 할당된 청크의 레이아웃은 AsciiFlow로 아래와 같이 나타낼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk, if allocated | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of chunk, in bytes |M|P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| User data starts here... .
. .
. (malloc_usable_size() bytes) .
. |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of chunk |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

위 레이아웃에서 프로그램에 반환되는 메모리 주소는 mem이지만, 그 이전에 INTERNAL_SIZE_T * 2크기의 헤더가 존재한다. (INTERNAL_SIZE_T는 내부적으로 size_t의 크기와 동일하게 연산된다.)

할당 해제된 청크의 레이아웃은 아래와 같이 나타낼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
'head:' | Size of chunk, in bytes |P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Forward pointer to next chunk in list |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Back pointer to previous chunk in list |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Unused space (may be 0 bytes long) .
. .
. |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
'foot:' | Size of chunk, in bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

청크가 해제되면 mem 위치에 fd를 설정하고, mem + sizeof(size_t) 위치에 bk를 설정하게 된다.
할당 해제된 청크는 이 fdbk를 통해 이중 연결 리스트로 관리된다.

또한 청크 헤더의 size위치에 M, P 등의 값이 붙어있는데, 이는 Heap 메모리가 할당될 때 연산 효율을 위해 할당 크기를 sizeof(size_t) * 2(32비트에서 8바이트)로 정렬하기 때문에, size 비트 하위 3비트(1, 2, 4)의 위치에 플래그를 설정하게 된다.
청크 플래그는 3가지가 존재하며, 아래와 같다.

P (PREV_INUSE)

이전 청크(연결 리스트의 이전 인덱스가 아닌, 물리적 메모리로 가장 인접한 청크)가 할당 해제된 상태일 때 0, 사용중일 때 1로 설정된다.

malloc.c:1269

1
2
3
4
5
/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */
#define PREV_INUSE 0x1

/* extract inuse bit of previous chunk */
#define prev_inuse(p) ((p)->size & PREV_INUSE)

M (IS_MMAPPED)

해당 청크가 mmap() 함수를 통해 할당되었는지 여부를 표시한다.
이 플래그가 설정되었을 경우, 다른 두가지의 플래그를 무시한다.

malloc.c:1276

1
2
3
4
5
/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */
#define IS_MMAPPED 0x2

/* check for mmap()'ed chunk */
#define chunk_is_mmapped(p) ((p)->size & IS_MMAPPED)

A (NON_MAIN_ARENA)

해당 청크가 main arena에 속해있을 경우 0으로 설정된다.
쓰레드가 생성될 때 마다 각각의 arena가 할당되며, 해당 arena에서 청크를 관리하게 되는데, 이때 1로 설정된다.

malloc.c:1283

1
2
3
4
5
6
7
/* size field is or'ed with NON_MAIN_ARENA if the chunk was obtained
from a non-main arena. This is only set immediately before handing
the chunk to the user, if necessary. */
#define NON_MAIN_ARENA 0x4

/* check for chunk from non-main arena */
#define chunk_non_main_arena(p) ((p)->size & NON_MAIN_ARENA)


3. Management of Chunk & Bin

Heap은 할당 해제된 청크들의 효율적인 관리를 위해 bin이라는 이름의 단일/이중 연결 리스트를 사용한다.
bin은 할당 해제된 청크의 크기에 따라 크게 4가지로 분류된다.

  1. Fast bin
  2. Unsorted bin
  3. Small bin
  4. Large bin

모든 bin은 쓰레드마다 할당되는 arena에 의해 관리되며, arena 구조체는 아래와 같이 정의되어 있다.

malloc.c:1686

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
struct malloc_state
{
/* Serialize access. */
mutex_t mutex;

/* Flags (formerly in max_fast). */
int flags;

/* Fastbins */
mfastbinptr fastbinsY[NFASTBINS];

/* Base of the topmost chunk -- not otherwise kept in a bin */
mchunkptr top;

/* The remainder from the most recent split of a small request */
mchunkptr last_remainder;

/* Normal bins packed as described above */
mchunkptr bins[NBINS * 2 - 2];

/* Bitmap of bins */
unsigned int binmap[BINMAPSIZE];

/* Linked list */
struct malloc_state *next;

/* Linked list for free arenas. Access to this field is serialized
by free_list_lock in arena.c. */
struct malloc_state *next_free;

/* Number of threads attached to this arena. 0 if the arena is on
the free list. Access to this field is serialized by
free_list_lock in arena.c. */
INTERNAL_SIZE_T attached_threads;

/* Memory allocated from the system in this arena. */
INTERNAL_SIZE_T system_mem;
INTERNAL_SIZE_T max_system_mem;
};

arena에서 Fast bin은 아래와 같이 선언되어 있다.

malloc.c:1694

1
2
/* Fastbins */
mfastbinptr fastbinsY[NFASTBINS];

Unsorted bin, Small bin, Large bin은 아래와 같이 선언되어 있다.

malloc.c:1703

1
2
/* Normal bins packed as described above */
mchunkptr bins[NBINS * 2 - 2];

3-1. Fast bin

16~88 Byte의 크기를 가지는 청크의 경우, Fast bin으로 관리된다.
Fast bin은 다른 bins와 다르게 단일 연결 리스트로 관리(fd 필드만 사용)되며, 후입선출(LIFO) 방식으로 관리된다.
Fast bin은 인접한 2개의 청크를 병합하지 않는다. 이로 인해 해제 속도가 빠르지만, 단편화(fragmentation) 가능성이 존재한다.

3-2. Unsorted bin

Small binLarge bin에 들어가야 할 크기의 청크가 해제되었을 때, 바로 Small/Large Bin에 들어가는 것이 아닌, Unsorted bin에 저장이 되어 최근 해제한 청크를 빠르게 재사용 가능(캐시 비슷한 역할)하게 만든다.

3-3. Small bin

16~504 Byte의 크기를 가지는 청크의 경우, Small bin으로 관리된다.
환형 이중 연결 리스트로 관리(fd, bk 모두 사용)되며, 선입선출(FIFO) 방식으로 관리된다.
해제되는 청크의 앞 뒤에 존재하는 해제된 청크와 병합을 진행하기 때문에 Fast bin보다는 느리지만, Large bin보다는 빠른 속도로 처리된다.

3-4. Large bin

크게 두 부분으로 나뉘는데, 첫번째 Large bin은 512-568바이트 크기의 청크를, 두번째 Large bin은 576-632바이트 크기의 청크를 포함한다.
환형 이중 연결 리스트로 관리되며, 사이즈 값에 따라 내림차순으로 정렬된다.
Small bin과 동일하게 할당 해제되었을 때 Unsorted bin 내에 임시적으로 저장되며, 인접한 할당 해제된 청크에 대해서 병합을 진행한다.

3-5. Top chunk

Arena의 최상단에 존재하는 청크이다.
동적 메모리 할당 요청이 들어오게 될 경우, 1차적으로 bins에서 재할당 가능한 청크가 존재하는지 검사하고, 재할당이 불가능하다면 Top chunk에서 요청한 크기만큼을 떼어 반환한다.
만약 요청 크기가 Top chunk의 크기보다 크다면 sbrk() 또는 mmap() 호출을 통해 확장하며 PREV_INUSE 플래그가 항상 활성화 되어 있다.

3-6. Last remainder chunk

사용자가 작은 크기의 청크를 할당 요청했으나 Small bin, Unsorted bin에 존재하는 할당 해제된 청크중 동일한 크기의 청크의 재할당이 불가능할 경우 요청 크기보다 큰 청크를 찾아, 요청 크기만큼 떼어 할당 후 반환한다. 이때 요청 크기만큼 떼고 남은 크기의 청크가 Last remainder chunk가 된다.


4. Core Functions

핵심 함수들은 다음과 같다.

4-1. void * __libc_malloc (size_t bytes)

  1. __malloc_hook이 설정되어 있다면, __malloc_hook 포인터를 호출한다.
  2. __malloc_hook이 설정되어 있지 않다면, arena_get() 함수 호출을 통해 mstate ar_ptr 포인터를 가져온다.
  3. ar_ptr, bytes를 인자로 _int_malloc()함수를 호출한다.
    1. 만약 ar_ptrNULL이 아니고, _int_malloc()함수의 반환값이 NULL이라면,
      • arena_get_retry() 호출을 통해 다른 Arena Pointer를 ar_ptr에 받아오고,
      • _int_malloc()함수를 새로운 ar_ptr을 인자로 다시 실행한다.
    2. 아니라면,
      • 해당 Arena에 대해 mutex_unlock()를 호출한다.
  4. 최종적으로 _int_malloc()의 반환값을 반환하기 전, 아래 사항들을 체크한다.
    • _int_malloc() 반환값(청크)이 NULL인지
    • 청크가 mmap()을 통해 할당되었는지
    • 해당 청크의 아레나가 2에서 찾은 청크인지
  5. 5의 내용 중 하나라도 True인 경우, 청크의 포인터를 반환한다.

4-2. static void * _int_malloc (mstate av, size_t bytes)

  1. 요청 크기가 범위를 벗어난 값인지 체크 후, size값을 정렬size_alignment한다.
  2. av의 값이 NULL인지 체크한다.
  3. 사용 가능한 Arena가 존재하지 않을 경우(av==NULL), sysmalloc()을 통해 mmap()을 호출한다. 만약 반환 값이 존재한다면, alloc_perturb()를 호출한 후 포인터 값을 반환한다.
    1. 만약 요청 크기가 Fast bin 범위 안이라면,
    2. 요청 크기와 동일한 크기의 Fast bin index를 받아 첫번째 청크를 victim으로 가져온다.
    3. 만약 victim이 NULL이라면, (2)로 넘어간다. (Small bin 탐색)
    4. 만약 NULL이 아니라면, victim의 사이즈가 해당 Fast bin의 사이즈가 맞는지 체크한다.
      • 만약 사이즈가 다르다면, malloc(): memory corruption (fast)를 출력하고, malloc_printerr()를 호출한다.
      • 만약 사이즈가 동일하다면, 해당 청크의 메모리 포인터를 가져온 후 alloc_perturb()를 호출한 뒤 메모리 포인터를 반환한다.
    1. 만약 요청 크기가 Small bin 범위 안이라면,
    2. 요청 크기와 동일한 크기의 Small bin index를 받아 청크를 victim으로 가져온다.
    3. 만약 victim0이라면, malloc_consolidate()를 호출한다.
    4. 아니라면, victim->bk->fd != victim 연산을 통해 victim 청크의 fdbk가 신뢰할 수 있는 값인지 체크한다.
      • 만약 victim->bk->fd != victim이라면, malloc(): smallbin double linked list corrupted를 출력한다.
    5. set_inuse_bit_at_offset() 호출을 통해 victim 청크의 뒤에 존재하는 청크의 PREV_INUSE flag를 변경한다.
    6. Small binfd bk를 재설정함으로써 victim 청크를 Small bin에서 제거한다.
    7. 현재 Arenamain_arena가 아닐 경우, NON_MAIN_ARENA flag를 설정한다.
    8. 해당 청크의 메모리 포인터를 가져온 후 alloc_perturb()를 호출한 뒤 메모리 포인터를 반환한다.
  4. 만약 요청 크기가 Large bin 범위라면,
    • 현재 ArenaFast bin 청크가 존재하는지 확인한 후, 존재한다면 malloc_consolidate()를 호출하여 청크를 병합한다. (Fast bin Chunk로 인한 단편화(fragmentation) 방지)
    1. Unsorted bin에서 청크를 victim으로 가져온다.
      2.victim의 크기가 최소 크기(2*SIZE_SZ)보다 작거나 최대 크기(av->system_mem)보다 큰지 체크한 후, 작거나 크다면 malloc(): memory corruption을 출력한다.
    2. 만약 요청 크기가 Small bin 범위이며(4.2에서 할당이 되었어야 하지만, bin내에 가용 청크가 없어서 할당되지 못한 경우), victimUnsorted bin 내의 유일한 청크이며, victimlast_remainder 청크이며, victim의 크기가 요청 크기보다 클 경우,
      • victim 청크를 할당된 청크Last remainder청크로 나눈 후 header,flag 설정 후 반환한다.
    3. 위의 4개의 조건이 하나라도 False일 경우, victimUnsorted bin에서 제거한다.
    4. 만약 victim의 크기가 요청 크기와 정확하게 일치할 경우,
      • PREV_INUSE, NON_MAIN_ARENA 등의 flag를 설정하고 alloc_perturb()를 호출한 뒤 해당 청크를 반환한다.
    5. 만약 요청 크기가 Small bin 범위라면,
      • 해당 크기의 bin을 가져와 bck로 설정하고, bck->fdfwd로 설정한다.
    6. 만약 요청 크기가 Large bin 범위라면,
      • size 필드에 flag bit를 붙이고, Large bin을 정렬한다.
        • 만약 victim이 마지막 청크(크기가 가장 작은)보다 작다면, 마지막 위치에 삽입한다.
        • 아니라면, victim size <= chunk size를 찾아 삽입한다. 만약 크기가 같으면 victimchunk 뒤에 삽입된다.
    7. 7.1~7의 내용을 최대 10000번 실행한다.

4-3. void __libc_free (void *mem)

  1. 만약, __free_hook이 설정되어 있다면, __free_hook을 호출한다.
  2. mem 값이 NULL인지 체크한다.
  3. 할당 해제하려는 청크가 mmap() 호출을 통해 할당된 청크라면 munmap_chunk()를 통해 할당 해제한다.
  4. 아니라면, 해당 청크에 대한 arena를 가져와 _int_free()를 호출한다.

4-4. static void _int_free (mstate av, mchunkptr p, int have_lock)

  1. p < p + chunksize(p)인지, p가 정렬된 포인터인지 체크한다.
    • 만약 아니라면, free(): invalid pointer를 출력한다.
  2. chunksize(p) < MINSIZE인지, 크기가 올바르게 정렬되었는지 체크한다.
    • 아니라면, free(): invalid size를 출력한다.
  3. 해당 청크의 크기가 Fast bin 범위인지 체크한다.
    1. 해당 청크 다음에 존재하는 청크의 크기가 최소 크기 2*SIZE_SZ보다 작거나, 최대 크기 av->system_mem보다 크다면 free(): invalid next size (fast)를 출력한다.
    2. 해당 청크에 대해 free_perturb()를 호출한다.
    3. mstateFASTCHUNKS_BIT를 설정한다.
    4. 해당 크기에 맞는 Fast bin index를 찾는다.
    5. 해당 Fast bin index의 최상단에 있는 청크가 해제하려는 청크인지 확인한 후, 맞다면 double free or corruption (fasttop)을 출력한다.
    6. 해당 index의 최상단에 있는 청크의 크기와 해제하려는 청크의 크기를 비교 후, 다르다면 invalid fastbin entry (free)를 출력한다.
    7. 해제하려는 청크를 Fast bin 최상단에 넣고, 끝낸다.
  4. 만약 해당 청크가 mmap()을 통해 할당된 청크가 아니라면,
    1. 만약 해당 청크가 top chunk라면, double free or corruption (top)을 출력한다.
    2. 만약 메모리상의 다음 청크가 해당 Arena의 영역을 벗어난다면, double free or corruption (out)을 출력한다.
    3. 만약 메모리상의 다음 청크의 PREV_INUSE flag가 설정되어 있지 않다면, double free or corruption (!prev)를 출력한다.
    4. 만약 다음 청크의 크기가 최소 크기(2*SIZE_SZ)보다 작거나 최대 크기(av->system_mem)보다 크다면, free(): invalid next size (normal)을 출력한다.
    5. 해당 청크에 대해 free_perturb()를 호출한다.
    6. 만약 해당 청크의 PREV_INUSE flag가 설정되어 있지 않다면, 이전 청크에 대해 unlink를 호출해 병합을 진행한다.
    7. 만약 다음 청크가 top Chunk가 아닐 경우,
      • 다음 청크가 사용중이지 않을 경우 unlink를 호출해 병합을 진행한다.
      • unsorted_chunk->fd->bk != unsorted_chunk인지 체크한 후, 같지 않다면 free(): corrupted unsorted chunks를 출력한다.
      • 청크를 Unsorted bin에 넣는다.
        8.만약 다음 청크가 Top Chunk라면 병합을 진행한다.
  5. 만약 해당 청크가 mmap()을 통해 할당된 청크라면, munmap_chunk()을 호출하여 할당 해제한다.
  1. chunk->fd->bk != chunk이거나, chunk->bk->fd != chunk일 경우, corrupted double-linked list를 출력한다.
  2. chunk->fd = chunk->bk, chunk->bk = chunk->fd를 수행한다. (이중 연결 리스트에서 해당 청크를 제거한다.)
  3. 청크의 크기가 Small bin 범위가 아니라면, chunk->fd_nextsize->bk_nextsize != chunk이거나 chunk->bk_nextsize->fd_nextsize != chunk일 경우, corrupted double-linked list (not small)을 출력한다.
  4. chunk->fd_nextsize, chunk->bk_nextsize에 대해 위와 동일하게 연결 해제한다.

5. Error Checks

5-1 checks in _int_malloc

Checks Error Message
fastbin_index (chunksize (victim)) != idx malloc(): memory corruption (fast)
victim->bk->fd != victim malloc(): smallbin double linked list corrupted
victim->size <= 2 * SIZE_SZ or victim->size > av->system_mem malloc(): memory corruption
unsorted->fd->bk != unsorted malloc(): corrupted unsorted chunks
unsorted->fd->bk != unsorted(in best-fit loop) malloc(): corrupted unsorted chunks 2

5-2 checks in _int_free

Checks Error Message
(uintptr_t) p > (uintptr_t) -size or misaligned_chunk (p) free(): invalid pointer
size < MINSIZE or !aligned_OK (size) free(): invalid size
next->size <= 2 * SIZE_SZ or next->size >= av->system_mem free(): invalid next size (fast)
top(fastbin) == p double free or corruption (fasttop)
old_idx != idx invalid fastbin entry (free)
p == av->top double free or corruption (top)
nextchunk > = ((char *) av->top + chunksize(av->top)) double free or corruption (out)
!prev_inuse(nextchunk) double free or corruption (!prev)
nextsize >= av->system_mem free(): invalid next size (normal)
fwd->bk != bck free(): corrupted unsorted chunks

Checks Error Message
FD->bk != P or BK->fd != P, 0 corrupted double-linked list
P->fd_nextsize->bk_nextsize != P or P->bk_nextsize->fd_nextsize != P corrupted double-linked list (not small)

마치며..

전에는 단순히 다른 사람이 정리해놓은 문서를 보고 필요한 만큼만 이해했었는데, 이 문서를 쓰면서 bin을 찾는 과정이나, bin에 넣는 과정 등등을 알아갈 수 있었다.

아쉬운 점은 세부적으로 분석은 많이 했는데 간단하게 표현을 하지 못한 점, 많은 부분(힙익스 페이로드를 놓고, 어떻게 동작하는지, 어떤 부분을 이용한건지 등등)을 써보고 싶었지만 팀프로젝트 기간이라 당장 리눅스 커널 익스를 해야하는 형국이라 나중에 시간이 나게 되면 추가적으로 문서화하도록 해야겠다.

리눅스 커널 익스하면서도 kmalloc, vmalloc 등 특이한 동적 메모리 관리 방법도 알아가는 중이라 틈틈히 추가하고 다듬는 과정을 통해서 더 정확하고, 유익한 정보가 있는 문서가 되었으면 좋겠다.


References

[Understanding glibc malloc 번역] https://tribal1012.tistory.com/78

[Heap 영역 정리] https://tribal1012.tistory.com/141

[Hacker’s Hut: Exploiting the heap] https://www.win.tue.nl/~aeb/linux/hh/hh-11.html

[glib malloc] https://umbum.tistory.com/386

size_alignment. sizeof(size_t) * 2(32비트에서 8바이트) 단위로 정렬된다.