카테고리 없음

드림핵 stage12 ptmalloc2

O'bin 2022. 7. 31. 23:58

운영체제의 핵심 역할 중 하나는 한정된 메모리 자원을 각 프로세스에 효율적으로 배분하는 일입니다. 모든 프로세스는 실행 중에 메모리를 동적으로 할당하고, 할당한 메모리의 쓰임이 다하면 이를 해제합니다. 이 과정은 매우 빈번하게 일어납니다. 그래서 운영체제의는 이 동작이 빠르고, 메모리의 낭비 없이 이뤄지도록 특수한 알고리즘으로 구현됩니다. 

 

 Memory Allocator

프로세스 실행 중 메모리 동적 할당, 사용 후 할당 해제 과정을 빠르고 메모리 낭비 없이 이루어지도록 특수 알고리즘으로 구현되는 것

구현에 사용된 알고리즘에 따라 여러 종류 존재

리눅스는 ptmalloc2,

구글은 tcmalloc,

페이스북이나 파이어폭스는 jemalloc을 사용

 

 

ptmalloc2동적 메모리 관리하는 리눅스 핵심 알고리즘메모리 해제 시 해제된 메모리 특징 기억, 비슷한 메모리의 할당 요청 발생 시 빠르게 반환=> 메모리 할당 속도 향상, 불필요 메모리 사용 방지

 

ptmalloc2가 구현된 GLibc의 버전에 따라 적용할 수 있는 공격 기법 차이 존재

리눅스에 설치된  GLibc 버전 확인 명령어 : /lib/x86_64-linux-gnu/libc-2.27.so

Ubuntu GLIBC 2.27-3ubuntu1.4 기준 취약점, 공격 기법 공부

리눅스에 설치된 GLibc 버전 확인

 

 

 

ㄹㅇ

ptmalloc2

ptmalloc2(pthread malloc 2)는 Wolfram Gloger가 개발한 Memory Allocator로, Doug Lea의 dlmalloc을 개선한 ptmalloc의 두 번째 버전

구현 목표 : 메모리의 효율적인 관리 = 메모리 낭비 방지 + 빠른 메모리 재사용 + 메모리 단편화 방지

 

메모리 단편화 방지

내부 단편화 : 할당한 메모리 공간의 크기에 비해 실제 데이터가 점유하는 공간이 적을 때 발생

외부 단편화 : 할당한 메모리 공간들 '사이'에 공간이 많아서 발생하는 비효율

단편화 심화 => 메모리 사용 효율 감소

 

단편화 감소 위한 ptmalloc의 방법 정렬(Alignment) 병합(Coalescence) 그리고 분할(Split)

정렬 : 64bit 환경에서 ptmalloc은 16byte 단위 메모리 할당 => 외부 단편화 감소 효과, 16byte이내 내부 단편화 발생 가능

정확히 같은 크기의 할당 요청보다 비슷한 크기의 할당 요청 발생 확률이 높음 -> 비슷한 크기 요청에 대해 모두 같은 크기의 공간 반환 => 해제된 청크 재사용률 증가, 외부 단편화 감소

병합과 분할 : 요청에 따라 잘게 나뉜 영역 병합, 분할하여 재사용 => 해제된 공간 재사용률 상승, 외부 단편화 감소

 

ptmalloc의 객체

청크(Chunk), bin, tcache, arena

청크 : ptmalloc이 할당한 메모리 공간, 헤더(청크 관리 정보)+데이터(사용자 입력 데이터)로 구성

사용중인 청크(in-use) 헤더와 해제된 청크(freed) 헤더 구조 다름

이름 크기 의미
prev_size 8byte 인접한 직전 청크 크기. 청크 병합 시 직전 청크 찾는 데 사용
size 8byte 현재 청크 크기(헤더 크기 포함)
64bit 환경에서 사용중인 청크 헤더 크기 : 16byte
=> 사용자 요청 크기 + 16byte
flags 3bit size의 하위 3bit
allocated arena(A), mmap’d(M), prev-in-use(P)
prev-in-use 플래그는 직전 청크가 사용 중인지를 나타내므로, ptmalloc은 이 플래그를 참조하여 병합이 필요한지 판단할 수 있습니다. 


fd 8byte 연결 리스트에서 다음 청크 가리킴
해제된 청크에만 있음
bk 8byte 연결 리스트에서 이전 청크 가리킴
해제된 청크에만 있음

 

 

 

