카테고리 없음

glibc 2.42 malloc.c code auditing

poiri3r 2026. 4. 17. 21:58

이번엔 2.42버전입니다.

2.39버전과 2.35버전은 크게 차이가 없었는데, 2.42버전에는 미리 알아보니 많이 바꼈다고 합니다.

2.39버전과의 차이점을 중점으로 찾아보겠습니다.

https://elixir.bootlin.com/glibc/glibc-2.42/source/malloc/malloc.c#L3961

 

malloc.c - malloc/malloc.c - Glibc source code glibc-2.42 - Bootlin Elixir Cross Referencer

 

elixir.bootlin.com

 

저번처럼 malloc.c부터 진행을 했고, 가장 먼저 눈에 띈 부분은

해당 부분입니다.

tcache_bins가 tcache_small_bins로 바겼는데, 안에 들어가도 크게 다른건 없는데

코드 위쪽에 이런게 있더라고요? 새로 추가된 것 같습니다

근데 참조하는데는 딱히 없네요.

 

그 외에도

tcache0>counts가 num_slots로 이름이 바꼈습니다.

검사로직에도 이게 새로 생겼는데, 이전에 fastbin에 다른 size 청크를 넣는 공격을 방지하기 위해,

방금 malloc 꺼낸 chunk가 그 chunk의 실제 size를 기반으로 계산한 tcache index와의 일치 여부를 탐색합니다.

이건 청크할당 과정이 아닌 fastbin -> tcache로 옮기는 과정에서 진행합니다.(tcache에 chunk가 없을 때 fastbin을 확인하고 fastbin에서 tcache를 채우는 과정)

 

여기서도

이 부분이 num_slot[tc_idx] != 0으로 바뀌었는데, 조금 더 자세히 찾아보니, 기존에는 tcache->count를 세면서 (++) tcache_count와 비교를 했었는데, 지금 num_slots는 현재 chunk 개수가 아닌 남은 chunk 개수를 세기 때문에, tcache_put과정이 끝나면

 로 남은 개수를 줄이게 됩니다.

큰 로직의 변화는 아니지만 어느정도의 성능 최적화 과정이고(tcache-<count[tc_idx] < mp_.tcache_count연산보다 !=0이 더 빠름), 또 각 tcache bin마다 chunk개수를 커스텀 가능하기에 생긴 변화입니다.

 

 

또 unsortedbin을 정리하는 코드에서도 바뀐점을 찾을 수 있었는데, 

large bin attack을 막는 검증 로직으로, bk_nextsize를 조작해서 partial corruption이 가능했던 부분을 fwd->fd->bk_nextsize->fd_nextsize검증을 통해서 fwd->fd와의 일치여부 검증으로 largebin의 정렬을 확인합니다

 

나머지 쭉 훑어봤을때는 크게 변경점들은 없고 이제 세부 함수들을 살펴봐야 할듯합니다.

\

 

일단 tcachue_put입니다.

기존 tcache_put이 tcache_put_n으로 바뀌었고, 저번 소스코드 오디팅에서 봤던대로, tcache가 단순 LIFO 방식이 아닌, 리스트 중간에도 값을 꺼낼 수 있도록 바꼈기 때문에, tcache에 넣는 로직에도 변화가 생겼습니다.

*ep가 평문 포인터일 때(암호화되기전)에는 기존 방식과 동일하지만,

이미 PROTECT_PTR일 경우, 다음노드를 복구해서 e->next에 넣고, 새 노드 e를 그 위치 기준으로 mangling해서 써줍니다.

 

free쪽 로직을 보면 살짝 어지러운데

 

일단 _int_free가 사라졌고, _int_free_chunk가 생겼습니다.

_int_free의 로직이 대부분 _int_free_chunk로 들어간건 맞는데, 일부 로직은 libc_free로 들어갔다고 합니다.

 

예전부터 궁금했던건데, libc_malloc과 _int_malloc이 둘 다 어떤건지 잘 모르겠어서 찾아봤습니다.

2.42버전 보니까 libc_malloc에 libc_malloc2도 추가가 되었더라고요.

 

일단 결론만 보면

libc_malloc은 _int_malloc 호출 전 전처리 기능을 하는 함수입니다.

저희가 malloc을 호출하면 먼저 libc_malloc이 호출되고, 여기서 전처리 과정을 거친 뒤 내부에서 libc_malloc2를 호출하고 그 안에서 _int_malloc을 호출하는 것입니다.

 

