0x00 前言
决定学习堆溢出很久了但却因为内心对源码的恐惧一直不愿意解出这块内容,于是在实习面试的时候被问到linux的内存管理一脸懵逼啥都不会。。。。痛定思痛利用一周末时间将堆的实现硬着头皮看了一遍,现在记录如下。
参考内容:
CTFwiki 深入理解堆的实现
Glibc内存管理Ptmalloc2源代码分析
0x01 操作系统内存分配的相关函数
brk()系统调用与sbrk()库函数
内核数据结构 mm_struct (在include/linux/mm_types.h中定义)中的成员变量 start_code 和 end_code 是进程代码段的起始和终止地址,start_data 和 end_data 是进程数据段的起始和终止地址,start_stack 是进程堆栈 段起始地址,start_brk 是进程动态内存分配起始地址(堆的起始地址),还有一个 brk(堆 的当前最后地址),就是动态内存分配当前的终止地址。。brk()是一个非常简单的系统调用, 只是简单地改变 mm_struct 结构的成员变量 brk 的值,从而达到改变堆大小的目的。
这两个函数的定义:
#include <unistd.h>
int brk(void *addr); //参数为堆的新边界
void *sbrk(intptr_t increment); //参数 increment 为 0 时,sbrk()返回的是进程的当前 brk 值, increment 为正数时扩展 brk 值,当 increment 为负值时收缩 brk 值
brk()系统调用源码分析:待补充
mmap()&munmap()
mmap()函数将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的 大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。munmap 执行相反的操 作,删除特定地址区域的对象映射。
函数定义如下:
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);
源码分析:待补充
参数说明:
addr:映射区的开始地址
length:映射区长度
prot:期望的内存保护标志,不能与文件的打开模式冲突。
- PROT_EXEC //页内容可以被执行,ptmalloc 中没有使用
- PROT_READ //页内容可以被读取,ptmalloc 直接用 mmap 分配内存并立即返回给用户时 设置该标志
- PROT_WRITE //页可以被写入,ptmalloc 直接用 mmap 分配内存并立即返回给用户时设 置该标志
- PROT_NONE //页不可访问,ptmalloc 用 mmap 向系统“批发”一块内存进行管理时设置 该标志
flags:指定映射对象的类型,映射选项和映射页是否可以共享。
- MAP_FIXED //使用指定的映射起始地址,如果由 start 和 len 参数指定的内存区重叠于现 存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。Ptmalloc 在回收从系统中“批发”的内存时设置该标志
- MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。 这个标志和以上标志是互斥的,只能使用其中一个。Ptmalloc每次调用mmap时都设置该标记
- MAP_NORESERVE //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改 的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信 号。Ptmalloc 向系统“批发”内存块时设置该标志。
- MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。Ptmalloc 每次调用 mmap 都设置该标志。
fd:有效的文件描述词。如果 MAP_ANONYMOUS 被设定,为了兼容问题,其值应为-1。
offset:被映射对象内容的起点。
0x02 堆的数据结构
1、Main_arena与non_main_arena
Main_arena:
主分配区,每个进程可以自主分配的内存区域。每个进程在向系统申请内存时,系统都会给它一段较大的内存,这段内存成为arena,以后再有申请内存的请求时,会直接从arena中获取内存,而不用向系统申请,从而提高效率。
non_main_arena:
非主分配区。为了能够支持多线程,增加了non_main_arena支持,non_main_arena和main_arena作用相同,提高线程申请内存的效率。
批发与零售关系
主分配 区可以访问进程的 heap 区域和 mmap 映射区域,也就是说主分配区可以使用 sbrk 和 mmap 向操作系统申请虚拟内存。而非主分配区只能访问进程的 mmap 映射区域,主分配区每 次使用 mmap()向操作系统“批发”HEAP_MAX_SIZE(32 位系统上默认为 1MB,64 位系统默 认为 64MB)大小的虚拟内存,当用户向非主分配区请求分配内存时再切割成小块“零售” 出去,毕竟系统调用是相对低效的,直接从用户空间分配内存快多了
2、Chunk
数据结构定义:
/*
This struct declaration is misleading (but accurate and necessary).
It declares a "view" into memory allowing access to necessary
fields at known offsets from a given base. See explanation below.
*/
struct malloc_chunk {
INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */