What is chunk
由malloc申请的内存称之为chunk。在ptmalloc内部用malloc_chunk结构体来表示。
无论一个 chunk 的大小如何,处于分配状态还是释放状态,它们都使用一个统一的结构。虽然它们使用了同一个数据结构,但是根据是否被释放,它们的表现形式会有所不同。
malloc_chunk的结构如下:
1 | struct malloc_chunk { |
处于已分配状态的chunk内存如下:(图中少标了一个标志位)前两个字段称为 chunk header,后面的部分称为 user data。每次 malloc 申请得到的内存指针,其实指向 user data 的起始处即图中的mem。
处于释放状态的chunk:此时 fd,bk 有效
可以发现,如果一个 chunk 处于 free 状态,那么会有两个位置记录其相应的大小
- 本身的 size 字段会记录,
- 它后面的 chunk 会记录。
chunk相关宏
chunk与mem指针头部的转换
1 | /* conversion from malloc headers to user pointers, and back */ |
最小的chunk大小等同于 MINSIZE
1 | /* The smallest possible chunk */ |
其中,offset()函数算出 fd_nextsize 在 malloc_chunk 中的偏移,说明最小的 chunk 至少要包含到 bk 指针。
检查分配给用户的内存是否对齐
1 | /* Check if m has acceptable alignment */ |
请求字节数判断
就是判断请求的内存大小是否可以满足
1 | /* |
request2size ——将用户请求内存大小转为实际分配的内存大小
1 | /* pad request bytes into a usable size -- internal version */ |
大致流程如下:
- 判断用户请求的字节大小是否可以满足,REQUEST_OUT_OF_RANGE
- 由于chunk间存在复用,所以当前chunk若是用来分配,则下一个chunk的 prev_chunk 也属于当前chunk的user_data部分
- 若 请求大小 + SIZE_SZ 对齐后的大小小于MINSIZE则直接分配MINSIZE
- 若大于,则再进行 2*SIZE_SZ 对齐
标记相关
chunk标志位进行或运算,用于掩码
1 | /* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */ |
通过chunk指针p 对某标志位进行提取、检查、置位和清除
1 | /* extract inuse bit of previous chunk */ |
要注意,当前chunk的使用状态是由下一个chunk的size成员的低比特位决定的。
获取chunk size
1 | /* Get size, ignoring use bits */ |
获取下一个物理相邻的chunk
当前chunk指针加上当前chunk的大小以获得
1 | /* Ptr to next physical malloc_chunk. */ |
获取(修改)前一个chunk的信息
仅当前一个chunk处于释放状态有效
1 | /* Size of the chunk below P. Only valid if !prev_inuse (P). */ |
当前chunk使用状态相关操作
即对下一个chunk的size对象的标志位操作
1 | /* extract p's inuse bit */ |
设置 chunk 的 size 字段
set_head_size 不会改变当前 chunk 的标志位(这里书是这么写,但单实现好像不然,这里先不管)而 set_head 会
1 | /* Set size at head, without disturbing its use bit */ |
获取指定偏移的 chunk
直接把指定偏移处看作chunk
1 | /* Treat space at ptr + offset as a chunk */ |
指定偏移处 chunk 使用状态相关操作
1 | /* check/set/clear inuse bits in known places */ |
chunk 合并
当一个非fast bin 的chunk被释放 时,会与相邻的chunk合并,顺序通常是先向后合并(相邻低地址)再向前合并(相邻高地址)。如果向前合并的chunk时 top chunk,则合并成新的chunk;如果不是的话则合并之后会被加入 unsorted bin 中。
chunk 拆分
当用户申请的 chunk 较小时,会先将一个大的 chunk 进行拆分,合适的部分返回给用户,剩下的部分(称为remainder)加入unsorted bin 中。同时malloc_state 中的last_remainder 会记录最近拆分出的remainder。当然这个remainder的大小至少要为MINSIZE,否则不能拆分。
拆分 chunk 的一种情况是:fast bin 和 small bin 中都没有合适的chunk,同时unsorted bin 中有且只有一个可拆分的chunk,并且该chunk 是last remainder。
关于其源码另起一篇来分析
参考资料
《CTF竞赛权威指南(PWN篇)》
CTF wiki
Comments