Установить размер стека с помощью setrlimit () и вызвать переполнение стека / segfault - PullRequest
12 голосов
/ 07 ноября 2010

В приведенном ниже примере я пытаюсь установить размер стека на 1 КБ.

Почему теперь можно выделить массив стеков в стеке размером 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);
}

Ответы [ 2 ]

7 голосов
/ 07 ноября 2010

Предел устанавливается немедленно, но проверяется только при попытке выделить новый стек или при попытке увеличить существующий стек.Grep для RLIMIT_STACK ( или поиск по идентификатору LXR ) в исходных текстах ядра должен указывать.

Очевидно, начальный размер стека - это то, что необходимо для имени файла + env strings + 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 "(еще 2 МБ для покрытия эффектов рандомизации.)" , см. Вызов новой функции 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
$  
4 голосов
/ 07 ноября 2010

Я думаю, 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), я не знаю.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...