ptmalloc2安全检查
分为两篇,一篇记录安全检测,一篇记录操作变化
需要注意,一个大版本下的libc还有许多小版本,版本不同libc也会存在差异,有些明明可能是后面版本的检查,可能被反向移植到旧版
这里以libc下载网站下载的为准
有些检查可能是在介绍的两个版本之间更新的,这里以只考虑比较经典的几个版本
glibc2.23
现在看来,2.23已经是一个比较老的版本了,比它更早的版本暂时不做关注,2.23是一个十分经典的版本,以它为基础先概览各类检查
malloc
__libc_malloc
1 | assert (!victim || chunk_is_mmapped (mem2chunk (victim)) || |
返回victim前的最后一个检查,需要满足以下三个条件中的至少一个:
- victim==NULL
- chunk的mmap分配标志位为1
- chunk的non_main_arena标志为0
__int_malloc
mglobal-1
1 |
1 |
_int_malloc初始会在checked_request2size (bytes, nb)中判断申请大小是否合规
如果在无符号比较中申请大小大于等于-2* MINSIZE,则会报错
另外提一下,虽然在代码中是先判断申请大小是否越界,再将其转为实际申请chunk大小,但在大多数实际情况下,二者的顺序是倒过来的,即先转化再判断
mfastbin-1
1 | if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0)) |
取出fastbin中的chunk时,通过该chunk计算出的fastbin索引是否与该chunk所在的链的索引相同,即大小是否对应
msmallbin-1
1 | bck = victim->bk; |
取出smallbin中的chunk时,判断链表的完整性
munsortedbin-1
1 | if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0) |
unsorted循环遍历时,检查从unsortedbin中取出的chunk的size是否合规
munsorted-2
1 | assert ((bck->bk->size & NON_MAIN_ARENA) == 0); |
将unsortedbin中取出的chunk放入不为空的largebin中时,largebin链中的最后一个chunk的NON_MAIN_ARENA标志位应该为0
1 | assert ((fwd->size & NON_MAIN_ARENA) == 0); |
将unsortedbin中取出的chunk放入不为空的largebin中时,chunk的size大于等于最小的size
会对每一个位于nextsize链上大于它的chunk调用检查
mlargebin-1
1 | bck = unsorted_chunks (av); |
largebin中取出chunk需要切割时,会检查unsorted头部的完整性
mbinmap-1
1 | bck = unsorted_chunks (av); |
与largebin-1相同,位于binmap分配中
free
__int_free
fglobal-1
1 | if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0) |
- 通过无符号数比较p是否大于-size,判断p是否正常
- 判断p是否对齐
fglobal-2
1 | if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size))) |
size是否小于MINSIZE或者size不对齐
ffastbin-1
1 | if (__builtin_expect (chunk_at_offset (p, size)->size <= 2 * SIZE_SZ, 0) |
判断p的下一个chunk的size是否正常
ffastbin-2
1 | if (__builtin_expect (old == p, 0)) |
如果p等于该fastbin链上的第一个chunk,那么判定为double free
ffastbin-3
1 | if (have_lock && old != NULL && __builtin_expect (old_idx != idx, 0)) |
比较少见的一个检查
fglobal-2
1 | if (__glibc_unlikely (p == av->top)) |
如果p为top_chunk,判定为double free
fglobal-3
1 | if (__builtin_expect (contiguous (av) |
判断p的nextchunk是否超越了堆的边界
fglobal-4
1 | if (__glibc_unlikely (!prev_inuse(nextchunk))) |
判断p的nextchunk的prev_inuse位是否为1,不为1则报错
fglobal-5
1 | nextsize = chunksize(nextchunk); |
判断p的nextchunk的size是否正常
fglobal-6
1 | if (__glibc_unlikely (fwd->bk != bck)) |
放入unsortedbin时,判断unsorted的头部完整性
附属
unlink
unlink-1
1 | if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \ |
验证完整性,操作chunk的上一个的下一个和下一个的上一个是否都等于该chunk
unlink-2
1 | if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) \ |
和unlink-1类似,不过检查的是largebin的nextsize链完整性
glibc2.27
附属
unlink
+unlink-3
1 | if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \ |
检查通过该chunk的size找到的nextchunk的prev_size是否等于该chunk的size,
unlink不会检查通过prev_size找到的chunk的size是否等于那个prev_size
malloc_consolidate
+maco-1
1 | { |
判断由p的size计算得出的fastbin索引是否与当前fastbin链匹配
tcache
+tcput-1
assert (tc_idx < TCACHE_MAX_BINS);
判断tc_idx是否越界
+tcget-1
1 | assert (tc_idx < TCACHE_MAX_BINS); |
判断tc_idx是否越界以及该tcache链是否为空
glibc2.29
malloc
@munsortedbin-1
1 | if (__glibc_unlikely (size <= 2 * SIZE_SZ) |
除了之前就存在的对victim的size检查外,新增:
- 对nextchunk(victim的物理相邻chunk)的size的检查
- 判断该chunk的size是否等于nextchunk的prev_size
- 判断链表尾部的完整性
- 对victim的nextchunk的prev_inuse位检查,是否合理
+munsortedbin-2
1 | if (__glibc_unlikely (bck->fd != victim)) |
移除unsorted中chunk时检查链表尾是否完整
+usetop-1
1 | if (__glibc_unlikely (size > av->system_mem)) |
检查topchunk的size是否合规
free
+ftcache-1
1 | if (__glibc_unlikely (e->key == tcache)) |
free时,如果一个chunk的key字段等于tcache,那么就会遍历该tcache链中的所有chunk来判断是否存在double free
可以通过破坏key绕过
+fglobal-7
1 | if (!prev_inuse(p)) { |
之前向低地址合并都是不检查即将unlink的chunk的size是否等于找到这个chunk的prev_size
现在新增了这个检查,使得unlink的利用受到多一点的限制
向高地址合并则没有变化
且宏unlink现在由函数unlink_chunk实现
但内部具体代码并无明显变化
附属
+maco-2
1 | if (!prev_inuse(p)) { |
与fglobal-7相同,只不过由malloc_consolidate触发
glibc2.31
malloc
+munsortedbin-3
1 | if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd)) |
当unsortedbin中的chunk放入largebin时,如果要放入的size不小于largebin链中最小的chunk,且size异于largebin链上的所有chunk,则会在插入nextsize链时检测nextsize链完整性
+munsortedbin-4
1 | if (bck->fd != fwd) |
当unsortedbin中的chunk放入largebin时,如果要放入的size不小于largebin链中最小的chunk,就会触发fd/bk链的完整性检查
这两个检查实际上应该是2.30增加的
附属
-tcput-1
tcache_put中去除
1 | assert (tc_idx < TCACHE_MAX_BINS); |
-tcget-1
tcache_get中去除
1 | assert (tc_idx < TCACHE_MAX_BINS); |
当然这并不意味着tcache->entries[tc_idx]== 0是可行的,因为这样的话__libc_malloc根本不会进入tcache_get
glibc2.32
malloc
+mfastbin-2
1 | if (__glibc_unlikely (misaligned_chunk (victim))) |
fastbin取出chunk时,新增chunk对齐检查
+mfastbin-3
1 | if (__glibc_unlikely (pp != NULL && misaligned_chunk (pp))) \ |
在REMOVE_FB宏中,新增对齐检测
+mfastbin-4
1 | if (__glibc_unlikely (misaligned_chunk (tc_victim))) |
fastbin填充tcachebin新增对齐检测,会检查每一个chunk是否对齐
free
@ftcache-1
1 | if (__glibc_unlikely (e->key == tcache)) |
判断doublefree时,会检查tcache链上的所有chunk是否对齐
附属
+maco-3
1 | if (__glibc_unlikely (misaligned_chunk (p))) |
每一个fastbin取出时会检查对齐
+tcget-2
1 | if (__glibc_unlikely (!aligned_OK (e))) |
tcache_get取出chunk时会有对齐检查
glibc2.33
free
@ftcache-1
1 | if (cnt >= mp_.tcache_count) |
判断doublefree时,会检查tcache链上的chunk数目是否超过限制
之前虽然也会与mp_.tcache_count比较,但只是作为一些分支的条件,并不会检查错误
除此之外tcache_counts几乎没有其他检查了
glibc2.34
无显著变化
glibc2.35
无显著变化
glibc2.37
无显著变化
glibc2.38
malloc
-munsortedbin-2
1 | if (__glibc_unlikely (bck->fd != victim)) |
移除该检查,因为其实前面已经做过一次检查了,这个检查有些重复