Daily Heap #9

juuun0·2022년 3월 3일
1

Heap-A-to-Z

목록 보기
9/10
post-thumbnail

Overview

이번 글은 약간의 변명을 추가하자면 일을 시작하게 되며 백수일 때와 다르게 개인 공부와 블로그를 정리할 시간이 줄어들어 업로드 주기가 길어지게 되었습니다. 어쩌다보니 Monthly Heap이 되는 건 아닐까 하는 걱정과 함께 서론은 줄이겠습니다.


malloc() in glibc 2.23

_int_malloc()

Largebin Size

만약 request size를 만족하는 smallbin chunk가 존재하지 않을 경우 그 다음으로 largebin에서 적합한 chunk가 존재하는지 검색합니다. 구현 코드는 아래와 같습니다.

3436   /*
3437      If this is a large request, consolidate fastbins before continuing.
3438      While it might look excessive to kill all fastbins before
3439      even seeing if there is space available, this avoids
3440      fragmentation problems normally associated with fastbins.
3441      Also, in practice, programs tend to have runs of either small or
3442      large requests, but less often mixtures, so consolidation is not
3443      invoked all that often in most programs. And the programs that
3444      it is called frequently in otherwise tend to fragment.
3445    */
3446 
3447   else
3448     {
3449       idx = largebin_index (nb);
3450       if (have_fastchunks (av))
3451         malloc_consolidate (av);
3452     }

언뜻 보기엔 중요한 내용이 없는 것처럼 보일지 몰라도 3줄의 코드 중 2줄의 코드가 매우 중요한 역할을 합니다.

largebin_index()

먼저 largebin_index() 매크로는 다음과 같이 정의되어 있습니다.

1510 #define largebin_index(sz) \
1511   (SIZE_SZ == 8 ? largebin_index_64 (sz)                                     \
1512    : MALLOC_ALIGNMENT == 16 ? largebin_index_32_big (sz)                     \
1513    : largebin_index_32 (sz))

'SIZE_SZ' 값이 8인지 확인하여 맞을 경우 현재 architecture를 x64로 판단하여 largebin_index_64() 매크로를 호출합니다. 그렇지 않을 경우 'MALLOC_ALIGNMENT' 값이 16인지 확인하여 일치할 경우 largebin_index_32_big() 을, 틀릴 경우 largebin_index_32() 매크로를 호출합니다.

'MALLOC_ALIGNMENT' 값은 SIZE_SZ * 2 값으로 정의되는데 일반적인 32bit 환경은 'SIZE_SZ' 값으로 4를 가지기 때문에 무엇을 위해 존재하는 함수인지는 모르겠습니다.

malloc_consolidate()

먼저 어떤 이유로 fastbin chunks가 존재할 때 malloc_consolidate() 함수를 호출하는지에 대해 설명하기 이전에 malloc_consolidate() 함수가 무슨 역할을 하는지 알아보았습니다.

smallbin과 largebin size의 chunk가 해제될 때 인접한 영역에 다른 해제된 영역이 존재할 경우 해당 영역과 병합을 시도합니다. 여기서 인접한 영역이란 물리적으로 인접한 영역을 의미합니다. 병합을 하는 이유는 메모리 공간을 효율적으로 재사용하기 위함입니다.

have_fastchunks()

그러나 fastbin은 기본적으로 병합을 진행하지 않습니다. 이름에서 알 수 있듯이 빠른 할당과 해제를 위해 사용되기 때문에 병합 등의 과정을 진행하지 않습니다. 그렇다면 왜 largebin size의 할당을 진행할 때 fastbin에 존재하는 chunk에 대해 병합을 진행할까요? 이를 이해하기 위해서는 heap의 철학을 이해할 필요가 있었습니다.

일반적으로 heap 공간을 활용할 때 largebin size를 요청할 경우 빠른 시간 내에 fastbin size, smallbin size에 대한 요청이 없을 것으로 판단합니다.

사실 개발을 직접적으로 배운 것이 아닌 저에게 이와 같은 철학은 다소 난해한 개념이었습니다. Heap exploit을 학습하기 위한 binary는 fastbin, smallbin, largebin, unsorted bin 모든 bin을 넘나들며 취약점을 사용하였기 때문입니다.

위와 같은 철학이 반영되어 있는 이유도 당연히 메모리 관리의 효율성을 위해서입니다. 일반적인 프로그램의 경우 요청되는 사이즈가 일정할 것이라고 판단하여 불필요한 공간이 생기지 않도록 하기 위함이죠.

따라서 현재 arena에 fastbin chunks가 존재할 경우 이를 병합하여 smallbin size로 변환한 뒤 largebin size의 할당을 진행합니다.

profile
To be

0개의 댓글