文章目录
堆概述
参考:https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/heap-overview/
1.堆概述
1.1 堆
在程序运行过程中,堆可以提供动态分配的内存,允许程序申请大小未知的内存。堆其实就是程序虚拟地址空间的一块连续的线性区域,它由低地址向高地址方向增长。
1.2 堆管理器
我们一般称管理堆的那部分程序为堆管理器。
堆管理器处于用户程序与内核中间,主要做以下工作
- 响应用户的申请内存请求,向操作系统申请内存,然后将其返回给用户程序。同时,为了保持内存管理的高效性,内核一般都会预先分配很大的一块连续的内存,然后让堆管理器通过某种算法管理这块内存。只有当出现了堆空间不足的情况,堆管理器才会再次与操作系统进行交互。
- 管理用户所释放的内存。一般来说,用户释放的内存并不是直接返还给操作系统的,而是由堆管理器进行管理。这些释放的内存可以来响应用户新申请的内存的请求。
Linux 中早期的堆分配与回收由 Doug Lea 实现,但它在并行处理多个线程时,会共享进程的堆内存空间。因此,为了安全性,一个线程使用堆时,会进行加锁。然而,与此同时,加锁会导致其它线程无法使用堆,降低了内存分配和回收的高效性。同时,如果在多线程使用时,没能正确控制,也可能影响内存分配和回收的正确性。Wolfram Gloger 在 Doug Lea 的基础上进行改进使其可以支持多线程,这个堆分配器就是 ptmalloc 。在 glibc-2.3.x. 之后,glibc 中集成了 ptmalloc2。
目前 Linux 标准发行版中使用的堆分配器是 glibc 中的堆分配器:ptmalloc2。ptmalloc2 主要是通过 malloc/free 函数来分配和释放内存块。
需要注意的是,在内存分配与使用的过程中,Linux 有这样的一个基本内存管理思想,只有当真正访问一个地址的时候,系统才会建立虚拟页面与物理页面的映射关系。 所以虽然操作系统已经给程序分配了很大的一块内存,但是这块内存其实只是虚拟内存。只有当用户使用到相应的内存时,系统才会真正分配物理页面给用户使用。
所有堆管理器:
•dlmalloc – General purpose allocator
•ptmalloc2 – glibc (敲黑板)
•jemalloc – FreeBSD and Firefox
•tcmalloc – Google libumem – Solaris
堆管理器并非由操作系统实现,而是由libc.so.6链接库实现。 封装了一些系统调用,为用户提供方便的动态内存分配接口的同时,力求高效地管理由系统调用申请来的内存。
1.3 概念示图

2.堆的基本操作
2.1 申请内存malloc
malloc
是 __libc_malloc
的别名:
strong_alias (__libc_malloc, __malloc) strong_alias (__libc_malloc, malloc)
函数原型:
void *__libc_malloc (size_t bytes)
param1: 申请的堆块的大小
return: 指向申请的堆块的数据部分的起始位置
备注: 还对一些特殊情况进行处理:
bytes = 0, 返回一个最小的chunk,大部分32位系统最小chunk为16字节,64位系统返回24或者32位。
bytes < 0, 由于大部分系统,size_t是无符号数,那么程序就会申请很大的空间,但是系统没有那么多的内存可以分配,因此大多数情况都会失败
示例图:

2.2 释放内存free
free()
为 __libc_free()
的别名:
strong_alias