- malloc首先进入__libc_malloc
- 在__libc_malloc中首先申请的是tcache bin
- 如果没初始化tcache就初始化
- 检查:
- 检查请求的大小对应的idx,是不是小于tcache_bins的个数
- 检查tcache是否不为空
- 检查tcache->counts[tc_idx] 是否大于0
- 如果检查通过那么就从tcache中取堆块,然后返回给用户,不进入_int_malloc
- 进入_int_malloc
- 获取0x10对齐后的大小,后简记为nb
- 如果av等于NULL,那么sysmalloc一个
- 如果nb小于等于宏定义中的MAX_FAST,进入fastbin的申请流程
- 根据nb,获取对应fastbin的idx
- 获取对应idx的fastbin的地址,fb
- victim等于*fb,也就是fastbin的最后放入的一个堆块
- 如果victim不为NULL
- 检查是否0x10对齐,不对齐就报错”malloc(): unaligned fastbin chunk detected 2
- 还原已经xor的指针
- 如果victim不为NULL(一般都会执行)
- 根据victim的size大小获取idx,victim_idx
- 如果victim_idx !=idx,就会报错”malloc(): memory corruption (fast)”
- 如果tcache已经初始化,并且获取到的tc_idx小于tcache_bins的个数
- 只要对应tc_idx的tcache没满,fastbin中还有堆块,那么就会让tc_victim等于fastbin最后释放的堆块
- 如果tc_victim不对齐,报错”malloc(): unaligned fastbin chunk detected 3”
- 如果是单线程
- 还原xor的fastbin指针
- 放入对应tc_idx的tcache(原来的victim也被放入,根据头插法,会被放到所有fastbin堆块最后)
- 直到不满足条件结束循环
- 只要对应tc_idx的tcache没满,fastbin中还有堆块,那么就会让tc_victim等于fastbin最后释放的堆块
- 返回victim
- 如果nb大于 MAX_FAST,并且属于smallbin的范围
- 获取nb对应smallbin的idx
- 获取对应idx的samllbin的地址,bin
- 如果smallbin不为空,同时
- 让victim等于smallbin尾部的堆块,也就是最先放入的堆块
- 获取victim的bk指向的堆块,bck
- 如果bck的fd指针不指向victim,报错”malloc(): smallbin double linked list corrupted”
- 为下一个堆块设置pre_inuse标志位
- 让bin的bk连上bck,让后面的fd连上bin,也就是victim从smallbin中拿出
- 根据nb获取对应的tcache的tc_idx
- 如果tcache已经初始化,并且tc_idx小于tcache_bins(其实就是在tcache的范围内,tcache_bins可以理解为管控tcache的大小范围的)
- 只要tcachebin没满并且smallbin不为空,不断取出循环放入tcache(同样根据头插法victim也会被放入tcache,并且是最后)
- 直到不满足条件,循环结束
- 返回用户堆块
- 如果上述条件不满足
- 根据nb,获取largebin对应的idx
- 如果fastbin不为空
- 触发malloc_consolidate,合并fastbin放入unsorted bin
- 初始化一些tcache的东西,为下面进入unsorted bin大循环做准备
- 根据nb获取对应tc_idx
- 如果tcache已经初始化,并且tc_idx小于tcache_bins
- 让tcache_nb=nb;
- 让return_cached = 0;标志位,标记是否直接从 tcache 返回内存
- 让tcache_unsorted_count = 0; 用于统计本次 _int_malloc 从 unsorted bin 移入 tcache 的 chunk 数量
- 进入unsorted bin大循环,最多循环1000次
- 初始化计数器iters = 0
- 只要unsorted bin不为空
- victim等于unsorted bin最后放入的堆块
- bck = victim的bk指向的堆块
- 获取victim的size,size
- 根据size,获取下一个堆块的地址,next
- 然后是一大堆检查
- 如果该堆块victim的size<0x10或者size>arena的大小就会报错”malloc(): invalid size (unsorted)”
- 如果下一个堆块next的size<0x10或者size>arena的大小就会报错”malloc(): invalid next size (unsorted)”
- 如果next的prev_size不等于victim的size就报错”malloc(): mismatching next->prev_size (unsorted)”
- 如果bck的fd不等于victim,或者victim的fd不等于unsorted bin就报错”malloc(): unsorted double linked list corrupted”
- 如果next的prev_inuse为1,就报错”malloc(): invalid next->prev_inuse (unsorted)”
- 如果这个堆块属于unsorted bin中smallbin的范围,并且unsorted bin只有这一个堆块,并且这个堆块还是上一次切割剩下的堆块,也就是last remainder,并且size>nb+0x20
- 再次切割这个堆块
- remainder_size = size - nb;然后remainder = chunk_at_offset (victim, nb);此时victim等于切割下来的堆块
- 保存remainder到last_remainder中,last_remainder=remainder
- 如果切割后还属于smallbin的范围,把fd_nextsize和bk_nextsize置为NULL
- 给victim设置头部信息,给remainder设置头部信息,给remainder的下一个堆块写入prev_size
- 进行检查
- 检查victim是否是mmap的
- 检查arenna的地址
- 检查victim是否对齐
- 检查victim是否size>=0x20
- 检查victim的大小是否≥nb
- 检查victim的大小是否 <nb+0x20
- 返回用户堆块
- 再次切割这个堆块
- 如果不切割,把victim拿出,unsorted_chunks (av)->bk = bck; bck->fd = unsorted_chunks (av);
- 如果victim的size等于nb
- 给victim的下一个堆块设置prev_inuse位
- 如果nb>0,并且对应idx的tcache还没满
- 把victim放入对应idx的tcache,然后继续循环取和放入,直到tcache满或者unsorted bin为空了
- 让return_cached = 1,标志一会要从tcache bin返回堆块
- continue直接跳出while循环,跳到 如果return_cached = 1,从tcache_get获取
- 否则
- 检查是否是mmap的如果是不进行接下来的检查,检查arenna的地址,检查是否对齐,检查是否size>=0x20, 检查victim的大小是否>=用户申请的大小,检查victim的大小是否<用户申请的大小+0x20
- 返回victim给用户
- 否则说明unsorted bin中没有合适的,把unsorted bin中的堆块放入对应大小的bin中,接下来就是这一过程,这个过程是沿着unsorted bin中的fd链的,如果第一个是0x420,第二个是0x430,第三个是0x410,那么malloc 0x430以后,0x420的堆块会进入large bin,0x430的堆块会被取出返回,0x410的堆块会呆在unsorted bin,参考上面victim的size等于nb的情况
- 如果victim的size属于smallbin的范围
- 根据size,获取对应smallbin的下标,victim_index
- bck等于对应victim的idx的smallbin地址
- fwd为从smallbin最后放入的堆块
- 如果victim的size属于largebin的范围(下面是更新fd_nextsize和bk_nextsize) 1. 根据size,获取对应largebin的下标,victim_index 2. bck等于对应victim的idx的largebin地址 3. fwd为从largebin最后放入的堆块(即largebin中最大 chunk )
- 如果idx对应的largebin不为空
- 将victim的size的prev_inuse位设置为1
- 如果victim的size<对应largebin放入的最先一个堆块(当前large bin中最小的chunk时),那么认定victim更小,因此需要将它插入到 Large Bin 的末尾(即最小 chunk 之后),把victim找到合适位置放入largebin并更新十字链表的指针
- 更新fwd等于largebin的地址
- bck为该largebin最先的第二个放入的堆块当前 Large Bin 中第二大的 chunk
- 让victim的fd_nextsize指向该largebin最后放入的堆块(当前 Large Bin 中最大的 chunk),这里是循环链表的缘故,victim最小了再小就指向最大的了
- 让victim的bk_nextsize指向该largebin最后放入的堆块(当前 Large Bin 中最大的 chunk)的bk_nextsize
- 该largebin最后放入的堆块(当前 Large Bin 中最大的 chunk)的bk_nextsize指向victim
- 否则
- 只要size小于即fwd(largebin中最大 chunk ),同时大于等于该large bin中最小的chunk
- 一直去寻找更小的堆块,
- 更新fwd=fwd->fd_nextsize
- 直到找到刚好小于等于victim size的chunk,此时fwd为这个chunk
- 如果victim的size正好等于这个chunk的size
- 更新fwd 等于 fwd的fd,准备把victim放到fwd的fd位置
- 否则
- 则说明victim要放到fwd的bk_nextsize位置
- victim的fd_nextsize指针指向这个chunk
- victim的bk_nextsize指针指向这个chunk的bk_nextsize
- 检查,如果这个chunk的bk_nextsize的fd_nextsize不是这个堆块报错”malloc(): largebin double linked list corrupted (nextsize)”
- 这个堆块(fwd)的bk_nextsize指向victim
- 更新bck为fwd(此时的堆块)的bk指向的堆块
- 检查,如果bck的fd不等于fwd报错”malloc(): largebin double linked list corrupted (bk)”
- 只要size小于即fwd(largebin中最大 chunk ),同时大于等于该large bin中最小的chunk
- 否则
- 让victim的fd_nextsize和bk_nextsize都指向victim
- 如果idx对应的largebin不为空
- victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim,victim连接fwd和bck,它们再连接victim
- 让tcache_unsorted_count++ tcache_unsorted_count代表tcacge中unsorted bin数量
- 如果return_cached = 1并且mp_.tcache_unsorted_limit (是 tcache中unsorted bin的 限制值通常为0)> 0并且tcache_unsorted_count大于mp_.tcache_unsorted_limit
- 直接从tcache中拿堆块,并返回用户堆块
- 如果循环大于1000次,退出循环
- 如果return_cached = 1,从tcache_get获取,并返回
- 接下来准备从largebin中拿
- 如果nb属于large bin的范围
- 获取对应large bin的地址,bin
- 如果largebin不为空,让victim变为largebin最后放入的堆块并且如果victim的size大于等于nb(意味着肯定是可以被分配的)
- 让victim变为large bin中最小的
- 只要victim的size小于nb
- 就去寻找更大的
- 直到victim的size大于等于nb
- 如果victim不是largebin最小的堆块并且victim的size等于victim->fd
- 选择切割后放入(victim->fd)的而不是先放入的(victim)
- 切割,remainder_size = size - nb
- 让victim脱链unlink
- 如果切割后剩下的小于 0x20
- 给下一个堆块设置prev_inuse位
- 否则
- 更新remainder为切割后的堆块,victim为切割下来的堆块
- 获取unsorted_chunks的地址,bck
- 更新fwd为unsorted bin中最后放入的堆块
- 检查,如果fwd的bk不指向bck,报错”malloc(): corrupted unsorted chunks”
- 让切割剩下的remainder放入unsorted bin的头部
- 如果remainder属于large bin的范围
- 把fd_nextsize和bk_nextsize设置为NULL
- 为victim设置头部信息,为remainder设置头部信息,为remainder的下一个 堆块设置prev_size
- 常规检查
- 返回用户堆块
- 位图再次搜索,如果有可用堆块,返回用户堆块
- 如果上述都没能返回用户堆块,使用top chunk
- victim = av->top ,获取top chunk的地址
- 根据victim获取size
- 检查,如果size>av->system_mem,报错”malloc(): corrupted top size”
- 如果size >=nb + 0x20
- 切割top chunk
- 设置victim的头部信息,设置remainder的头部信息,为remainder的下一个堆块设置prev_size
- 返回用户堆块
- 否则 ,查看fastbin是否存在堆块
- 若存在进行malloc_consolidate,让fastbin堆块合并放入unsorted bin
- 如果nb属于smallbin的范围,获取对应smallbin的idx
- 如果nb属于largebin的范围,获取对应larrgebin的idx
- 否则
- 就是前面都不能找到合适堆块
- 直接sysmalloc
- 返回用户堆块
Thanks for reading!
