실습 환경 : Ubuntu 16.04
실습 코드
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char* a=(char*)malloc(0x10);
char* b=(char*)malloc(0x20);
char* b2=(char*)malloc(0x25);
char* c=(char*)malloc(0x30);
char* d=(char*)malloc(0x40);
char* d2=(char*)malloc(0x45);
char* e=(char*)malloc(0x50);
char* f=(char*)malloc(0x60);
char* g=(char*)malloc(0x70);
free(a);
free(b);
free(b2);
free(c);
free(d);
free(d2);
free(e);
free(f);
free(g);
return 0;
}
총 10번의 malloc 호출을 하고 전부 다 free를 하는 단순한 코드입니다. fast bin이 관리하는 0x20 ~ 0x80 사이즈를 확인하기 위해 사이즈별로 동적할당을 진행했습니다. 또한 단일연결 리스트로 구성되는 걸 확인하기 위해 b2와 d2를 추가적으로 삽입했습니다.
맨 마지막 free(h)가 진행되기 직전 메모리 상태를 확인해보겠습니다.
fastbin[0] : 0x20
요청한 크기 0x10 + 헤더 0x10 이 합쳐저 총 0x20 bytes의 청크가 만들어지고고 0번 인덱스 binlist에 추가됐습니다.
fastbin[1] : 0x30
청크는 0x10 단위로 할당되므로 요청 사이즈 0x20과 0x25은 0x30 사이즈로 할당됩니다. 따라서 binlist의 0x30 사이즈인 0x30에 추가됩니다.
fastbin[2] : 0x40
요청크기 0x30 + 헤더 0x10 이 합쳐저 총 0x40사이즈 청크가 추가됩니다.
fastbin[3] : 0x50
d청크와 d2청크 모두 헤더포함 0x50 사이즈이므로 3번 인덱스에 추가됩니다.
fastbin[4] : 0x60
요청 크기 0x50 + 헤더 0x10이 합쳐저 총 0x60사이즈 청크가 추가됩니다.
fastbin[5] : 0x70
요청 크기 0x60 + 헤더 0x10이 합쳐저 총 0x70사이즈 청크가 추가됩니다.
fastbin[6] : 0x80
요청 크기 0x70 + 헤더 0x10이 합쳐저 총 0x80사이즈 청크가 추가됩니다.
그 이유는 아래와 같습니다.
현재 fastbin에 아무것도 없는 상태에서, 만약 0x20사이즈의 청크가 사용중이라고 해보자. 그렇다면 mem 영역에 사용자가 입력한 데이터가 들어가 있을 것이다. 0x20사이즈 a청크가 free되면 fastbin[0]에 들어가게 될텐데, 여기서 fd가 0으로 초기화 되지 않는다면, fastbin[0]→a청크→a청크's 데이터 이렇게 리스트가 연결되어 다음에 동일한 사이즈인 0x20이 요청들어왔을때 청크를 반환하는 것이 아닌, a청크's 데이터값을 반환하려고 할것이다.
fastbins은 단일 연결리스트로 fd에 들어가있는 값을 보고 LIFO를 진행하기 때문입니다. 아래의 그림을 보면 쉽게 이해가 될 것입니다(그림에 표현된 값들은 상관없는 값입니다).
따라서 처음 free된 청크들의 fd를 0으로 만드는 것입니다. 어쨋든 이러한 이유로 코드에서 a, b, c, d, e, f, g 청크들의 fd 부분을 0으로 채워집니다. 직접 확인해보겠습니다.