学习链接
https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/comment-page-1/
http://www.cnblogs.com/alisecurity/p/5486458.html
http://www.cnblogs.com/alisecurity/p/5520847.html
首先还是看看linux内存的布局

实验演示
代码:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
void* threadFunc(void* arg){
printf("Before malloc in thread 1\n");
getchar();
char* addr = (char*) malloc(1000);
printf("After malloc and before free in thread 1\n");
getchar();
free(addr);
printf("After free in thread 1\n");
getchar();
}
int main(int argc, char const *argv[]){
pthread_t t1;
void* s;
int ret;
char* addr;
printf("Welcome to per thread arena example::%d\n", getpid());
printf("Before malloc in main thead\n");
getchar();
addr = (char*) malloc(1000);
printf("After malloc and before free in main thread\n");
getchar();
ret = pthread_create(&t1, NULL, threadFunc, NULL);
if (ret){
printf("Thread creation error\n");
return -1;
}
ret = pthread_join(t1, &s);
if (ret){
printf("Thread join error\n");
return -1;
}
return 0;
}
我用的是kali1.1 32位系统
编译一开始报
pthread1.c:5:22: fatal error: sys/types.h: No such file or directory
安装一下
sudo apt-get install build-essential flex libelf-dev libc6-dev-amd64 binutils-dev libdwarf-dev
继续
/tmp/ccpy3gZb.o: In function `main': pthread1.c:(.text+0xda): undefined reference to `pthread_create' pthread1.c:(.text+0x10d): undefined reference to `pthread_join'
解决方法
pthread 库不是 Linux 系统默认的库,连接时需要使用静态库 libpthread.a,所以在使用pthread_create()创建线程,以及调用 pthread_atfork()函数建立fork处理程序时,需要链接该库。
最终:
gcc pthread1.c -o pthread1 -l pthread
开始运行
Before malloc in main thead
root@kali:~/learn/learnHeap# ./pthread1 Welcome to per thread arena example::22219 Before malloc in main thead
看看内存,是没有heap段的,栈段是有的,当然线程栈也是没有
root@kali:/# cat /proc/22219/maps 08048000-08049000 r-xp 00000000 08:01 1598728 /root/learn/learnHeap/pthread1 08049000-0804a000 rw-p 00000000 08:01 1598728 /root/learn/learnHeap/pthread1 b7551000-b7553000 rw-p 00000000 00:00 0 b7553000-b76f7000 r-xp 00000000 08:01 1311582 /lib/i386-linux-gnu/i686/cmov/libc-2.19.so b76f7000-b76f9000 r--p 001a4000 08:01 1311582 /lib/i386-linux-gnu/i686/cmov/libc-2.19.so b76f9000-b76fa000 rw-p 001a6000 08:01 1311582 /lib/i386-linux-gnu/i686/cmov/libc-2.19.so b76fa000-b76fd000 rw-p 00000000 00:00 0 b76fd000-b7715000 r-xp 00000000 08:01 1311574 /lib/i386-linux-gnu/i686/cmov/libpthread-2.19.so b7715000-b7716000 r--p 00017000 08:01 1311574 /lib/i386-linux-gnu/i686/cmov/libpthread-2.19.so b7716000-b7717000 rw-p 00018000 08:01 1311574 /lib/i386-linux-gnu/i686/cmov/libpthread-2.19.so b7717000-b7719000 rw-p 00000000 00:00 0 b7733000-b7737000 rw-p 00000000 00:00 0 b7737000-b7739000 r--p 00000000 00:00 0 [vvar] b7739000-b773b000 r-xp 00000000 00:00 0 [vdso] b773b000-b775a000 r-xp 00000000 08:01 1311723 /lib/i386-linux-gnu/ld-2.19.so b775a000-b775b000 r--p 0001f000 08:01 1311723 /lib/i386-linux-gnu/ld-2.19.so b775b000-b775c000 rw-p 00020000 08:01 1311723 /lib/i386-linux-gnu/ld-2.19.so bfba5000-bfbc6000 rw-p 00000000 00:00 0 [stack]
以免占用太多的篇幅,将后面不相关的删掉了
After malloc and before free in main thread
08048000-08049000 r-xp 00000000 08:01 1598728 /root/learn/learnHeap/pthread1 08049000-0804a000 rw-p 00000000 08:01 1598728 /root/learn/learnHeap/pthread1 09d74000-09d95000 rw-p 00000000 00:00 0 [heap]
可以看到出现了heap段,而且是132KB,我们程序只申请了1000(B)字节

