内存空间的开辟与释放
程序运行时的标准内存布局:
代码段(.text )
存放编译后的机器指令,只读,不可修改。CPU 会从这里读取指令执行
数据段(.data)
已初始化的全局变量、静态变量。在程序运行前就分配好,生命周期是整个程序。
bss 段(.bss)
存放未初始化的全局变量、静态变量。特点:系统自动清 0,在运行分配。
栈(stack)
存放局部变量,函数参数,返回地址。自动分配自动释放。
堆(heap)
是程序员手动申请的动态内存,不连续、空间大、手动管理、易泄露
bin 的分类体系:
Tcache bin:
- 大小范围:0x20-0x410
- 单链表
- 线程私有缓存,LIFO,几乎无 size 检查,版本要求 libc>=2.26
- tcache poisoning(修改 fd 实现任意地址分配)、tcache dup(多次释放同一块)、tcachehouse of spirit
Fast bin:
- 大小范围:0x20-0x80
- 单链表
- LIFO,不合并相邻空闲块,PREV_INUSE 位始终为 1,fastbin 会检查 size 位的大小
- double free、fastbin poisoning、house of spirit
UnSorted bin:
- 大小范围:任意大小
- 双链表
- 除了 fastbin&tcachebin,释放的 chunk 先进入这里,FIFO,分配时会遍历并切割或归类
- unsortedbin attack 修改 bk 写大数、利用 main_arena 泄露 libc 基地址
Small bin:
- 大小范围:<0x400
- 双链表
- FIFO,释放时前后合并
- house of lore
Large bin:
- 大小范围:>=0x400
- 双链表,包含 fd_nextsize/bk_nextsize
- 同一 bin 大小不固定,按照 Size 降序排列,LIFO
- largebin_attack 修改指针实现任意地址写
程序开辟空间的步骤:malloc
入口转换:
__libc_malloc 将用户请求的 size 转换为内部 chunk 大小 ,获取当前现成的分配区 arena
Tchache 快速检查:
在 libc>=2.26 时,如果 size 在 tcachebin 范围(0x20-0x410)且对应链表非空,直接从 tcachebin 当中取出
Fastbin 检查:
如果对应 tcachebin 不符合标准,就检查 fastbin 范围(<=0x80)且对应链表非空,从 Fastbin 中取出
Smallbin 检查:
如果不符合 Fastbin,就检查 size 是否在 Smallbin 范围(<0x400)且对应链表非空,从 smallbin 链尾取出
UnSortedbin 处理:
如果依旧不符合 Smallbin,就进入主循环,遍历 UnSortedbin 中的所有 chunk
- 如果大小恰好等于 UnSortedbin 当中的 chunk,直接分配
- 如果 UnSortedbin 大于请求内存,就切割一部分进行分配,剩余依旧放回 UnSortedbin
- 如果 UnSortedbin 小于请求内存,即大小不匹配,就会将其从 UnSortedbin 当中移除,根据大小选择放入 Smallbin 或者 Largebin 当中。
Largebin 查找:
利用 binmap 快速定位非空 Largebin,取出最小能满足的 chunk 进行切割分配,剩余的放入 UnSortedbin 当中
Top_chunk 分配:
假如上述所有 bins 都未找到合适的空闲区块,检查 Top_chunk 剩余空间,若 Top_chunk 足够大,就从中切割一块并返回,其余部分依旧是 top_chunk
向操作系统申请空间(sysmalloc):
假如 Top_chunk 也不够用,就会调用 sysmalloc
- 大内存:>mmap_threshold 默认 128KB,直接使用 mmap 映射匿名内存,并设置 MMAP 标志
- 主线程小内存:使用 brk 扩展堆顶,扩大 top_chunk 后再切割,若 brk 失败(如堆顶被 mmap 区域占据),则退化为 mmap
释放内存空间:free
释放过程是分配过程的逆操作,核心是将内存块归还到合适的 bin 但这个,并可能触发合并甚至归还系统。
入口检查:
__libc_free 首先检查 __free_hook,然后通过 mem2chunk 将用户指针转回 chunk 指针 p
处理 mmap 内存:
检查 chunk 的 MMAP 标志,若是由 mmap 分配的,调用 munmap_chunk 直接归还给操作系统
安全检查:
_int_free 进行对齐检查、size 合法性检查,并检测 double free
Tchachebin 优先:
如果 Tcache 已启用且对应大小链表未满,将 chunk 放入 tcachebin 当中
Fastbin 处理:
如果 chunk 大小在 fastbin 范围内且未放入 tcachebin 当中,则插入对应 fastbin 链表头部
合并与归位:
对于非 Fastbin&Tcachebin 的 chunk,进行物理相邻合并:
- 后向合并:检查低地址相邻 chunk,若空闲则从原链表中取出并合并
- 前向合并:检查高地址相邻 chunk:
若相邻 chunk 是 top_chunk
直接合并到 top_chunk 当中,释放结束。
若相邻的 chunk 空闲且非 Top
从原链表中取出合并,然后将合并后的新 chunk 放入 UnSortedbin 中
归还系统:
若合并后的 Top_chunk 过大,会触发 malloc_consolidate 或者 systrim,尝试通过 brk 将部分内存归还给操作系统。
更新: 2026-04-23 15:00:03
原文: https://www.yuque.com/idcm/wnemg9/vogi00ckmfqd3ewq