Можно ли предсказать переполнение стека в C в Linux?

StackOverflow https://stackoverflow.com/questions/427209

  •  06-07-2019
  •  | 
  •  

Вопрос

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

  • struct my_big_object[HUGE_NUMBER] в стеке.Прохождение через него в конечном итоге приводит к SIGSEGV.
  • А 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);
}
Это было полезно?

Решение

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

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;
}

Другие советы

Умеренная процедура Alloca () (например, Malloc (), но использует стек, автоматически освобождается, а также вызывает Sigsegv, если он слишком большой).

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

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

И разве вы не получите null из alloca, если не останется достаточно места?(так же, как и malloc?)

А когда ваш код выходит из строя, где он выходит из строя?это в alloca или в doStuff()?

/Йохан

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

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

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

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

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

Вопрос интересный, но вызывает удивление.Он поднимает стрелку моего измерителя с квадратным колышком и круглым отверстием.

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

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

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

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

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

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

Производительность malloc была упомянута на Подкаст Stackoverflow № 36.

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

Даже если это не прямой ответ на ваш вопрос, я надеюсь, вы знаете о существовании Валгринд — замечательный инструмент для обнаружения подобных проблем во время выполнения в Linux.

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

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

void *closeToBase;

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

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

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

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

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

Извините, если это говорит об очевидном, но вы можете легко написать функцию для проверки определенного размера выделения стека, просто попробовав alloca (этого размера) и перехватив исключение переполнения стека.Если вы хотите, вы можете поместить его в функцию с некоторой заранее определенной математикой для накладных расходов стека функций.Например:

bool CanFitOnStack( size_t num_bytes )
{
    int stack_offset_for_function = 4; // <- Determine this
    try
    {
        alloca( num_bytes - stack_offset_for_function );
    }
    catch ( ... )
    {
        return false;
    }

    return true;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top