우분투에서 사용하는 memory allocator는 ptmalloc2이지만 기본 알고리즘은 dlmalloc이니 dlmalloc을 기준으로 설명하겠다. (참고로 dlmalloc에서 멀티 쓰레드 기능이 추가된게 ptmalloc이다)
heap은 특정 크기(0x21000)의 메모리 영역을 미리 할당한 뒤 이 영역을 사용하는 방식으로, free를 해도 이 영역안에 남아있다.
#include <stdlib.h>
int main()
{
unsigned long *p1;
p1 = malloc(0x640);
free(p1);
}
최초의 malloc이 실행되기 전에는 heap영역이 설정되어있지 않다.
malloc이 실행 된 후에 0x21000크기의 heap 영역이 설정된 것을 볼 수 있다.
malloc이 실행되지 않았다고 heap 영역이 아예 없는것은 아니다. initial heap이 기본적으로 설정되어 있으며 사용함에 따라 heap segment를 늘려가는 방식이다. 이는 이후에 자세히 다루겠다.
chunk
chunk는 mem과 header로 나누어진다.
mem : malloc으로 할당받은 부분
header : prev_size + size
prev_size : 인접한 앞 쪽 chunk의 크기
size : 자신의 chunk의 크기
prev_size는 인접한 앞 쪽 chunk가 free되면 초기화가 된다.
참고로 chunk의 정렬은 mem의 크기가 정렬되고 정렬후에 header가 붙게 된다.
chunk size
chunk의 크기는 메모리 단편화 방지를 위해 x86 에서는 8바이트, x64 에서는 16바이트 단위로 정렬된다. 그래서 chunk의 size는 x86에서는하위 3비트, x64에서는 하위 4비트가 사용되지 않아서 하위 3비트는 chunk의 3가지 속성을 나타낸다.(그래서 size에서 하위 3비트는 실제 크기에 포함되지 않는다)
000 (1, 2, 3)
1) non_main_arena
thread arena에 속하면 1
thread arena에 속하지 않으면 0
2) is_mmapped
mmap으로 할당 되었으면 1
mmap으로 할당되지 않았으면 0
3) prev_inuse bit
인접한 이전 chunk가 할당되어있으면 1
인접한 이전 chunk가 해제되어있으면 0
arena란 heap영역을 전체를 관리하는 구조체이다. 이 부분에 대해서는 따로 정리하겠다.
mmap이란 그냥 큰 메모리 요청이 들어왔을 때 malloc대신 호출되는 함수라고 보면 된다.
우리는 일단 prev_inuse bit만 외워두자.
(참고로 첫 번째 chunk는 이전의 chunk가 없으니 prev_inuse bit가 필요없지만 이전 영역이 bss영역이니 그냥 prev_inuse bit를 1로 고정한다.)
chunk의 종류
//예시 코드
#include <stdio.h>
int main(){
char *p = malloc(0x30);
*p = 'a';
free(p);
return 0;
}
Allocated chunk
malloc()을 호출했을 때 heap 영역에 생기는 chunk이다.
malloc으로 0x30을 요청하니 size가 0x41로 되어있다. 이는 [요청한 크기인 0x30] + [header크기인 0x10] + [prev_inuse bit 1] 이다.
data로 0x61 즉 'a'가 들어간 것을 알 수 있다.
Freed chunk
말 그대로 free된 chunk 이다. 이때 free()를 했을 때 실제로 반환되는 것이 아니라 allocated chunk 구조에서 free chunk 구조로 변경이 되는 것이다. 변경되는 부분은 data가 있던 부분에 fd, bk가 생기게 된다. 이때 large bin 크기의 chunk이면 fd_nextsize, bk_nextsize가 추가로 생긴다. binning 에 대해서는 따로 정리하겠다.
원래 data였던 부분에 fd와 bk가 쓰여졌다. 원래 있던 data는 신경쓰지 않고 그대로 overwrite한다.
기본적으로 free 과정에서 data 부분을 조작하거나 지우지는 않는다. 하지만 data 부분의 첫 8바이트와 다음 8바이트는 fd와 bk로 overwrite되고 그 이후의 data는 남아있게 된다. fd와 bk는 arena에 있는 주소를 가리키게 된다. fd와 bk는 binning에서 자세하게 다루겠다.
만약 동일한 크기의 chunk가 할당될때는 freed chunk가 allocated chunk로 변환되어 재사용된다.
Top chunk
힙 영역에 가장 마지막에 위치한다. 새롭게 할당되면 top chunk에서 분리해서 반환하고 top chunk에서 인접한 chunk가 free되면 병합한다.
만약 heap에 충분한 공간이 없으면 top chunk를 확장하여 추가적인 메모리를 할당한다.
참고로 첫 번째 0x291크기의 청크는 tcache와 관련된 공간으로 tcache_perthread_struct구조체에 해당하며, tcache chunk를 관리하는 구조체라고 보면 된다. tcache가 무엇인지는 따로 정리하겠다.
<틀린 부분이 있다면 비난과 욕설을 해주세요>
'포너블 > Heap' 카테고리의 다른 글
malloc 소스코드 분석(libc 2.23) (0) | 2023.08.02 |
---|---|
heap의 개념-(4) (0) | 2023.07.23 |
heap의 개념-(3) (0) | 2023.07.10 |
Heap의 개념-(1) (1) | 2023.07.03 |
K0n9의 heap부터 시작하는 pwnable이야기 (4) | 2023.06.28 |