Heap unlink.
일단 unlink는 특정 chunk를 free할 때 이전에 free된 chunk들 중에서
인접한 chunk들의 prev_size flag를 보고, flag가 0으로 셋팅되어 있으면, 그 chunk와 병합을 진행한다.
chunk는 이전 글에서도 말했듯이 malloc된 메모리를 말한다.
#define unlink(P,BK,FD)
{
BK = P -> bk;
FD = P -> fd;
FD => bk = BK;
BK => fd = FD;
}
unlink는 매크로함수로 지정되어 있으며, P,BK,FD는
<=============== <=================
BK P FD
===============> ==================>
위의 화면처럼 P는 현재의 chunk, BK는 이전 chunk, FD는 다음의 chunk가 된다.
일반적으로 chunk의 구조는
낮은 주소 ===================> 높은 주소.
prev_size | 현재 size |user data
malloc를 하게 되면 성공 시 리턴값으로 할당된 영역의 주소를 주게 되는데, 실제 할당주소 +8을 주게 된다.(32bit 기준).
+8인 이유는 prev_size랑 현재 size가 각각 4byte씩 8byte이기 때문이다.
즉 실제 user data가 들어가는 영역은 시작 주소 +8이 된다.
cf) prev_size는 물리적으로 인접한 이전의 chunk가 free될 시에 기록되게 된다.
또한 prev_inuse flag은, 즉 현재의 size에는 prev_inuse라는 flag가 들어가게 된다.
만약 chunk가 현재 사용 중이면 1, free된 상태면 0이 들어간다. Ex)
#define PREV_INUSE 0x1
#define IS_MMAPED 0x2
#define NON_MAIN_ARENA 0x4
현재 size를 디버깅 해보면, 처음 할당했을 시, user data 크기 + 앞의 헤더 크기 +1이 되는데
즉 1이 현재 사용중이라는 flag를 나타낸다. ==> free 되면 flag가 0이 되어 정상적인 크기로 보인다.
동적 할당을 하게 되면, 할당한 크기보다 더 큰 크기로 heap에 할당이 된다. ==> 이게 arena.
추가로 들어오는 할당에 대하여 arena에 남겨진 공간을 이용해 할당을 한다.
또한 동적 할당을 해보면, Top chunk라는 게 존재하고, Top chunk는 arena의 제일 위쪽에 존재하는 chunk.
if) Chunk가 free되게 된다면...?
낮은 주소 ===============> 높은 주소
prev_size|현재 size|fd|bk|user data.
cf) fd와 bk는 동일한 bin내의 이전, 이후 chunk. 물리적으로 인접한 메모리의 chunk를 가리키는 것이 아니다.
free()를 통해 메모리를 해제해주면, chunk는 크기에 따라 bin이라는 구조에 들어가게 된다.
이후 추가적인 메모리 할당이 있을 경우, 새롭게 공간을 할당하기에 앞서, bin list를 검색해서
매칭되는 크기가 있으면, 그 영역을 재할당하게 된다 ==> UAF 버그가 발생한다.
bin에서 chunk들은 double-linked list라는 구조를 유지한다.
병합 과정이 진행되면,기존에 bin에 존재하던 chunk를 unlink하게 된다.