文章目录
一、 什么是堆
堆是可以根据运行时的需要进行动态分配和释放的内存,大小可变
Malloc/New
Free/Delete
堆的实现重点关注内存块的组织和管理方式,尤其是空闲内存块
- 如何提高分配和释放效率
- 如何降低碎片化、提高空间利用率
glibc的堆管理实现
ptmalloc2-glibc
不同线程维护不同的堆,称为per thread arena
主线程创建的堆,称为main arena
Arena数量受到CPU核数的限制
- 对于32位系统 arena数量上限=2*核数
- 对于64位系统 arena数量上限=8*核数
(一)arena
- 指的是堆内存区域本身,并非结构
- 主线程的main arena通过sbrk创建
- 其他线程arena通过mmap创建
(二)malloc_state
- 管理arena的核心机构,包括堆的状态信息、bins链表等
- main arena对应的malloc_state结构存储在glibc的全局变量中
- 其他线程arena对应的malloc_state存储在arena本身中
(三) bins
- bins用来管理空闲内存块,通常使用链表结构来进行组织
- 一个记录空闲块的账本
根据chunk的大小和状态,有许多种不同的Bins结构
1.Fast bins
用于管理小的chunk
- 大小:
- x86_32平台:16-64字节
- x64平台:32-128字节
- 相同大小的chunk放在一个bin中
- 单向链表
- 后进先出
- 相邻空闲fast bin chunk不会被合并
- 当chunk被free时,不会清理PREV_INUSE标志
2.bins
(1)small bins
用于管理中等大小的chunk
- chunk大小<1024bytes(64bit)
- 相同大小的chunk放在一个bin中
- 双向循环链表
- 先进先出
- 当有空闲块相邻时,chunk会被合并成一个更大的chunk
- bins[2]bins[3]…bins[125]共62组smallbin,大小范围[0x20,0x3f0](64位)
(2)large bins
用于管理较大的chunk,代码量多,在范围内寻找合适的chunk返回给用户
- chunk大小>=1024bytes(64bit)
- 每组bin表示一组size范围而不是具体的size,例如bins[126],bin[127]的链表中保存长度在[0x400,0x440]的chunk
- 双向循环链表
- 先进先出
- chunk按照从大到小排序
- 当有空闲块相邻,chunk会被合并
- bins[126],bin[127]…bin[250,bin[251]共有63组largebin,大小范围[0x400,x](64位)
(3)unsorted bins
用于存放未整理的chunk
- 64位平台中:chunk大小>128字节
- 只存在唯一一个unsorted bin
- 双向循环链表
- 当一个chunk(非fast bin)被free,它首先被放入对应的small bin/large bin
- bins[0],bins[1]
对双向链表的解释:这样看,低地址是前,高地址后
(四)chunk
内存块的结构
1. free chunk
64位平台下,free chunk的第一个字段prev_size(8字节)存储了前一个chunk的大小。
free chunk的第二个字段size记录了当前chunk的大小,该字段最低三个bit被用作其他含义。
P代表PREV_INUSE,即代表前一个chunk是否被使用
M代表IS_MMAPPED,代表当前chunk是否被mmap出来的。
N代表NON_MAIN_ARENA,代表改chunk是否属于非Main Arena
第三个字段fd和第四个字段bk(8字节)前向指针和后向指针,这两个字段用于bin链表单重,用来链接大小相同或者相近的free chunk,便于后续分配时查找
2.allocted chunk
allocted chunk的前两个字段和free chunk相同
第三个字段开始到最后,chunk中存储的都是用户数据。甚至下一个chunk的第一个字段prev_size,也可被用来存放数据,原因是prev_size字段只有当前一个chunk是free的时候才有意义,如果前一个chunk是已经分配的,堆管理器并不放心。
所以对一个chunk来说,用户可用大小从偏移+8(为何+8,是加上下一个chunk的prev_size的8个字节)开始,一直到下一个chunk的prev_size字段。
在64位平台下,chunk的大小一定是0x10zijie的整数倍。malloc返回的指针位图中mem指向的位置,即数据开头
malloc参数与chunk大小的关系
malloc参数为用户申请的内存大小
chunk包含数据和metadata
返回的chunk只要保证其中可用数据大小大于等于用户申请
在x86 32位平台下,chunk的大小一定是8字节的整数倍,x64平台下,chunk的大小一定是16字节的整数倍
3.Top chunk
- 不属于任何bin
- 在arena中处于最高地址
- 当没有其他空闲块时,top chunk就会被用于分配
- 分裂时:
- 一块是请求大小的chunk
- 另一块余下chunk将成为新的top chunk
4.Last_remainder
当请求small chunk大小的内存时,如发生分裂,则剩余的chunk保存为last_remainder
二、 malloc和free的工作流程
(一)malloc工作流程
- 如果size<max first,在fast bins中存在fast chunk,如找到则结束
- 如果size in_smallbin_range,在small bins中寻找small chunk,如找到则结束
- 如果size not in _smallbin_range,合并所有fastbin的chunk
- 循环:
a.检查unsorted bin中的last_remainder
如果满足一定条件,则分裂之,将剩余的chunk标记为新的last_remainder
b.在unsorted bin中搜索,同时进行整理
如遇到精确大小,则返回否则就把当前chunk整理到small/large bin中去
c.在small bin和large bin中搜索最合适的chunk(不一定是精确大小) - 使用top chunk
(二)free流程
- 如果size<max fast,放入fast bin,结束
- 如果前一个chunk是free的
a. unlink前面的chunk
b.合并两个chunk,并放入unsorted bin - 如果后一个chunk是top chunk,则将当前chunk并入top chunk
- 如果后一个chunk时free的
a.unlink后面的chunk
b.合并两个chunk,并放 入unsorted bin - 前后chunk都不是free的,放入unsorted bin
三、fastbin attack
Fast bin利用技术
- Fast bin为单向链表,结构简单,容易伪造
- 为了提高效率,安全检查较少
- 只针对Fast bin大小的chunk,small/large chunk 不适用
利用思路
- 空闲Fast chunk如果发生溢出被覆盖,则链表指针fd可以被修改
- 可以通过修改链表指针fd,在Fast bin链表中引入伪造的空闲Fast chunk
- 下次分配时分配出伪造的Fast chunk
- 伪造的Fast chunk可以在.bss全局变量处,也可以在栈上·