Можно ли предсказать переполнение стека в C на Linux? - PullRequest
4 голосов
/ 09 января 2009

Существуют определенные условия, которые могут вызвать переполнение стека в системе Linux x86:

  • struct my_big_object[HUGE_NUMBER] в стеке. Проходя через это в конечном итоге вызывает
  • Подпрограмма alloca() (как и malloc(), но использует стек, автоматически освобождает себя, а также взрывается с SIGSEGV, если она слишком большая). Обновление: alloca () формально не устарела, как я изначально говорил; это просто обескуражено .

Есть ли способ программно определить, достаточно ли велик локальный стек для данного объекта? Я знаю, что размер стека регулируется с помощью ulimit, поэтому я надеюсь, что есть способ (каким бы непереносимым он ни был). В идеале я хотел бы иметь возможность сделать что-то вроде этого:

int min_stack_space_available = /* ??? */;
if (object_size < min_stack_space_available)
{
    char *foo = alloca(object_size);
    do_stuff(foo);
}
else
{
    char *foo = malloc(object_size);
    do_stuff(foo);
    free(foo);
}

Ответы [ 12 ]

5 голосов
/ 09 января 2009

Вы можете определить пространство стека, доступное процессу, найдя размер пространства стека процесса и затем вычтя использованное количество.

ulimit -s

показывает размер стека в системе Linux. Для программного подхода проверьте getrlimit () . Затем, чтобы определить текущую глубину стека, вычтите указатель на вершину стека из одного в нижний. Например (код не проверен):

unsigned char *bottom_of_stack_ptr;

void call_function(int argc, char *argv) {
    unsigned char top_of_stack;
    unsigned int depth = (&top_of_stack > bottom_of_stack_ptr) ? 
        &top_of_stack-bottom_of_stack_ptr : 
        bottom_of_stack_ptr-&top_of_stack;

    if( depth+100 < PROGRAMMATICALLY_DETERMINED_STACK_SIZE ) {
        ...
    }
}

int main(int argc, char *argv) {
    unsigned char bottom_of_stack;
    bottom_of_stack_ptr = &bottom_of_stack;
    my_function();
    return 0;
}
1 голос
/ 10 января 2009

Вы можете использовать от GNU libsigsegv до обработки ошибки страницы, включая случаи, когда происходит переполнение стека (с его сайта):

В некоторых приложениях обработчик переполнения стека выполняет некоторую очистку или уведомляет пользователя, а затем немедленно завершает работу приложения. В других приложениях обработчик переполнения стека возвращается к центральной точке приложения. Эта библиотека поддерживает оба варианта использования. Во втором случае обработчик должен обеспечить восстановление нормальной маски сигналов (поскольку многие сигналы блокируются во время выполнения обработчика), а также должен вызвать sigsegv_leave_handler () для передачи управления; только тогда это может быть далеко.

1 голос
/ 09 января 2009

Несколько компиляторов, например Open Watcom C / C ++ , поддерживают функцию stackavail (), которая позволяет вам делать именно это

1 голос
/ 09 января 2009

Вы мало говорите о том, почему вы хотите выделить в стеке, но если это привлекательная модель стековой памяти, вы также можете реализовать распределение стека в куче. Выделите большой кусок памяти в начале программы и сохраните для него стек указателей, которые будут соответствовать кадрам в обычном стеке. Вам просто нужно помнить, чтобы вывести свой закрытый указатель стека, когда функция вернется.

1 голос
/ 09 января 2009

Не уверен, применимо ли это в Linux, но в Windows можно столкнуться с нарушениями доступа при большом выделении стека , даже если они успешны!

Это связано с тем, что по умолчанию VMM в Windows фактически помечает только несколько верхних (не знаю точно, сколько) 4096-байтовых страниц ОЗУ стека как страничные (то есть поддерживаемые файлом подкачки), поскольку полагает, что доступ к стеку обычно марш вниз сверху; когда доступы становятся ближе и ближе к текущей «границе», нижняя и нижняя страницы помечаются как доступные для просмотра. Но это означает, что раннее чтение / запись памяти намного ниже вершины стека вызовет нарушение прав доступа, поскольку эта память фактически еще не распределена!

1 голос
/ 09 января 2009

Функция alloca не устарела. Однако, это не в POSIX, и это также зависит от машины и компилятора. Справочная страница Linux для alloca отмечает, что «для некоторых приложений ее использование может повысить эффективность по сравнению с использованием malloc, а в некоторых случаях это также может упростить освобождение памяти в приложениях, которые используют longjmp () или siglongjmp (). В противном случае, его использование не рекомендуется. "

В справочной странице также говорится, что «нет указания на ошибку, если кадр стека не может быть расширен. Однако после неудачного размещения программа может получить SIGSEGV».

Производительность malloc фактически упоминалась в Stackoverflow Podcast # 36 .

(Я знаю, что это неправильный ответ на ваш вопрос, но я подумал, что он может быть полезен в любом случае.)

1 голос
/ 09 января 2009

alloca () вернет NULL в случае сбоя, я считаю, что поведение alloca (0) не определено и вариант платформы. Если вы проверите это до do_something (), вы никогда не должны быть поражены SEGV.

У меня есть пара вопросов:

  1. Почему, ну почему, вам нужно что-то такое большое в стеке? Размер по умолчанию на большинстве систем 8M, это все еще слишком мало?
  2. Если функция, вызывающая alloca (), блокирует, будет ли защита такого же количества кучи с помощью mlock () / mlockall () гарантировать со временем примерно одинаковую производительность доступа (т. Е. «Не меняй меня, брат!»)? Если вы используете более агрессивный планировщик 'rt', рекомендуется в любом случае вызывать его.

Вопрос интересный, но поднимает бровь. Это поднимает стрелку на моем квадратном колышке, круглом отверстии, о-метре.

1 голос
/ 09 января 2009

Устаревшая процедура alloca () (например, malloc (), но использующая стек, автоматически освобождает себя, а также взрывается с SIGSEGV, если он слишком большой).

Почему alloca устарела?

Во всяком случае, насколько быстрее в вашем случае alloca vs malloc? (Оно того стоит?)

А вы не получите нулевой ответ от alloca, если не осталось достаточно места? (так же, как malloc?)

А когда ваш код падает, где он падает? это в alloca или в doStuff ()?

/ Johan

0 голосов
/ 14 октября 2016

Конец области стека определяется ОС динамически. Несмотря на то, что вы можете найти «статические» границы стека, рассматривая области виртуальной памяти (VMA) с высокой степенью зависимости от ОС (см. Файлы stackvma * в libsigsegv / src / ), вы дополнительно должен учитывать

  • значения getrlimit,
  • размер стека для каждого потока (см. pthread_getstacksize )
0 голосов
/ 09 января 2009

Нет хорошего способа, о котором я могу думать. Может быть, это возможно, используя getrlimit () (предложенный ранее) и некоторую арифметику указателей? Но сначала спросите себя, действительно ли вы этого хотите.

void *closeToBase;

main () {
  int closeToBase;
  stackTop = &closeToBase;
}

int stackHasRoomFor(int bytes) {
  int currentTop;
  return getrlimit(...) - (&currentTop  - closeToBase) > bytes + SomeExtra;
}

Лично я бы этого не делал. Выделите большие вещи в куче, стек не предназначен для этого.

...