在下面的给出示例中,我尝试将stacksize设置为1kb。
为什么现在可以在8kb
中的大小为foo()
的堆栈上分配一个int数组?
#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);
}
答案 0 :(得分:7)
限制立即设置,但仅在尝试分配新堆栈或尝试增加现有堆栈时进行检查。内核源代码上的RLIMIT_STACK(or a LXR identifier search)的grep应该告诉。
显然,堆栈的初始大小是文件名+ env字符串+ arg字符串以及setup_arg_pages
上分配的一些额外页面所需的内容(2.6.33 1中的20页,{{ 3}},在2.6.34 2上的128 Kb。)
总结:
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()
(3,[Ubuntu1],[Ubuntu2])的新功能acct_stack_growth()
。
我编辑了原始程序以显示:
#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
$
答案 1 :(得分:4)
我认为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 lim: 8388608 / -1 foo 1: 42 lim: 65536 / 65536 foo 2: 42 Killed
为什么在打印限制之前(以及在调用foo之前)进程被杀死,我不知道。