目录
  1. 1. _GI___libc_free
  2. 2. _int_free
  3. 3. 进入 fastbin
  4. 4. 进入 Unsorted bin
  5. 5. Check In Glbc
free源码简单分析

_GI___libc_free

首先是 _GI___libc_free

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
void __fastcall _GI___libc_free(void *ptr)
{
if ( _free_hook )
{
_free_hook(ptr, retaddr);
}
else if ( ptr )
{
v1 = (unsigned __int64)ptr - 16;
v2 = *((_QWORD *)ptr - 1);
if ( v2 & 2 ) // 判断size位,判断是不是 mmap 获得的 chunk
{
if ( !mp_.no_dyn_threshold
&& v2 > mp_.mmap_threshold
&& v2 <= 0x2000000
&& (v1 < (unsigned __int64)dumped_main_arena_start || v1 >= (unsigned __int64)dumped_main_arena_end) )
{
mp_.mmap_threshold = v2 & 0xFFFFFFFFFFFFFFF8LL;
mp_.trim_threshold = 2 * (v2 & 0xFFFFFFFFFFFFFFF8LL);
}
munmap_chunk((mchunkptr)((char *)ptr - 16));
}
else
{
av = &main_arena;
if ( v2 & 4 )
av = *(malloc_state **)(v1 & 0xFFFFFFFFFC000000LL);
int_free(av, (mchunkptr)v1, 0);
}
}
}

如果存在 free_hook , 就会直接调用 free_hook(ptr) 然后返回。否则判断被 free 的 内存是否是 mmap 获取的 ,如果是则使用 munmap_chunk 回收内存,否则进入 _int_free

_int_free

首先会做一些简单的检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 size = chunksize (p);

//检查指针是否正常,对齐
if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
|| __builtin_expect (misaligned_chunk (p), 0))
{
errstr = "free(): invalid pointer";
errout:
if (!have_lock && locked)
(void) mutex_unlock (&av->mutex);
malloc_printerr (check_action, errstr, chunk2mem (p), av);
return;
}

// 检查 size 是否 >= MINSIZE ,且是否对齐
if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))
{
errstr = "free(): invalid size";
goto errout;
}

// 检查 chunk 是否处于 inuse 状态
check_inuse_chunk(av, p);

检查

  • 指针是否对齐
  • 块的大小是否对齐,且大于最小的大小
  • 块是否在 inuse 状态

进入 fastbin

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

if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())) {
if (have_lock
|| ({ assert (locked == 0);
mutex_lock(&av->mutex);
locked = 1;
chunk_at_offset (p, size)->size <= 2 * SIZE_SZ // next->size <= 2 * SIZE_SZ
|| chunksize (chunk_at_offset (p, size)) >= av->system_mem; //
}))
{
errstr = "free(): invalid next size (fast)";
goto errout;
}

set_fastchunks(av);
unsigned int idx = fastbin_index(size);
fb = &fastbin (av, idx);

mchunkptr old = *fb, old2;
unsigned int old_idx = ~0u;
do
{

if (__builtin_expect (old == p, 0))
{
errstr = "double free or corruption (fasttop)";
goto errout;
}
if (have_lock && old != NULL)
old_idx = fastbin_index(chunksize(old));
p->fd = old2 = old; // 插入 fastbin
}
while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2)) != old2);

if (have_lock && old != NULL && __builtin_expect (old_idx != idx, 0))
{
errstr = "invalid fastbin entry (free)";
goto errout;
}
}

如果 size 满足 fastbin 的条件,则首先判断 next_chunk->size 要满足

1
2
next_chunk->size > 2 * SIZE_SZ
next_chunk->size < av->system_mem

接着就会找对相应的 fastbin ,然后插入 该 bin 的第一项。插入前有一个检查

1
2
3
4
5
if (__builtin_expect (old == p, 0))
{
errstr = "double free or corruption (fasttop)";
goto errout;
}

就是 p->size 索引到的 fastbin 的第一个指针不能和当前的 p 相同,否则会被认为是 double free

进入 Unsorted bin