那下次再malloc,直接从这里搞就行了
如果这132KB都用完了,就增大这个heap段的大小,调整program break(可看上面内存布局图片)的位置就行了
After free in main thread
可以看到内存是没有马上给回操作系统
而是由glibc 的malloc库函数加以管理。它会将释放的chunk添加到main arenas的bin(这是一种用于存储同类型free chunk的双链表数据结构),记录空闲空间的freelist数据结构称之为bins。之后当用户再次调用malloc申请堆空间的时候,glibc malloc会先尝试从bins中找到一个满足要求的chunk,如果没有才会向操作系统申请新的堆空间。
08048000-08049000 r-xp 00000000 08:01 1598728 /root/learn/learnHeap/pthread1 08049000-0804a000 rw-p 00000000 08:01 1598728 /root/learn/learnHeap/pthread1 09d74000-09d95000 rw-p 00000000 00:00 0 [heap]
Before malloc in thread 1
thread1的堆还没分配,但栈空间已经分配了
08048000-08049000 r-xp 00000000 08:01 1598728 /root/learn/learnHeap/pthread1 08049000-0804a000 rw-p 00000000 08:01 1598728 /root/learn/learnHeap/pthread1 09d74000-09d95000 rw-p 00000000 00:00 0 [heap] b6d11000-b6d12000 ---p 00000000 00:00 0 b6d12000-b7514000 rw-p 00000000 00:00 0 [stack:24056]
After malloc and before free in thread 1
root@kali:/# cat /proc/24050/maps 08048000-08049000 r-xp 00000000 08:01 1598728 /root/learn/learnHeap/pthread1 08049000-0804a000 rw-p 00000000 08:01 1598728 /root/learn/learnHeap/pthread1 09d74000-09d95000 rw-p 00000000 00:00 0 [heap] b6c00000-b6c21000 rw-p 00000000 00:00 0 b6c21000-b6d00000 ---p 00000000 00:00 0 b6d11000-b6d12000 ---p 00000000 00:00 0 b6d12000-b7514000 rw-p 00000000 00:00 0 [stack:24056]
可以看到,比没malloc前多了这两行
b6c00000-b6c21000 rw-p 00000000 00:00 0 b6c21000-b6d00000 ---p 00000000 00:00 0
同时从这个区域的起始地址可以看出,它并不是通过brk分配的,而是通过mmap分配
因为地址是b6c21000,地址是比较高的,而brk分配的内存的地址是比较低的,就在程序后面一点的地方
那么main函数的堆区是brk分配,线程的就mmap分配
总共的话是1MB,算一下就知道

但是实际根据内存属性有分成了两端,还是只有132KB可读写的

