chunk分配了必然是要回收的,为了更高效的分配与回收,最早glibc提供了BIN数组用来实现unsorted bin,small bin,largebin。为了便于小chunk的回收与分配,提供了fast bin。为了解决多线程互斥产生的性能浪费,提供了tcache bin。
如果内容有疑问或是错误,还请各位师傅指出。
Small bin,Large bin,Unsorted bin
事实上,这三个bin储存在一个BIN[127]数组里。
- BIN[0]未被使用
- BIN[1] unsorted bin
- BIN[2]~BIN[63] small bin
- BIN[64]~BIN[126] large bin
先从最简单的small bin开始,BIN[2]到BIN[63]共62个指针,每一个指针都是一个双向链表,在64位下依次存储从32到1008间隔16的chunk,链表通过chunk的fd与bk指针链接。
- fd指向前一个空闲chunk,bk指向后一个空闲chunk。这里前一个后一个并非是物理地址上的相邻,而是链表上的相邻。

值得注意的是,当从small bin中申请chunk时,遵循LIFO,类似于栈。
对于small bin来说每个bin链表存储的都是相同大小的chunk,对于large bin来说每个链表存储的是一个区间大小的chunk,甚至有多个链表存储同一个区间大小的chunk。(但一个chunk只会被存在一个链表里)
unsorted bin其实是作为small bin与large bin的缓冲区,因为程序往往会以结构体为单位进行内存的申请与回收,所以在释放内存块后又对相同大小的内存块进行申请是很常见的。于是unsorted bin同样维护一个双向链表,可以抽象理解为,程序回收chunk时,chunk先进入unsorted bin期待被立马重新申请,在下次申请时如果不能精确匹配申请的大小,便依据chunk的大小进入small bin与large bin等待合并或在申请时分割。
可能是我理解不深且见识太少,对于ctf中的pwn来说unsorted bin,fast bin,large bin用的都不算很多,对于现实中的pwn应该用的更多(毕竟真实环境的申请和回收操作的数量级应该远大于ctf)。
Fast bin
先区分fast bin与前三种bin的区别
- fast bin只使用fd,所以fast bin是一个单向链表
- chunk进入fast bin后并不会被合并
最关键的地方就在于fast bin会维持chunk的使用状态来避免chunk被合并,前文提到相同大小的chunk的回收与申请往往是频繁的,在内存空间上也有较大可能是相邻的,如果短时间大量申请了一个大小的chunk,而后又一一回收,会产生大量的浪费在合并与以后的分割上。
64位下,fast bin中的chunk大小范围为16~128B,这与small bin有一些重合。
Tcache bin
在glibc 2.26以后的版本引入了Tcache bin,其用意是为了解决多线程对bin中chunk的申请。如果两个线程同时对fast bin申请同样大小的chunk,在不加锁的情况下可能会索引到同一个chunk从而引发混乱。所以不同线程之间共享的fast bin,small bin,large bin,unsorted bin实际上都是有互斥锁的,但互斥锁又对性能有很大影响,于是每个线程独立维护一个Tcache bin来保持高效。
同样,Tcache bin也会维持chunk的使用状态,使其不产生合并。
Tcache bin 每种相同大小的chunk最多存放七个
设计一个程序用gdb调试
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
void *chunk1,*chunk2,*chunk3,*chunk4,*chunk5,*chunk6,*chunk7,*chunk8,*chunk9;
chunk1=malloc(0x30);
chunk2=malloc(0x30);
chunk3=malloc(0x30);
chunk4=malloc(0x30);
chunk5=malloc(0x30);
chunk6=malloc(0x30);
chunk7=malloc(0x30);
chunk8=malloc(0x30);
chunk9=malloc(0x30);
// free
free(chunk1);
free(chunk2);
free(chunk3);
free(chunk4);
free(chunk5);
free(chunk6);
free(chunk7);
free(chunk8);
free(chunk9);
return 0;
}

申请9个0x30大小的内存,实际分配9个0x40大小的chunk。随后在回收时,最后释放的七个进入tcache bin,先去释放的两个被挤到fast bin中。

Comments NOTHING