2010-11-07 99 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