使用 setrlimit() 设置堆栈大小并引发堆栈溢出/段错误
Posted
技术标签:
【中文标题】使用 setrlimit() 设置堆栈大小并引发堆栈溢出/段错误【英文标题】:Set stack size with setrlimit() and provoke a stack overflow/segfault 【发布时间】:2011-05-06 07:18:09 【问题描述】:在下面给出的示例中,我尝试将堆栈大小设置为 1kb。
为什么现在可以在foo()
中分配大小为8kb
的整数数组?
#include <stdio.h>
#include <sys/resource.h>
void foo(void);
int main()
struct rlimit lim = 1024, 1024;
if (setrlimit(RLIMIT_STACK, &lim) == -1)
return 1;
foo();
return 0;
void foo()
unsigned ints[2048];
printf("foo: %u\n", ints[2047]=42);
【问题讨论】:
谢谢,我现在很想知道为什么这不像 man(2) setrlimit 中所宣传的那样有效。幸运的是,gcc 允许您指定堆栈大小:) 一个问题被收藏的次数多于被点赞的次数——此时。很有趣。 【参考方案1】:我认为setrlimit
移动了“资源指针”,但在您exec
一个新的程序副本之前不会应用新的限制。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
void foo(int chk)
unsigned ints[2048];
ints[2047] = 42;
printf("foo %d: %u\n", chk, ints[2047]);
int main(int argc, char **argv)
char *newarg[] = "argv[0]", "one", "two" ;
char *newenv[] = NULL ;
struct rlimit lim;
newarg[0] = argv[0];
getrlimit(RLIMIT_STACK, &lim);
printf("lim: %d / %d\n", (int)lim.rlim_cur, (int)lim.rlim_max);
switch (argc)
case 1: /* first call from command line */
lim.rlim_cur = 65536;
lim.rlim_max = 65536;
if (setrlimit(RLIMIT_STACK, &lim) == -1) return EXIT_FAILURE;
newarg[2] = NULL;
foo(1);
execve(argv[0], newarg, newenv);
break;
case 2: /* second call */
lim.rlim_cur = 1024;
lim.rlim_max = 1024;
if (setrlimit(RLIMIT_STACK, &lim) == -1) return EXIT_FAILURE;
foo(2);
execve(argv[0], newarg, newenv);
break;
default: /* third call */
foo(3);
break;
return 0;
还有一个测试运行:
$ ./a.out 林:8388608 / -1 富 1:42 林:65536 / 65536 富 2:42 被杀我不知道为什么进程在打印限制之前(以及在调用 foo 之前)被终止。
【讨论】:
我怀疑类似,只是尝试了fork()
,没有区别。我不明白为什么 setrlimit() 只影响通过 exec
而不是父进程产生的进程,但似乎确实如此。
使用 GDB 我在“foo 2: 42”行之后得到“程序正常退出” - 没有被杀死,没有段错误
@tur1ng:尝试在 main 开头添加 newarg[0] = argv[0];
。我怀疑您的二进制文件不称为“a.out”
这是正确的 a.out。即使是ulimit -s 1
也不会导致错误。
@pmg,你用的是什么内核版本/操作系统专用酱?我想我们可能在这里谈论一个移动的目标:) 我也无法用 2.6.31 (Ubuntu) 重现你的结果【参考方案2】:
限制会立即设置,但仅在尝试分配新堆栈或尝试增加现有堆栈时检查。内核源代码上的 RLIMIT_STACK (or a LXR identifier search) 的 grep 应该会告诉您。
显然,堆栈的初始大小是文件名 + env 字符串 + arg 字符串加上在 setup_arg_pages
上分配的一些额外页面(2.6.33 1,2 中的 20 页,128 Kb 2.6.34 3)。
总结:
initial stack size = MIN(size for filename + arg strings + env strings + extra pages, MAX(size for filename + arg strings + env strings, RLIMIT_STACK))
在哪里
size for filename + arg strings + env strings <= MAX(ARG_MAX(32 pages), RLIMIT_STACK/4)
此外,带有 Ingo Molnar 的 exec-shield
补丁的内核(Fedora、Ubuntu 等)还有一个额外的 EXEC_STACK_BIAS “(多出 2MB 以涵盖随机化效果。)”,请参阅对新功能 over_stack_limit()
来自 acct_stack_growth()
([Ubuntu1], [Ubuntu2], [Ubuntu3])。
我已经编辑了原始程序来显示这个:
#include <stdio.h>
#include <sys/resource.h>
void foo(void);
int main(int argc, char *argv[])
struct rlimit lim = 1, 1;
if (argc > 1 && argv[1][0] == '-' && argv[1][8]=='l')
printf("limiting stack size\n");
if (setrlimit(RLIMIT_STACK, &lim) == -1)
printf("rlimit failed\n");
return 1;
foo();
return 0;
void foo()
unsigned ints[32768];
printf("foo: %u\n", ints[2047]=42);
结果:
$./rl
foo: 42
$./rl -l
limiting stack size
Segmentation fault
$
【讨论】:
不,实际上,我能够扩展现有堆栈。我现在就像一条狗,不会放过这个问题。 @Tim Post:你确定堆栈确实增长了吗?请参阅我编辑的答案,初始堆栈上有一些额外的空间。 是的,我将两种情况都扩大到 16k,结果相同。 @Tim Post:在 2.6.33-x86 上将其扩展为 > 80 Kb,在 2.6.34+ 上扩展至 > 128 Kb 尝试在Stack *** 修正后设置rlimit_stack
可能会导致失败或相关问题。另请参阅红帽 Issue 1463241以上是关于使用 setrlimit() 设置堆栈大小并引发堆栈溢出/段错误的主要内容,如果未能解决你的问题,请参考以下文章