_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 ) { 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 & 0xFFFFFFFFFFFFFFF8 LL; mp_.trim_threshold = 2 * (v2 & 0xFFFFFFFFFFFFFFF8 LL); } munmap_chunk((mchunkptr)((char *)ptr - 16 )); } else { av = &main_arena; if ( v2 & 4 ) av = *(malloc_state **)(v1 & 0xFFFFFFFFFC000000 LL); 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 ; } if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size))) { errstr = "free(): invalid size" ; goto errout; } 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 || 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; } 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 ; } nextchunk = chunk_at_offset(p, size); if (__glibc_unlikely (p == av->top)) { errstr = "double free or corruption (top)" ; goto errout; } if (__builtin_expect (contiguous (av) && (char *) nextchunk >= ((char *) av->top + chunksize(av->top)), 0 )) { errstr = "double free or corruption (out)" ; goto errout; } if (__glibc_unlikely (!prev_inuse(nextchunk))) { errstr = "double free or corruption (!prev)" ; goto errout; } nextsize = chunksize(nextchunk); 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); 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) { nextinuse = inuse_bit_at_offset(nextchunk, nextsize); if (!nextinuse) { unlink(av, nextchunk, bck, fwd); size += nextsize; } else clear_inuse_bit_at_offset(nextchunk, 0 ); bck = unsorted_chunks(av); fwd = bck->fd; 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); } 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 { 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); } }
大概流程
首先做了一些检查, 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 获得的,就通过
释放掉他
Check In Glbc
来源 https://github.com/DhavalKapil/heap-exploitation
https://heap-exploitation.dhavalkapil.com/author.html