청크 구조

 

 

bin

사용 끝난 청크들 저장되는 객체

메모리 낭비 방지, 해제된 처크 빠르게 재사용할 수 있게 함

ptmalloc에 128개의 bin 정의되어 있음

 

 

smallbin

32byte~1024byte 크기 청크 보관

하나의 smallbin에는 같은 크기의 청크들만 보관

index가 증가하면 저장되는 청크들의 크기는 16바이트씩 커짐 -> smallbin[0]는 32바이트 크기의 청크를, smallbin[61]은 1008 바이트 크기의 청크를 보관

원형 이중 연결 리스트(circular doubly-linked list)

먼저 해제된 청크가 먼저 재할당(FIFO)

unlink : smallbin에 청크를 추가하거나 꺼낼 때 연결 고리를 끊는 과정이 필요

consolidation : 메모리상 인접한 두 청크 해제된 상태 + 이들이 smallbin에 들어있음 => 병합

 

fastbin

ptmalloc은 작은 청크들 효율적 할당, 해제 위해 특정 크기 이하 청크들은 fastbin에 저장

메모리 단편화보다 속도 우선

32byte~176byte 크기 청크 보관

16byte 단위로 10개의 fastbin 존재, 리눅스는 작은 크기부터 7개만 사용

=> 리눅스는 32byte~128byte 이하 청크들 fastbin에 저장

단일 연결 리스트(= unlink 과정 불필요)

속도 빠르나 파편화 심한 LIFO방식(나중에 해제된 청크가 먼저 재할당됨)

fastbin에 저장되는 청크들은 병합 x

 

largebin

1024byte 이상 크기 청크 보관

총 63개

재할당 요청 발생 시 ptmalloc은 best-fit 청크 꺼내 재할당 <- 속도 향상 위해 largebin 내 청크 크기 내림차순 정렬

이중연결리스트, 재할당 과정 unlink 과정 필요

연속된 largebin 청크들은 병합 대상

 

unsortedbin

미분류 청크 보관

fastbin에 들어가지 않는 모든 청크들은 해제되었을 때 크기를 구분하지 않고 unsortedbin에 보관

원형 이중 연결 리스트

내부적으로 정렬 x

 

smallbin 크기에 해당하는 청크를 할당 요청

ptmalloc은 fastbin / smallbin 탐색

unsortedbin을 탐색

 

largebin의 크기에 해당하는 청크는 unsortedbin을 먼저 탐색

 

 

 

탐색된 청크들은 크기에 따라 적절한 bin으로 분류됨

 

 

arena

fastbin, smallbin, largebin 등의 정보를 모두 담고 있는 객체

multi-thread 환경에서 ptmalloc은 레이스 컨디션을 막기 위해 arena에 접근할 때 arena에 을 적용

레이스 컨디션(Race Condition) : 어떤 공유 자원을 여러 쓰레드나 프로세스에서 접근할 때 발생하는 오동작

 

 

 

병목 현상 유발 방지 위해 최대 64개의 arena를 생성 가능

 

arena에 락이 걸려서 대기해야 하는 경우, 새로운 arena를 생성

그러나 생성 갯수 64개이므로 과도한 multi-thread 환경에서는 병목 현상 발생, tcache 도입

 

 

 

 

tcache

 thread local cache

각 쓰레드에 독립적으로 할당되는 캐시 저장소

thread 는 64개의 tcache 보유

GLibc 버전 2.26에서 도입

단일 연결리스트(LIFO)

하나의 tcache는 같은 크기의 청크들만 보관

리눅스는 각 tcache에 보관할 수 있는 청크의 갯수를 7개로 제한

내부 청크 병합 x

tcache에는 32 바이트 이상, 1040 바이트 이하의 크기를 갖는 청크들이 보관

청크가 보관될 tcache가 가득찼을 경우에는 적절한 bin으로 분류

 

ptmalloc은 레이스 컨디션을 고려하지 않고 이 캐시에 접근할 수 있음(각 스레드 고유 캐시이기 때문)

 arena의 bin에 접근하기 전에 tcache를 먼저 사용하므로 arena에서 발생할 수 있는 병목 현상을 완화

보안 검사가 많이 생략되어 있어서 공격자들에게 힙 익스플로잇의 좋은 도구로 활용됨