如果被 free 的这个块不是 通过 mmap 获得的,就会进入下面的逻辑

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
 else if (!chunk_is_mmapped(p)) {
if (! have_lock) {
(void)mutex_lock(&av->mutex);
locked = 1;
}

// 得到下一个 chunk 的指针
nextchunk = chunk_at_offset(p, size);

// 不能 free top chunk
if (__glibc_unlikely (p == av->top))
{
errstr = "double free or corruption (top)";
goto errout;
}
// nextchunk 不能越界,就是限制了 p->size
if (__builtin_expect (contiguous (av)
&& (char *) nextchunk
>= ((char *) av->top + chunksize(av->top)), 0))
{
errstr = "double free or corruption (out)";
goto errout;
}
/*p 要被标识为 inuse 状态 */
if (__glibc_unlikely (!prev_inuse(nextchunk)))
{
errstr = "double free or corruption (!prev)";
goto errout;
}

nextsize = chunksize(nextchunk);
// nextsize 在 [ 2 * SIZE_SZ, av->system_mem] 之间
if (__builtin_expect (nextchunk->size <= 2 * SIZE_SZ, 0)
|| __builtin_expect (nextsize >= av->system_mem, 0))
{
errstr = "free(): invalid next size (normal)";
goto errout;
}

free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);

/* 如果 p的前一个块是 free 状态,就向前合并,通过 p->pre_inused 判断*/
if (!prev_inuse(p)) {
prevsize = p->prev_size;
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(av, p, bck, fwd);
}

if (nextchunk != av->top) {
// 获得 nextchunk 的下一个 chunk, 的 pre_inused位
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);

// 如果 nextchunk 也是 free 状态的,合并
if (!nextinuse) {
unlink(av, nextchunk, bck, fwd);
size += nextsize;
} else
clear_inuse_bit_at_offset(nextchunk, 0);

// 合并的结果放置到 unsorted bin
bck = unsorted_chunks(av);
fwd = bck->fd;

// 防止 unsortedbin 被破坏
if (__glibc_unlikely (fwd->bk != bck))
{
errstr = "free(): corrupted unsorted chunks";
goto errout;
}
p->fd = fwd;
p->bk = bck;
if (!in_smallbin_range(size))
{
p->fd_nextsize = NULL;
p->bk_nextsize = NULL;
}
bck->fd = p;
fwd->bk = p;

set_head(p, size | PREV_INUSE);
set_foot(p, size);

check_free_chunk(av, p);
}

else {
size += nextsize;
set_head(p, size | PREV_INUSE);
av->top = p;
check_chunk(av, p);
}


// 如果 free 得到的 unsorted bin 的 size(包括合并chunk 得到的) 大于等于 FASTBIN_CONSOLIDATION_THRESHOLD 就会触发 malloc_consolidate
if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD) {
if (have_fastchunks(av))
malloc_consolidate(av);

if (av == &main_arena) {
#ifndef MORECORE_CANNOT_TRIM
if ((unsigned long)(chunksize(av->top)) >=
(unsigned long)(mp_.trim_threshold))
systrim(mp_.top_pad, av);
#endif
} else {
/* Always try heap_trim(), even if the top chunk is not
large, because the corresponding heap might go away. */
heap_info *heap = heap_for_ptr(top(av));

assert(heap->ar_ptr == av);
heap_trim(heap, mp_.top_pad);
}
}

if (! have_lock) {
assert (locked);
(void)mutex_unlock(&av->mutex);
}
}
/*
If the chunk was allocated via mmap, release via munmap().
*/

大概流程

  • 首先做了一些检查, p != top_chunk, p->size 不能越界, 限制了 next_chunk->size, p要处于 inuse状态(通过 next_chunk->pre_inused 判断)
  • 接着判断 p 的前后相邻块是不是 free 状态,如果是就合并
  • 根据此次拿到的 unsorted bin 的 大小,如果 size>=FASTBIN_CONSOLIDATION_THRESHOLD 就会触发 malloc_consolidate

如果 p 是通过 mmap 获得的,就通过

1
munmap_chunk (p);

释放掉他

Check In Glbc

来源 https://github.com/DhavalKapil/heap-exploitation

https://heap-exploitation.dhavalkapil.com/author.html

文章作者: nocbtm
文章链接: https://nocbtm.github.io/2020/02/26/free源码简单分析/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 nocbtm's Blog
打赏
  • 微信
  • 支付宝