什么是堆

  • 栈常常用于为函数分配大小固定的局部内存

  • 堆是可以根据运行时的需要进行动态分配和释放的内存,大小可变

    • 堆由低地址向高地址增长

    • 对应接口:

      malloc/new(c++)

      free/delete

  • 栈是保证函数递归运行的数据结构,程序调用过程中保存局部变量等等由系统自动开辟的一个内存空间,堆是一个我们自己开辟的一个内存空间(不要把两者混为一谈

堆在干什么

  • 堆的实现重点关注内存块的组织和管理方式,尤其是空闲内存块
    • 如何提高分配和释放效率
    • 如何降低碎片化,提高空间利用率
  • 举例:浏览器的DOM树通常分配在堆上
    • 堆的实现算法影响堆分配网页加载和动态效果速度
    • 堆的实现算法影响浏览器对内存的使用效率(降低)

常见堆实现

  • dlmalloc - 通用分配器
  • ptmalloc2 - glibc
    • 基于dlmalloc fork
  • jemalloc - FreeBSD、Firefox、Android
  • tcmalloc - Google Chrome
  • libumen - Solaris
  • Windows 10 - segment heap

ptmalloc2的多线程支持

  • 不同的线程维护不同的堆,称为per thread arena
  • 主线程创建的堆,称为main arena
  • Arena 数量受CPU核数的限制
    • 32位系统:arena数量上限 = 2*核数
    • 64位系统:arena数量上限 = 8*核数

glibc的堆管理 - ptmalloc2(glibc 2.26)

  • arena
    • 指的是堆内存区域本身,并非结构
    • 主线程的main arena通过sbrk创建
    • 其他线程arena通过mmap创建
  • malloc_state
    • 管理arena的核心结构,包含堆的状态信息,bins链表等
    • main arena对应的malloc_state结构储存在glibc的全局变量中(意味着如果揭露了glibc的位置那么也可以知道了malloc_state的位置
    • 其他线程arena对应的malloc_state储存在arena本身当中
  • bins
    • bins用来管理空闲的内存块,通常使用链表结构来进行组织
  • chunks
    • 内存块的结构

arena

  • 头部结构:malloc_state

    • malloc_state申请了一个名为main_arena 的全局变量用来存储arena的状态

    • 以下三个用来管理堆空间中的空闲位置

      fastbinsY(数组)用来管理空闲的bins链表

      top chunk:指向自留地初始位置(堆中的空闲内存)

      bins:

main arena的堆空间大致模型

  • free chunk的大概结构

    在64位平台下,free chunk的第一个字段(前八个字节): prev_size储存了前一个chunk的大小,当前一个chunk为free chunk时,这个的数据为前一个chunk中的size中的数据,若前一个chunk为allocated时,这里存放的是date

    第二个字段size:存储了当前chunk的大小该字段中有三个bit用作其他用途:

    ​ P:代表PREV_INUSE,即代表前一个chunk是否被使用

    ​ M:代表IS_MMAPPED,即代表当前chunk是否是mmap出来的

    ​ N:代表NON_MAIN_ARENA,代表chunk是否属于非Main Arena。

    由于size的大小为0x10字节的整数倍,所以size内第三位会被忽略掉,于是可以在后三位来表示其他信息节省空间

    第三四字阶段fd和bk(各8字节)前向指针和后向指针,这两个字段用于bin链表当中,用来链接大小相同或是相近的free chunk,便于后续分配时查找。(非空闲堆块中没有这个两个数据)

    32位平台下,chunk大小一定是8字节的整数倍,64位平台下chunk的大小一定是16字节的整数倍

  • bins结构

    用来管理和组织空闲内存块的链表结构,根据chunk的大小和状态,有许多不同的bins结构

    • small bins - 用于管理小(fast bins)到中等大小的chunk
    • large bins - 用于管理较大的chunk
    • unsorted bins - 用于存放未整理的chunk

Fast bins

  • 大小

    • x86_32平台:16~64字节
    • 非平台:32~128字节

    chunk的大小

  • 相同大小的chunk放在一个bin中

  • 单向链表(只用了fd,没有使用bk)

  • 后进现出(First in last out,类似栈)

  • 相邻的空闲的fast bin chunk不会被合并(整个堆对于fast bin不会对它有过多操作)

  • 当chunk被free时,不会改变下一个chunk中的PREV_INUSE(size中的P)标志

small bins

  • chunk大小 < 1024 bytes(64bit)
  • 相同大小的chunk放到一个bin中
  • 双向循环链表
  • 先进先出
  • 有空闲块相邻时,chunk会被合并成一个更大的chunk
  • 在64位下,bins[2],bins[3],…,bins[124],bins[125],共62组small bin,大小范围[0x20,0x3f0]
  • 堆块合并的主要目的是减少碎片化和提高堆块效率

large bins

  • chunk 大小 >= 1024 bytes (64bit)
  • 每组bin表示一组size范围而不是具体的size,例如bins[126],bins[127]的链表中保存长度在[0x400,0x440]的chunk
  • 双向循环链表
  • 先进先出
  • chunk按照大小从大到小排序
  • 有空闲块相邻,chunk会被合并
  • 64位下,bins[126],bins[127],…,bins[250],bins[251]共63组large bin,大小范围[0x400,X]

unsorted bin

  • 64位平台中:chunk大小 > 128字节
  • 有且只有一个unsorted bin
  • 双向循环链表
  • 当一个chunk(非fast bin)被free,它首先被放入unsorted bin,等后续整理出来时才会放入对应的small bin/fast bin
  • bin[0],bin[1](仅有这一组)

其他chunk

top chunk

  • 不属于任何bin
  • 在arena中处于最高地址
  • 在没有其他空闲块时,top chunk就会被用于分配
  • 分裂时:一块是请求大小的chunk,另一块余下的chunk将成为新的top chunk

last_remainder

  • 当请求small chunk 大小的内存时,如发生分裂,则剩余的chunk保存为last_remainder

malloc和free大概流程

malloc()工作流程

1.如果size< max fast,在fast bins中寻找fast chunk,如找到则结束

2.如果size in_smallbin_range,在small bins中寻找small chunk,如找到则结束

3.如果size not in_smallbin_range,合并所有fast bin的chunk

4.循环

  • 检查unsorted bin中的last_remainder。如果满足一定条件,则分裂之,将剩余的chunk标记为新的last_remainder
  • 在unsorted bin中搜索,同时进行整理。如果遇到精确大小,则返回,否则就把当前chunk整理到small/large bin中去
  • 在small bin和large bin中搜索最合适的chunk(不一定是精确大小)

5.使用top chunk

free()工作流程

1.如果size< max fast,放入fast bin,结束

2.如果前一个chunk是free的

  • unlink(通过修改fd和bk的数值把chunk从bin中解除出来)前面的chunk
  • 合并两个chunk,并放入unsorted bin

3.如果后一个chunk是top chunk,则将当前chunk并入top chunk

4.如果后面一个chunk是free时,

  • unlink后面的chunk
  • 合并两个chunk,并放入unsorted bin

5.前后chunk都不是free的,放入unsorted bin

free()的实现较为简单,一共有三种可能

符合条件1时放入fast bin中

不符合1且后面为top chunk时并入top chunk

前两者都不符合,先看前后有无free chunk能否合并,后放入unsorted bin

只有在malloc()运行时才会得到large bin或small bin(先申请一个很大的堆,然后free掉,在malloc)

Fastbin attack

  • Fast bin利用技术

    • fast bin为单向链表,结构简单,容易伪造
    • 为了提高效率,安全检查较少
    • 只针对Fast bin大小的chunk,small/large chunk不适用
  • 利用思路

    • 空闲的Fast chunk如果发生溢出被覆盖,则链表指针fd可以被修改
    • 可以通过修改链表指针fd,在Fast bin链表中引入伪造的空闲Fast chunk
    • 下次分配时分配出伪造的Fast chunk
    • 伪造的Fast chunk可以在.bss全局变量处,也可以在栈上
  • 伪造Fast chunk用处

    • 在栈上伪造Fast chunk——覆盖返回地址
    • 在bss上伪造Fast chunk——修改全局变量
    • 在堆上伪造Fast chunk——修改堆上的数据
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值