malloc analysis note
본 문서에서 분석하는 malloc.c(glibc2.23) 소스 코드는 여기에서 다운로드 받을 수 있습니다.
목차
- 목차
- 1. What’s Heap?
- 2. The Heap Chunk
- 3. Management of Chunk & Bin
- 4. Core Functions
- 5. Error Checks
- 마치며..
- References
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
를 설정하게 된다. 할당 해제된 청크는 이 fd
와 bk
를 통해 이중 연결 리스트로 관리된다.
또한 청크 헤더의 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가지로 분류된다.
- Fast bin
- Unsorted bin
- Small bin
- 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 bin
과 Large 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-2. void * __libc_malloc (size_t bytes)
__malloc_hook
이 설정되어 있다면,__malloc_hook
포인터를 호출한다.__malloc_hook
이 설정되어 있지 않다면,arena_get()
함수 호출을 통해mstate ar_ptr
포인터를 가져온다.ar_ptr
,bytes
를 인자로_int_malloc()
함수를 호출한다.- 만약
ar_ptr
가NULL
이 아니고,_int_malloc()
함수의 반환값이NULL
이라면,arena_get_retry()
호출을 통해 다른 Arena Pointer를ar_ptr
에 받아오고,_int_malloc()
함수를 새로운ar_ptr
을 인자로 다시 실행한다.
- 아니라면,
- 해당 Arena에 대해
mutex_unlock()
를 호출한다.
- 해당 Arena에 대해
- 만약
- 최종적으로
_int_malloc()
의 반환값을 반환하기 전, 아래 사항들을 체크한다._int_malloc()
반환값(청크)이NULL
인지- 청크가
mmap()
을 통해 할당되었는지 - 해당 청크의 아레나가
2
에서 찾은 청크인지
5
의 내용 중 하나라도 True인 경우, 청크의 포인터를 반환한다.
4-1. static void * _int_malloc (mstate av, size_t bytes)
- 요청 크기가 범위를 벗어난 값인지 체크 후,
size
값을 정렬1한다. av
의 값이 NULL인지 체크한다.- 사용 가능한
Arena
가 존재하지 않을 경우(av
==NULL),sysmalloc()
을 통해mmap()
을 호출한다. 만약 반환 값이 존재한다면,alloc_perturb()
를 호출한 후 포인터 값을 반환한다. - 만약 요청 크기가
Fast bin
범위 안이라면, - 요청 크기와 동일한 크기의
Fast bin
index를 받아 첫번째 청크를victim
으로 가져온다. - 만약
victim
이 NULL이라면, (2)로 넘어간다. (Small bin
탐색) - 만약 NULL이 아니라면,
victim
의 사이즈가 해당Fast bin
의 사이즈가 맞는지 체크한다.- 만약 사이즈가 다르다면,
malloc(): memory corruption (fast)
를 출력하고,malloc_printerr()
를 호출한다. - 만약 사이즈가 동일하다면, 해당 청크의 메모리 포인터를 가져온 후
alloc_perturb()
를 호출한 뒤 메모리 포인터를 반환한다.
- 만약 사이즈가 다르다면,
- 만약 요청 크기가
- 만약 요청 크기가
Small bin
범위 안이라면, - 요청 크기와 동일한 크기의
Small bin
index를 받아 청크를victim
으로 가져온다. - 만약
victim
이0
이라면,malloc_consolidate()
를 호출한다. - 아니라면,
victim->bk->fd != victim
연산을 통해victim
청크의fd
와bk
가 신뢰할 수 있는 값인지 체크한다.- 만약
victim->bk->fd != victim
이라면,malloc(): smallbin double linked list corrupted
를 출력한다.
- 만약
set_inuse_bit_at_offset()
호출을 통해victim
청크의 뒤에 존재하는 청크의PREV_INUSE
flag를 변경한다.Small bin
의fd
bk
를 재설정함으로써victim
청크를Small bin
에서 제거한다.- 현재
Arena
가main_arena
가 아닐 경우,NON_MAIN_ARENA
flag를 설정한다. - 해당 청크의 메모리 포인터를 가져온 후
alloc_perturb()
를 호출한 뒤 메모리 포인터를 반환한다.
- 만약 요청 크기가
- 만약 요청 크기가
Large bin
범위라면,- 현재
Arena
에Fast bin
청크가 존재하는지 확인한 후, 존재한다면malloc_consolidate()
를 호출하여 청크를 병합한다. (Fast bin
Chunk로 인한 단편화(fragmentation) 방지)
- 현재
Unsorted bin
에서 청크를victim
으로 가져온다. 2.victim
의 크기가 최소 크기(2*SIZE_SZ
)보다 작거나 최대 크기(av->system_mem
)보다 큰지 체크한 후, 작거나 크다면malloc(): memory corruption
을 출력한다.- 만약 요청 크기가
Small bin
범위이며(4.2
에서 할당이 되었어야 하지만,bin
내에 가용 청크가 없어서 할당되지 못한 경우),victim
이Unsorted bin
내의 유일한 청크이며,victim
이last_remainder
청크이며,victim
의 크기가 요청 크기보다 클 경우,victim
청크를 할당된 청크와 Last remainder청크로 나눈 후 header,flag 설정 후 반환한다.
- 위의 4개의 조건이 하나라도
False
일 경우,victim
을Unsorted bin
에서 제거한다. - 만약
victim
의 크기가 요청 크기와 정확하게 일치할 경우,PREV_INUSE
,NON_MAIN_ARENA
등의 flag를 설정하고alloc_perturb()
를 호출한 뒤 해당 청크를 반환한다.
- 만약 요청 크기가
Small bin
범위라면,- 해당 크기의 bin을 가져와
bck
로 설정하고,bck->fd
를fwd
로 설정한다.
- 해당 크기의 bin을 가져와
- 만약 요청 크기가
Large bin
범위라면,size
필드에 flag bit를 붙이고,Large bin
을 정렬한다.- 만약
victim
이 마지막 청크(크기가 가장 작은)보다 작다면, 마지막 위치에 삽입한다. - 아니라면,
victim size
<=chunk size
를 찾아 삽입한다. 만약 크기가 같으면victim
은chunk
뒤에 삽입된다.
- 만약
7.1~7
의 내용을 최대 10000번 실행한다.
4-3. void __libc_free (void *mem)
- 만약,
__free_hook
이 설정되어 있다면,__free_hook
을 호출한다. mem
값이 NULL인지 체크한다.- 할당 해제하려는 청크가
mmap()
호출을 통해 할당된 청크라면munmap_chunk()
를 통해 할당 해제한다. - 아니라면, 해당 청크에 대한
arena
를 가져와_int_free()
를 호출한다.
4-4. static void _int_free (mstate av, mchunkptr p, int have_lock)
p < p + chunksize(p)
인지,p
가 정렬된 포인터인지 체크한다.- 만약 아니라면,
free(): invalid pointer
를 출력한다.
- 만약 아니라면,
chunksize(p) < MINSIZE
인지, 크기가 올바르게 정렬되었는지 체크한다.- 아니라면,
free(): invalid size
를 출력한다.
- 아니라면,
- 해당 청크의 크기가
Fast bin
범위인지 체크한다.- 해당 청크 다음에 존재하는 청크의 크기가 최소 크기
2*SIZE_SZ
보다 작거나, 최대 크기av->system_mem
보다 크다면free(): invalid next size (fast)
를 출력한다. - 해당 청크에 대해
free_perturb()
를 호출한다. mstate
의FASTCHUNKS_BIT
를 설정한다.- 해당 크기에 맞는
Fast bin
index를 찾는다. - 해당
Fast bin
index의 최상단에 있는 청크가 해제하려는 청크인지 확인한 후, 맞다면double free or corruption (fasttop)
을 출력한다. - 해당 index의 최상단에 있는 청크의 크기와 해제하려는 청크의 크기를 비교 후, 다르다면
invalid fastbin entry (free)
를 출력한다. - 해제하려는 청크를
Fast bin
최상단에 넣고, 끝낸다.
- 해당 청크 다음에 존재하는 청크의 크기가 최소 크기
- 만약 해당 청크가
mmap()
을 통해 할당된 청크가 아니라면,- 만약 해당 청크가
top chunk
라면,double free or corruption (top)
을 출력한다. - 만약 메모리상의 다음 청크가 해당
Arena
의 영역을 벗어난다면,double free or corruption (out)
을 출력한다. - 만약 메모리상의 다음 청크의
PREV_INUSE
flag가 설정되어 있지 않다면,double free or corruption (!prev)
를 출력한다. - 만약 다음 청크의 크기가 최소 크기(
2*SIZE_SZ
)보다 작거나 최대 크기(av->system_mem
)보다 크다면,free(): invalid next size (normal)
을 출력한다. - 해당 청크에 대해
free_perturb()
를 호출한다. - 만약 해당 청크의
PREV_INUSE
flag가 설정되어 있지 않다면, 이전 청크에 대해unlink
를 호출해 병합을 진행한다. - 만약 다음 청크가
top Chunk
가 아닐 경우,- 다음 청크가 사용중이지 않을 경우
unlink
를 호출해 병합을 진행한다. unsorted_chunk->fd->bk != unsorted_chunk
인지 체크한 후, 같지 않다면free(): corrupted unsorted chunks
를 출력한다.- 청크를
Unsorted bin
에 넣는다. 8.만약 다음 청크가Top Chunk
라면 병합을 진행한다.
- 다음 청크가 사용중이지 않을 경우
- 만약 해당 청크가
- 만약 해당 청크가
mmap()
을 통해 할당된 청크라면,munmap_chunk()
을 호출하여 할당 해제한다.
4-5. unlink(AV, P, BK, FD)
chunk->fd->bk != chunk
이거나,chunk->bk->fd != chunk
일 경우,corrupted double-linked list
를 출력한다.chunk->fd = chunk->bk
,chunk->bk = chunk->fd
를 수행한다. (이중 연결 리스트에서 해당 청크를 제거한다.)- 청크의 크기가
Small bin
범위가 아니라면,chunk->fd_nextsize->bk_nextsize != chunk
이거나chunk->bk_nextsize->fd_nextsize != chunk
일 경우,corrupted double-linked list (not small)
을 출력한다. 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 |
5-3 checks in unlink
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
sizeof(size_t) * 2
(32비트에서 8바이트) 단위로 정렬된다. ↩