12

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

 4 years ago
source link: http://www.pengrl.com/p/20032/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

aUVjaqn.png!web

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 <stdio.h>
#include <unistd.h>
#include <sys/types.h>

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/<pid>/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 <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

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 )

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

ruq2Mbu.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK