[译] malloc中的系统调用brk和mmap

阅读本文前你可能已经知道,malloc通过系统调用的方式从操作系统申请内存。事实上,malloc内部是通过系统调用 brkmmap 来申请内存的。如下面的进程虚拟内存布局图所示,mmap对应 Memory Mapping Segment ,brk对应 Heap

brk

brk 通过增加program break的位置( brk )从内核申请(非零值初始化的)内存。一开始,堆段(heap segment)的起始位置( start_brk )和结束位置( brk )指向同一个位置:

  • ASLR (Address Space Layout Randomization)关闭时, start_brk 和brk同时指向 data/bss 段的结束位置( end_data )。
  • 当ASLR打开时, start_brk 和brk同时指向 data/bss 段的结束位置( end_data )再加上一个随机的brk偏移。

上面的进程虚拟内存布局图展示了,start_brk是堆段的开始位置,brk(program break)则是堆段的结束位置。

例子:

/* sbrk, brk 例子 */
#include 
#include 
#include 

int main()
{
        void *curr_brk, *tmp_brk = NULL;

        printf("Welcome to sbrk example:%d\n", getpid());

        /* sbrk(0) 获取当前 program break 位置 */
        tmp_brk = curr_brk = sbrk(0);
        printf("Program Break Location1:%p\n", curr_brk);
        getchar();

        /* 使用 brk 增加 program break 位置 */
        brk(curr_brk+4096);

        curr_brk = sbrk(0);
        printf("Program break Location2:%p\n", curr_brk);
        getchar();

        /* 使用 brk 减小 program break 位置 */
        brk(tmp_brk);

        curr_brk = sbrk(0);
        printf("Program Break Location3:%p\n", curr_brk);
        getchar();

        return 0;
}

在增加program break之前,输出如下:

sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/syscalls$ ./sbrk 
Welcome to sbrk example:6141
Program Break Location1:0x804b000
...
sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/syscalls$ cat /proc/6141/maps
...
0804a000-0804b000 rw-p 00001000 08:01 539624     /home/sploitfun/ptmalloc.ppt/syscalls/sbrk
b7e21000-b7e22000 rw-p 00000000 00:00 0 
...

  • start_brk=brk=end_data=0x804b000

此时没有堆段。

译者yoko注,这里通过 cat /proc//maps 的方式查看进程映射的内存区域,输出的含义下文会具体讲

在增加program break之后,输出如下:

sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/syscalls$ ./sbrk 
Welcome to sbrk example:6141
Program Break Location1:0x804b000
Program Break Location2:0x804c000
...
sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/syscalls$ cat /proc/6141/maps
...
0804a000-0804b000 rw-p 00001000 08:01 539624     /home/sploitfun/ptmalloc.ppt/syscalls/sbrk
0804b000-0804c000 rw-p 00000000 00:00 0          [heap]
b7e21000-b7e22000 rw-p 00000000 00:00 0 
...

start_brk=end_data=0x804b000
brk=0x804c000

可以观察到堆段。

其中 0804b000-0804c000 rw-p 00000000 00:00 0 [heap] 的含义:

  • 0804b000-0804c000 是这个堆段的虚拟地址范围。
  • rw-p 标准的含义是 Read, Write, NoeXecute, Private
  • 00000000 是文件偏移量,由于并没有映射任何文件,所以为零
  • 00:00 是major/minor device number,由于并没有映射任何文件,所以为零
  • 0 是inode,由于并没有映射任何文件,所以为零
  • [heap] 是堆段

mmap

malloc使用 mmap 创建一个私有匿名的映射段。这个映射段的主要目的是申请一块(零值初始化的)新内存,并且这块内存只能被调用的这个进程独占使用。

例子:

/* 使用mmap系统调用做私有匿名映射的例子 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 

void static inline errExit(const char* msg)
{
        printf("%s failed. Exiting the process\n", msg);
        exit(-1);
}

int main()
{
        int ret = -1;
        printf("Welcome to private anonymous mapping example::PID:%d\n", getpid());
        printf("Before mmap\n");
        getchar();
        char* addr = NULL;
        addr = mmap(NULL, (size_t)132*1024, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        if (addr == MAP_FAILED)
                errExit("mmap");
        printf("After mmap\n");
        getchar();

        /* Unmap mapped region. */
        ret = munmap(addr, (size_t)132*1024);
        if(ret == -1)
                errExit("munmap");
        printf("After munmap\n");
        getchar();
        return 0;
}

调用mmap之前:如下输出我们可以看到,只有属于 libc.sold-linux.so 共享库的内存映射段。

sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/syscalls$ cat /proc/6067/maps
08048000-08049000 r-xp 00000000 08:01 539691     /home/sploitfun/ptmalloc.ppt/syscalls/mmap
08049000-0804a000 r--p 00000000 08:01 539691     /home/sploitfun/ptmalloc.ppt/syscalls/mmap
0804a000-0804b000 rw-p 00001000 08:01 539691     /home/sploitfun/ptmalloc.ppt/syscalls/mmap
b7e21000-b7e22000 rw-p 00000000 00:00 0 
...

调用mmap之后:如下输出我们可以观察到,我们mmap映射的内存段( b7e00000–b7e21000 ,大小是132KB)和已经存在的内存映射段( b7e21000–b7e22000 )合并了(变成 b7e00000-b7e22000 )。

sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/syscalls$ cat /proc/6067/maps
08048000-08049000 r-xp 00000000 08:01 539691     /home/sploitfun/ptmalloc.ppt/syscalls/mmap
08049000-0804a000 r--p 00000000 08:01 539691     /home/sploitfun/ptmalloc.ppt/syscalls/mmap
0804a000-0804b000 rw-p 00001000 08:01 539691     /home/sploitfun/ptmalloc.ppt/syscalls/mmap
b7e00000-b7e22000 rw-p 00000000 00:00 0 
...

其中 b7e00000-b7e22000 rw-p 00000000 00:00 0 的含义:

b7e00000-b7e22000
rw-p is Flags (Read, Write, NoeXecute, Private)
00000000
00:00
0

调用munmap之后:如下输出我们可以看到,mmap映射的内存段已经被解除映射了,换言之,相应的已归还给操作系统。

sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/syscalls$ cat /proc/6067/maps
08048000-08049000 r-xp 00000000 08:01 539691     /home/sploitfun/ptmalloc.ppt/syscalls/mmap
08049000-0804a000 r--p 00000000 08:01 539691     /home/sploitfun/ptmalloc.ppt/syscalls/mmap
0804a000-0804b000 rw-p 00001000 08:01 539691     /home/sploitfun/ptmalloc.ppt/syscalls/mmap
b7e21000-b7e22000 rw-p 00000000 00:00 0 
...

注意:我们以上所做实验,ASLR是关闭的。

英文原文地址: https://sploitfun.wordpress.com/2015/02/11/syscalls-used-by-malloc/

原文链接: https://pengrl.com/p/20032/

原文出处: yoko blog ( https://pengrl.com )

原文作者: yoko ( https://github.com/q191201771 )

版权声明:本文欢迎任何形式转载,转载时完整保留本声明信息(包含原文链接、原文出处、原文作者、版权声明)即可。本文后续所有修改都会第一时间在原始地址更新。