这里应该也是一次性分配多一点,那不可读不可写的给可能其他线程(如果有的话)malloc的时候用的?或者当前thread1的132KB不够用的时候用的吧
笔记:作者还说,如果一次性申请超过128字节( lets say malloc(132*1024)),而且没有足够的arena能够满足用户的需求时,系统使用的是mmap系统调用来申请内存,而不管是从main arena还是thread arena申请的
After free in thread 1
最后跟main的释放是一样的,释放只是给了thread arenas bin
root@kali:/# cat /proc/24050/maps 08048000-08049000 r-xp 00000000 08:01 1598728 /root/learn/learnHeap/pthread1 08049000-0804a000 rw-p 00000000 08:01 1598728 /root/learn/learnHeap/pthread1 09d74000-09d95000 rw-p 00000000 00:00 0 [heap] b6c00000-b6c21000 rw-p 00000000 00:00 0 b6c21000-b6d00000 ---p 00000000 00:00 0 b6d11000-b6d12000 ---p 00000000 00:00 0 b6d12000-b7514000 rw-p 00000000 00:00 0 [stack:24056]
Arena
arena数量
在上面的例子中main thread和thread1有自己独立的arena,那么是不是无论有多少个线程,每个线程都有自己独立的arena呢?这样太浪费内存了。事实上,arena的个数是跟系统中处理器核心个数相关的
国内的翻译是给作者的公式+1了,不过根据下面的描述也对
For 32 bit systems: Number of arena = 2 * number of cores + 1. For 64 bit systems: Number of arena = 8 * number of cores + 1.
Multiple Arena
那假如我的程序有4个线程(main thread(主线程) 和 3 个 user thread),我运行在32位的单核系统上,那我这系统只能有2+1=3个arena ,那user thread就只能共享arena咯
那怎么共享的呢
首先,主线程首次malloc的话,直接给他arena,不需要其他条件
thread 1 和 thread 2也是第一次申请的话,也是创建一个新的arena给他,这时候各个线程和arena是一一对应的
假如thread 3也是第一次申请,算一下,arena已经达到上限,只能跟前面的共用咯,那前面3个用哪个呢?
首先循环可用的arena,并且再循环的时候尝试锁住它,如果成功就返回那个arena给用户(比如说是main arena),但如果失败就阻塞,知道有可用的arena为止
现在thread3第二次malloc,首先先尝试上一次拿到额共享的arena,有空闲的就直接用,否则就阻塞,直到共享的 arena有空闲的空间为止
Multiple Heaps
作者在‘glibc malloc’源码中看到有3种数据结构
heap_info
就像是heap Header,一个thread arena可以有多个heap,所以为了便于管理就都有一个heap header
那么在什么情况下一个thread arena会包含多个heaps呢?在当前heap不够用的时候,malloc会通过系统调用mmap申请新的堆空间,新的堆空间会被添加到当前thread arena中,便于管理。
typedef struct _heap_info
{
mstate ar_ptr; /* Arena for this heap. */
struct _heap_info *prev; /* Previous heap. */
size_t size; /* Current size in bytes. */
size_t mprotect_size; /* Size in bytes that has been mprotected
PROT_READ|PROT_WRITE. */
/* Make sure the following data is properly aligned, particularly
that sizeof (heap_info) + 2 * SIZE_SZ is a multiple of
MALLOC_ALIGNMENT. */
char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK];
} heap_info;
malloc_state
通俗来说就是arena Header
一个线程可以有多个堆,但是这个线程的所有这些堆,只有一个arena Header
这个Header包含了bins,top chunk,last remainder chunk…
struct malloc_state
{
/* Serialize access. */
mutex_t mutex;
/* Flags (formerly in max_fast). */
int flags;
/* Fastbins */
mfastbinptr fastbinsY[NFASTBINS];
/* Base of the topmost chunk -- not otherwise kept in a bin */
mchunkptr top;
/* The remainder from the most recent split of a small request */
mchunkptr last_remainder;
/* Normal bins packed as described above */
mchunkptr bins[NBINS * 2 - 2];
/* Bitmap of bins */
unsigned int binmap[BINMAPSIZE];
/* Linked list */
struct malloc_state *next;
/* Linked list for free arenas. */
struct malloc_state *next_free;
/* Memory allocated from the system in this arena. */
INTERNAL_SIZE_T system_mem;
INTERNAL_SIZE_T max_system_mem;
};
malloc_chunk
即chunk Header,一个heap被分成很多个chunk,一个chunk的大小是跟用户malloc的大小相关的,每个chunk都有自己的Header
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. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
作者笔记:
main arena没有多个heap,因此没有heap info 这个结构,但它需要更多空间就通过brk申请更多,直到memory mapping段
和thread arena不同, main arena的arena header不是brk的heap段的一部分,他是一个全局变量,在libc的data段中
复制一下main arena和thread arena只有单个堆的示意图
可以看到不同的是main arena的arena Header(malloc state)是在libc的data段
而且main arena没有heap info结构
thread arena则在堆的地址上

那么有多个heap段的Thead arena呢

thread arena只含有一个malloc_state(即arena header),却有两个heap_info(即heap header)。
可以看到malloc state和heap info形成一个循环单向链表
原来那个堆的top chunk变成了free chunk
后面那些其实做了笔记的,但由于意外,没保存,所以就这样吧
其实动态调试去理解更好

