2010-11-07 1 views
11

В приведенном ниже примере я пытаюсь установить stacksize в 1kb.Установите размер стека с помощью setrlimit() и спровоцируйте переполнение стека/segfault

Почему теперь можно выделить массив ints в стеке с размером 8kb в foo()?

#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 позволяет указать размер стека :) –

+0

Вопрос был одобрен чаще, чем он был поднят - на этот раз. Интересно. –

ответ

4

Предел устанавливается сразу же, но проверяется только при попытке выделить новый стек или пытается увеличить существующий стек. Должен сказать grep для RLIMIT_STACK (or a LXR identifier search) в источниках ядра.

По-видимому, начальный размер стека все, что необходимо, чтобы имя файла + ENV строк + ARG строк плюс некоторые дополнительные страницы, выделенные на setup_arg_pages (20 страниц в 2.6.33 1, 2, 128 Кб на 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 больше, чтобы покрыть эффекты рандомизации.) «, см. звонок к новой функции over_stack_limit() от acct_stack_growth() ([Ubuntu1], , [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 
$ 
+1

Нет, на самом деле, мне удалось вырастить существующий стек. Я теперь как собака, которая не отпустит кость с этой проблемой. –

+0

@Tim Post: вы уверены, что стек вырос? См. Мой отредактированный ответ, в исходном стеке есть дополнительное пространство. – ninjalj

+0

Да, я расширил оба варианта до 16k, тот же результат. –

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', а не на родителя, но похоже, что это так. –

+0

С GDB я получаю «Программа вышла нормально» после строки «foo 2: 42» - не убита, нет segfault – tur1ng

+0

@ tur1ng: попробуйте добавить 'newarg [0] = argv [0];' в начале main. Я подозреваю, что ваш двоичный файл не называется «a.out» – pmg