16

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

 4 years ago
source link: https://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.
neoserver,ios ssh client

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

发表于 2020-03-16 | 分类于 计算机系统
| 字数统计: 1.3k

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

进程虚拟内存布局图

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)则是堆段的结束位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* 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之前,输出如下:

1
2
3
4
5
6
7
8
9
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之后,输出如下:

1
2
3
4
5
6
7
8
9
10
11
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]是堆段

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* 使用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共享库的内存映射段。

1
2
3
4
5
6
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)。

1
2
3
4
5
6
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是major/minor device number,由于并没有映射任何文件,所以为零
  • 0是inode,由于并没有映射任何文件,所以为零

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

1
2
3
4
5
6
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/

本文完,作者yoko,尊重劳动人民成果,转载请注明原文出处: https://pengrl.com/p/20032/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK