操作系统是否为堆栈或其他东西保留了固定数量的有效虚拟空间?我是否能够仅通过使用大局部变量来产生堆栈溢出?
我写了一个小C程序来测试我的假设。它在 X86-64 CentOS 6.5 上运行。
#include <string.h>
#include <stdio.h>
int main()
{
int n = 10240 * 1024;
char a[n];
memset(a, 'x', n);
printf("%x\n%x\n", &a[0], &a[n-1]);
getchar();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
运行程序给出&a[0] = f0ceabe0和&a[n-1] = f16eabdf
proc 映射显示堆栈: 7ffff0cea000-7ffff16ec000. (10248 * 1024B)
然后我尝试增加 n = 11240 * 1024
运行程序给出&a[0] = b6b36690和&a[n-1] = b763068f
proc 映射显示堆栈: 7fffb6b35000-7fffb7633000. (11256 * 1024B)
ulimit -s10240在我的电脑上打印。
如您所见,在这两种情况下,堆栈大小都大于ulimit -s给出的大小。并且堆栈随着更大的局部变量而增长。堆栈顶部以某种方式减少了 3-5kB &a[0](AFAIK,红色区域为 128B)。
那么这个堆栈映射是如何分配的呢?
从这篇文章可以看出这FS:[0x28]是一个堆栈金丝雀。我在这个函数上使用 GCC 生成相同的代码,
void foo () {
char a[500] = {};
printf("%s", a);
}
Run Code Online (Sandbox Code Playgroud)
具体来说,我得到这个程序集..
0x000006b5 64488b042528. mov rax, qword fs:[0x28] ; [0x28:8]=0x1978 ; '(' ; "x\x19"
0x000006be 488945f8 mov qword [local_8h], rax
...stuff...
0x00000700 488b45f8 mov rax, qword [local_8h]
0x00000704 644833042528. xor rax, qword fs:[0x28]
0x0000070d 7405 je 0x714
0x0000070f e85cfeffff call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
; CODE XREF from 0x0000070d (sym.foo)
0x00000714 c9 leave
0x00000715 c3 ret
Run Code Online (Sandbox Code Playgroud)
什么是设置值fs:[0x28]?内核,还是 GCC 投入了代码?你能在内核中显示代码,或者编译成设置的二进制文件fs:[0x28]吗?金丝雀是在启动时重新生成的,还是在进程生成时重新生成的?这是在哪里记录的?
例如,在 OSX 上,它甚至小于 512k。
考虑到应用程序不使用递归并且不分配大量堆栈变量,是否有任何推荐的大小?
我知道这个问题太广泛了,它在很大程度上取决于使用情况,但仍然想问,因为我想知道这个庞大的数字背后是否有一些隐藏的/内部/系统原因。
我想知道,因为我打算在我的应用程序中将堆栈大小更改为 512 KiB - 这听起来仍然是一个巨大的数字,但它比 8MiB 小得多 - 并且会导致进程的虚拟内存显着减少,因为我有很多线程(I/O)。
我也知道这并没有什么坏处,这里有很好的解释:pthreads 的默认堆栈大小
在开始编写自己的代码之前,我想知道是否有 GNU/Linux 软件能够输出类似于QNX 的showmem.
对于正在运行的每个进程的每个线程,我希望看到内存消耗分为以下几类:
Process listing (Total, Code, Data, Heap, Stack, Other)
319488 1024000 24587 167936 24576 0 4103 devc-con-hid (thread 2)
0 0 0 0 4096 0 4103 devc-con-hid (thread 2)
0 0 0 0 20480 0 4103 devc-con-hid (thread 1)
0 102400 8192 0 0 0 4103 devc-con-hid (proc/boot/devc-con-hid)
0 0 16384 0 0 0 4103 devc-con-hid (proc/boot/libc.so.3)
0 0 0 0 0 ( 36864) 4103 devc-con-hid (/dev/mem)
Run Code Online (Sandbox Code Playgroud)
有什么可以使这成为可能吗?
我正在研究 ELF 规范(http://www.skyfree.org/linux/references/ELF_Format.pdf),关于程序加载过程我不清楚的一点是堆栈是如何初始化的,以及什么初始页面大小是。这是测试(在 Ubuntu x86-64 上):
$ cat test.s
.text
.global _start
_start:
mov $0x3c,%eax
mov $0,%edi
syscall
$ as test.s -o test.o && ld test.o
$ gdb a.out -q
Reading symbols from a.out...(no debugging symbols found)...done.
(gdb) b _start
Breakpoint 1 at 0x400078
(gdb) run
Starting program: ~/a.out
Breakpoint 1, 0x0000000000400078 in _start ()
(gdb) print $sp
$1 = (void *) 0x7fffffffdf00
(gdb) info proc map
process 20062
Mapped address spaces:
Start Addr End Addr Size Offset …Run Code Online (Sandbox Code Playgroud) 我在 Manjaro 上使用bash5.0.16(和bash-completion2.10)。今天,我在移动一些文件时心不在焉地按了 Tab 键,并收到了一条意想不到的消息:
[charles@wirepile some_dir]$ mv ~down*** stack smashing detected ***: <unknown> terminated
Run Code Online (Sandbox Code Playgroud)
我必须^C重新获得对外壳的控制。
看来,每当在波浪号字符上调用制表符补全(无论后面有或没有其他字符)时,我的(最新的 Manjaro)bash 版本都会触发堆栈保护器。以下确实有效:
[charles@wirepile some_dir]$ ls ~/
.this_dir/ .that_file another_file
Run Code Online (Sandbox Code Playgroud)
我的 中不应该有任何不寻常的行.bashrc,除非 Manjaro 默认自定义它。
我想这是某种缓冲区溢出,应该被视为一个错误——这是故意的吗?我应该联系开发人员还是包维护人员?
我也很好奇——有人能重现这个问题吗?
我创建了一个简单的 C 程序,每次在 GDB 中加载它时,我都会看到分配给程序指令的相同内存地址。例如,一个函数what()总是在内存位置 0x000055555555472d 加载。实际上,每次执行的堆栈都完全相同(不仅是堆栈的内容,还包括 rsp 指向的内存地址。
我知道可以通过将“/proc/sys/kernel/randomize_va_space”设置为 0 在 Linux 中禁用 ASLR,但我的 Debian 系统中的值为 2。
root@Sierra ~ % cat /proc/sys/kernel/randomize_va_space
2
Run Code Online (Sandbox Code Playgroud)
根据我对 ASLR 的理解,这些地址应该在每次运行时随机化。我的问题是为什么会发生这种情况?我做错了什么吗?
我一直在仔细阅读 clone() 的 linux 手册页,我了解 clone() 包装器和“原始”系统调用之间的区别。但我不明白的是为什么父进程需要为子进程分配一个堆栈,即使包装器中没有使用 CLONE_VM。
如果不使用 CLONE_VM,包装器是否会简单地忽略堆栈参数?为什么需要它呢?原始系统调用允许它为空,这是有道理的,但我不明白为什么包装器需要这个。即使您不告诉它,包装器也会让孩子和父母共享内存吗?
我的默认堆栈大小(根据 ulimit -s)是 8192 kB,所以当我尝试运行它时,自然会出现段错误下面的代码。此外,自然地,如果我执行'ulimit -s 9000',它可以正常工作。但是,当我执行 'ulimit -s unlimited' 时,代码再次出现段错误。任何想法这里发生了什么?
如果有用,我正在使用内核 4.19.0-6 和 gcc 版本 Debian 8.3.0-6 运行 Debian 10。
#include <iostream>
#include <unistd.h>
#include <cstdlib>
void* wait_exit(void*)
{
char bob[8193*1024];
return 0;
}
int main()
{
pthread_t t_exit;
int ret;
if((ret = pthread_create(&t_exit,NULL,wait_exit,NULL)) !=0)
{
std::cout<<"Cannot create exit thread: "<<ret<<std::endl;
}
std::cout<<"Made thread"<<std::endl;
sleep(5);
return 0;
}
Run Code Online (Sandbox Code Playgroud) 我目前正在阅读Robert Love 的Linux Kernel Development。
在“15 进程地址空间”一章中,他打印了进程的内存映射。
user@machine:~$ pmap 1424
#all the processes mapped memory (skipped for readability)
bfffe000 (8KB) rwxp (0:00 0) [ stack ]
Run Code Online (Sandbox Code Playgroud)
最后一行显示堆栈(随着它向下增长)。
他现在说:
堆栈自然是可读、可写和可执行的——否则没有多大用处。
据我所知,我们将堆栈用于数据和代码地址(函数及其参数/变量)。
我不明白为什么堆栈必须是可执行的?
我在我的一台计算机上运行 Arch Linux,64 位最新更新。我目前是一名计算机科学专业的学生,昨天我们进行了一项测试,我们将使用链表实现动态堆栈。我现在有兴趣了解我的计算机中的堆栈是如何构建的,但是我无法在我的 Arch Linux 计算机上找到任何带有注释的“stack.c”。堆栈编程位于何处?我了解堆栈如何创建内存,但我想实际查看代码并可能自己玩弄它。