我们使用此代码遇到段错误:
#include <fcntl.h>
#include <sys/mman.h>
#include <stdio.h>
#define CHUNKSIZE 4096
int main(int argc, char **argv) {
printf("Hallo!\n"); // does not segfault without this line
void* first_chunk = mmap(NULL, CHUNKSIZE, PROT_NONE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
void* next_chunk_addr = (void*) ((char*)first_chunk + CHUNKSIZE);
mmap(next_chunk_addr, CHUNKSIZE, PROT_NONE, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, 0, 0);
printf("Bumm!\n"); // segfaults
}
即使第二个 mmap 调用的地址无效,我相信我应该得到 MAP_FAILED 而不是损坏的堆栈。
GDB 给了我这个:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a94104 in _IO_file_xsputn () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) bt
#0 0x00007ffff7a94104 in _IO_file_xsputn () from /lib/x86_64-linux-gnu/libc.so.6
#1 0x00007ffff7a8ad79 in puts () from /lib/x86_64-linux-gnu/libc.so.6
#2 0x0000000000400591 in main (argc=1, argv=0x7fffffffdf28) at test.cpp:14
(gdb) x/i $rip
=> 0x7ffff7a94104 <_IO_file_xsputn+324>: mov %dl,(%r8,%rax,1)
为什么他试图从 0x7ffff7ff8000 读取,这与他应该打印的内容无关?
在另一台机器上,我们得到了带有相关代码的堆栈跟踪:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7b0985a in mmap64 () at ../sysdeps/unix/syscall-template.S:81
81 ../sysdeps/unix/syscall-template.S: No such file or directory.
这可能与内核方面有关吗?
这发生在使用 gcc 和 clang 的三个不同的 Linux 系统上。 OS X 下没有任何反应。
最佳答案
内核的行为完全符合预期。请注意 mmap(2)
documentation 中的这句话在 MAP_FIXED
上:
If the memory region specified by
addr
andlen
overlaps pages of any existing mapping(s), then the overlapped part of the existing mapping(s) will be discarded.
如果您执行strace(1)
的程序,你会看到这正是发生的事情:
...
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0710755000
write(1, "Hallo!\n", 7) = 7
mmap(NULL, 4096, PROT_NONE, MAP_SHARED|MAP_ANONYMOUS, 0, 0) = 0x7f0710754000
mmap(0x7f0710755000, 4096, PROT_NONE, MAP_SHARED|MAP_FIXED|MAP_ANONYMOUS, 0, 0) = 0x7f0710755000
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV (core dumped) +++
第一次调用printf()
(如您所见,编译器优化为对puts()
的调用)使用malloc分配一些内存()
(因为 stdout
已缓冲),它调用 mmap()
。然后,程序调用 mmap(NULL)
并获取紧邻 printf
分配的内存之前的页面。第二次调用 mmap()
会在已分配的页面之上分配一个新页面,将其清零并破坏 malloc
的内部数据结构。随后对 printf()
(实际上是 puts()
)的调用在尝试附加到它认为已分配的内存缓冲区并访问这些损坏的数据结构时会崩溃。
关于c - mmap 导致堆栈损坏,涉及内核吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24470834/