2010-11-07 102 views
11

在下面给出的示例中,我尝试将堆栈大小设置为1kb。用setrlimit()设置堆栈大小并激发堆栈溢出/段错误

为什么现在可以在堆栈中具有尺寸8kbfoo()分配整数数组?

#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

谢谢你,现在我沉迷于找出为什么像广告这不起作用在男人(2)setrlimit。幸运的是,gcc可以让你指定堆栈的大小:) – 2010-11-07 16:26:49

+0

这个时候,比起upvoted更频繁的问题。有趣。 – 2010-11-07 17:39:02

回答

4

该限制是立即设置的,但仅在尝试分配新堆栈或试图增大现有堆栈时才会被检查。内核源代码的RLIMIT_STACK(or a LXR identifier search)应该告诉grep。

显然,堆栈的初始大小无论是需要对文件名+ ENV字符串+ ARG串加上setup_arg_pages(20页分配一些额外的页2.6.33 12,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) 

此外,由英莫尔纳的exec-shield补丁(的Fedora,Ubuntu的,...)内核有一个额外的EXEC_STACK_BIAS “(2MB更多覆盖随机化效应)。“,请参阅从acct_stack_growth()[Ubuntu1][Ubuntu2][Ubuntu2][Ubuntu3])调用新函数over_stack_limit()

我编辑的原始程序,以显示这一点:

#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

不,实际上,我能够增长现有的堆栈。我现在就像一只不会放过这个问题的狗。 – 2010-11-07 16:25:01

+0

@Tim Post:你确定这个堆栈确实增长了吗?看到我编辑的答案,初始堆栈上有一些额外的空间。 – ninjalj 2010-11-07 18:21:43

+0

是的,我将这两种情况都扩展到16k,结果相同。 – 2010-11-07 18:48:33

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之前)杀害前,我不知道。

+0

我怀疑是类似的,只是用'fork()'试过,没有什么区别。我不明白为什么setrlimit()只影响通过'exec'生成的进程,而不是父进程,但似乎确实如此。 – 2010-11-07 15:31:46

+0

在GDB中,我在'foo 2:42'行后得到'程序退出正常' - 没有死亡,没有段错误 – tur1ng 2010-11-07 15:46:33

+0

@ tur1ng:尝试在main的开头添加'newarg [0] = argv [0];''。我怀疑你的二进制文件不叫“a.out” – pmg 2010-11-07 15:50:02