linux下堆的结构
|
|
为了设计简单,硬件访问内存是以机器字长位单位的,所以申请堆块时,malloc
函数会调整申请的内存的长度为机器字长的倍数,
32位及64位情况下均是8的倍数,这就导致size
大小最低3个bit全部为0,
本着物尽其用的原则,这三个bit作为堆块的标志位,其中最低位作为pre_chunk
是否空闲的标志位,
也因为这个原则,特定情况下prev_size
部分也会成为前一个堆块的内容,
这是各种heap base null byte overflow能被利用的原因,
Extending Free Chunks
|
|
输出如下:
$ ./extend_free_overlap
C chunk: 0x2036210 -> 0x2036288
New B chunk: 0x2036110 -> 0x2036288
malloc
函数从freelist
中搜索大小符合要求的堆块,但是只检查了该堆块的size
部分,没有检查next_chunk
的prev_size
Extending Allocated Chunks
|
|
输出:
root@kali:/vagrant/Heap/8_byte_test# ./test
C chunk: 0xe25210 -> 0xe25288
New B chunk: 0xe25110 -> 0xe25288
为了防止double free
,free
函数会检查该堆块的next_chunk
的pre_inuse
标志位,
这个例子中,溢出修改B chunk的size
为B,C两个chunk大小之和,free
函数根据B->size
寻找下一个堆块的pre_inuse
标志位,已确保B不是空闲状态,
但此时寻找到的是C的next_chunk,C不是空闲态,free
函数判定合法,将B+C大小的堆块链入freelist
,
同样的,free
函数也不检查堆块的next_chunk
的pre_size
Shrinking Free Chunks
|
|
程序输出:
root@kali:/vagrant/Heap/8_byte_test# ./test
B2 chunk: 0x1025220 -> 0x10252a0
B3 chunk: 0x1025110 -> 0x1025290
free(B)
后原先的B堆块被链入freelist
,再申请一个大小小于B的B1,malloc函数优先从原先的B中切割一块给B1
此时会修改C堆块的prev_size
,但是因为此前溢出修改了B->size
,根据被修改过的B->size
找到了错误的C堆块头部(B堆块中),
真正的C->prev_size
并没有被修改,在free(C)
时,因为C->PREV_INUSE
为0发生向前合并,根据C->prev_size
找到了原先B堆块(现B1堆块)的头部,
此时B1堆块空闲,满足向前合并的条件,所以B和C两个堆块被合并然后链入freelist,此时再申请合适大小的堆块,就可以覆盖到B2,产生堆块重叠