free에서는 기존 _int_free에 있던 내용들이 대부분 _int_free_chunk로 넘어간것은 맞으나 size검사나 overflow check등의 로직은 _libc_free로 넘어갔습니다.

2.39버전에는 size를 내부에서 변수로 선언을 했는데,

2.42에서는 size가 인자로 넘어오는걸 확인할 수 있었습니다.

2.39에 _int_free에 있던 size검사와 포인터 검사도 (사진속 2.35는 잘못표기함)

2.42에서는 libc_free로 넘어왔습니다.

 

마찬가지로 2.39에서는 int_free 내부에 있던 tcache 검ㅅ ㅏ로직도

2.42에서는 tcache_double_free_verify라는 함수로 따로 빠져나왔고 해당 함수도 libc_free에서 사용됩니다.

근데 제가 2.39버전 auditing할때 디테일을 놓쳤던 부분이 free할때 chunkㄹ르 이웃 free chunk와 병합하는 consolidate부분이 _int_free_merge_chunk라는 함수로 따로 빠졌었더라고요,

이것도 로직적인 변화는 없고 그냥 분업이 된 것 같습니다.

 

그리고 consolidate에도 변화가 생겼는데,

이거는 2.38에 생긴 로직으로, free 이후 추가 consolidate가ㅓ 필요한지 판단하는 함수입니다.

원래 있던 로직에 helper함수긴 합니다.

2.35버전에도 있었던 내용인데, free한 크기가 클 경우 consolidate가 발생하던 부분을 _int_free_maybe_consolidate가 담당합니다.

 

마찬가지로 2.39버전에 free 이후 청크를 bin이나 top에 넣는 로직도 _int_free_create_chunk도 헬퍼함수로 빠졌습니다.

요즈음에 컨디션이 안좋아서 그런가 집중을 잘 못한거같네요.

로직상 변한 부분은 크게 없는듯합니다

 

pwnable 관점에서 조금 주목해야되는 변화만 살펴보면, tcache입니다.

2.39에서는 리스트처럼 단순 pop기능에서 중간 값을 꺼내는 기능이 생겼고, 2.42에서는 중간에 값을 삽입하는 기능도 있습니다.

또 tcache가 small tcache와 large tcache로 바꼈는데, small tcache는 기존처럼 LIFO지만 large tcache는 size기준으로 정렬된리스트입니다. 중간 삽입 / 중간 제거 로직이 있습니다.

큰 청크도 캐싱으로 빠르게 reuse를 위한 동작인데, 사실 이정도면 그냥 작은 bins라고 봐도 될거같네요.

 

tcache관련 로직을 본 기억이 없어서 다시 봤는데,

libc_malloc에서 tc_idx < TCACHE_SMALL_BINS를 확인하고 tcache_get과 tcache_get_large를 호출하기 때문입니다.

libc_malloc은 안봤으니 모를수밖에. ..

마찬가지로 libc_free도 tcache_put과 tcache_put_large도 분리되어있습니다.

 

tcache_put_large와 tcache_get_large를 살펴보면

mangled는 아까 tcache_get_n에서 봤던 값으로, 중간 노드를 꺼내거나 넣을 때 safe linking때문에 REVEAL_PTR을 거쳐야하는데, 이 값을 false로 넣어둬서 large tcache의 경우 중간 노드에 값을 넣거나 꺼낼 준비를 해줍니다.

그 후, tcache_location_large를 통해, 요청한 size에 맞는 chunk를 찾아주고, 그 값을 tcache_get_n으로 꺼내오거나 tcache_put_n으로 넣어줍니다.

 

이제 large bin attack을 하려고 해도 tcache를 다 채우고 하는 로직이 필요하겠네요.

근데 어차피 largebin attack도 막혀서 굳이 large tcache를 건들 이유는 없을것같긴 합니다.

확실히 최신버전이라 그런가 빡빡하네요.

그래도 사용자 프로그래밍 이슈로 UAF/double free/overflow 등이 발생한다면 건들 여지가 있긴 합니다.

 

일단 오디팅할건 다 오디팅한것 같은데, 오락가락하면서 찾기도 했고, 시험기간이라 정신이 없어서 조금 결과는 아쉬운거 같네요.

로직도 크게 변한게 없는건지 제가 잘 못찾은건지 모르겠습니다.

 

어차피 포너블할때 계속 bootln에서 봐야되니 꾸준히 봐야겠네요.

부족한 글 읽어주셔서 감사합니다