ptmalloc2源码详解 Part1
本系列讲解的版本为Glibc2.23
高版本的diff比如tcache bin的增加等会被放在heap exploitation系列中进行分析
在正式开始malloc流程分析的讲解之前,我们先来了解点基础知识
Chunk
在ptmalloc中,无论是申请内存还是释放内存,操作的对象都为堆块(Chunk)
其在源码中的结构体表示为
1 | struct malloc_chunk { |
一个chunk结构体中有六个字段,但是对于后两个字段:fd_nextsize
和bk_nextsize
,只有大小符合large bin的堆块才会用到,所以一个普通堆块的最小占用空间为4*INTERNAL_SIZE_T字节。
INTERNAL_SIZE_T为机器字长
对于64位来讲,INTERNAL_SIZE_T的大小为8字节,那么堆块最小占用空间就为0x20字节
1 | 已申请堆块在内存中形如: |
prev_size
这个字段只有当低地址的堆块被释放后才会被使用,指示了前一个堆块的大小
这也就意味着此处空间是可以被复用的,即两个已分配堆块,前一个堆块可以使用后一个堆块的prev_size字段的空间
size
当前堆块的大小
在上图中可以看到size
的最低位有两个字符M
和P
,其中:
-
P(PREV_INUSE):该位被设置时,代表上一个堆块是一个正在使用的堆块
-
**M(IS_MMAPED):**该位被设置时,代表堆块内存是通过mmap分配的
这里还有一个上图中未被提到的字段
-
**A(NON_MAIN_ARENA):**记录当前堆块是否属于主进程
这三个字段从高位到低位排序为:A->M->P
fd和bk
双向链表中的前向和后继指针,只有当堆块被释放时才会被使用
在堆块处于被分配状态时,从fd字段开始为用户空间,即上图中mem指向的位置
fd_nextsize和bk_nextsize
作用同fd和bk,但是只有大小处在largebin区间中的被释放的堆块才会使用这两个字段
Chunk相关操作
在ptmalloc中,用户申请的内存大小和实际申请的内存大小是不一样的
request2size
1 | # define MALLOC_ALIGNMENT (2 *SIZE_SZ) |
假设在64位下此时用户想要申请0x20大小的内存
ptmalloc会使用request2size
宏函数检查0x20(req)+0x8(SIZE_SZ)+0xf(MALLOC_ALIGN_MASK)是否小于0x20(MINSIZE)
如果小于的话,直接返回MINSIZE给用户,否则返回(0x20+0x8+0xf)&~0xf即0x30给用户
这也就意味着:
-
当申请小于0x20大小堆内存的时候,系统会直接返回0x20给用户
-
当申请大于0x20大小堆内存的时候,系统会给原来申请大小加上0x10(prev_size和size字段所占用空间)并且最终对其到0x10
prev_size和size字段所处空间对于用户来说是透明的,因此对于最终返回给用户的指针是需要进行一些处理的
chunk2mem、mem2chunk
1 | /* conversion from malloc headers to user pointers, and back */ |
对于已经分配的 chunk,通过 chunk2mem 宏根据 chunk 地址获得返回给用户的内存地址
反过来通过 mem2chunk 宏根据 mem 地址得到 chunk 地址
chunk 的地址是按 2*SIZE_SZ对齐的,而 chunk 结构体的前两个域刚好也是 2*SIZE_SZ 大小,所以,mem 地址也是 2*SIZE_SZ 对齐的