Как обнаружить возможные / потенциальные проблемы переполнения стека в программе c / c ++?

Есть ли стандартный способ узнать, сколько места в стеке у вашего приложения и каков самый высокий водяной знак для использования стека во время выполнения?

Что же происходит в ужасном случае фактического переполнения?

Вылетает ли он, вызывает исключение или сигнал? Есть ли стандарт или он разный для всех систем и компиляторов?

Я ищу специально для Windows, Linux и Macintosh.


person KPexEA    schedule 14.10.2008    source источник
comment
Связано: stackoverflow.com/questions/389219/   -  person masterxilo    schedule 23.11.2016
comment
Я ищу специально для Windows, Linux и Macintosh - тогда вполне конкретно :)   -  person Nihar Karve    schedule 09.07.2020


Ответы (8)


В Windows будет сгенерировано исключение переполнения стека.

Следующий код Windows иллюстрирует это:

#include <stdio.h>
#include <windows.h>

void StackOverFlow()
{
  CONTEXT context;

  // we are interested control registers
  context.ContextFlags = CONTEXT_CONTROL;

  // get the details
  GetThreadContext(GetCurrentThread(), &context);

  // print the stack pointer
  printf("Esp: %X\n", context.Esp);

  // this will eventually overflow the stack
  StackOverFlow();
}

DWORD ExceptionFilter(EXCEPTION_POINTERS *pointers, DWORD dwException)
{
  return EXCEPTION_EXECUTE_HANDLER;
}

void main()
{
  CONTEXT context;

  // we are interested control registers
  context.ContextFlags = CONTEXT_CONTROL;

  // get the details
  GetThreadContext(GetCurrentThread(), &context);

  // print the stack pointer
  printf("Esp: %X\n", context.Esp);

  __try
  {
    // cause a stack overflow
    StackOverFlow();
  }
  __except(ExceptionFilter(GetExceptionInformation(), GetExceptionCode()))
  {
    printf("\n****** ExceptionFilter fired ******\n");
  }
}

Когда этот exe запускается, генерируется следующий вывод:

Esp: 12FC4C
Esp: 12F96C
Esp: 12F68C
.....
Esp: 33D8C
Esp: 33AAC
Esp: 337CC

****** ExceptionFilter fired ******
person jussij    schedule 14.10.2008
comment
Поправьте меня, если я ошибаюсь, потому что не уверен. Ваш код иллюстрирует простой случай. Но если стек временно переполняется в кучу, а затем повторная обработка, это не всегда будет вызывать исключение переполнения, поскольку этот механизм обнаружения работает в другом потоке. - person Martin York; 14.10.2008
comment
Я определенно не эксперт, но я бы подумал, что исключение стека генерируется, когда запрос на перемещение указателя стека приводит к тому, что указатель ссылается на недопустимую память. Переменные в стеке могут повредить стек, но я не думаю, что это вызовет исключение переполнения стека. - person jussij; 15.10.2008
comment
В примере, который я опубликовал, каждый вызов функции StackOverFlow продвигает указатель стека (как видно из распечатки), и в конечном итоге этот указатель попадает в недопустимую память. - person jussij; 15.10.2008

В Linux вы получаете ошибку сегментации, если ваш код пытается писать мимо стека.

Размер стека - это свойство, передаваемое между процессами. Если вы можете прочитать или изменить его в оболочке, используя такие команды, как ulimit -ssh, ksh, zsh) или limit stacksize (tcsh, zsh).

Из программы размер стека можно узнать с помощью

#include <sys/resource.h>
#include <stdio.h>

int main() {
    struct rlimit l;
    getrlimit(RLIMIT_STACK, &l);
    printf("stack_size = %ld\n", l.rlim_cur);
    return 0;
}

Я не знаю стандартного способа получить размер доступного стека.

Стек начинается с argc, за которым следует содержимое argv и копия среды, а затем ваши переменные. Однако, поскольку ядро ​​может рандомизировать расположение начала стека, и могут быть некоторые фиктивные значения выше argc, было бы неправильно предполагать, что у вас есть l.rlim_cur байтов, доступных ниже &argc.

Один из способов узнать точное местоположение стека - просмотреть файл /proc/1234/maps (где 1234 - это идентификатор процесса вашей программы). Как только вы узнаете эти границы, вы можете вычислить, какая часть вашего стека используется, посмотрев на адрес последней локальной переменной.

person adl    schedule 14.10.2008
comment
Я не верю, что существует стандартный способ получить размер доступного стека. Стандарт вообще определяет существование стека? - person Greg D; 14.10.2008
comment
Я только что посмотрел на стандарт C, и действительно, он даже не использует слово stack. Это забавно. Он различает статическое, автоматическое или выделенное хранилище; однако я не смог найти места, где можно было бы предположить, что вызов функции может завершиться неудачно из-за ограничений памяти. - person adl; 15.10.2008
comment
@GregD есть косвенный способ 1. получить максимальный размер стека 2. получить текущий размер стека 3. сделать A-B - person Y00; 07.08.2020

gcc помещает дополнительный блок памяти между адресом возврата и обычными переменными в «небезопасных» вызовах функций, например (в этом примере функция void test () {char a [10]; b [20]}:

call stack:
-----------
return address
dummy
char b[10]
char a[20]

Если функция запишет 36 байтов в указатель «a», переполнение «испортит» адрес возврата (возможное нарушение безопасности). Но он также изменит значение «пустышки», то есть между указателем и адресом возврата, поэтому программа выйдет из строя с предупреждением (вы можете отключить это с помощью -fno-stack-protector)

person Community    schedule 14.10.2008

В Linux библиотека libsigsegv Gnu включает функцию stackoverflow_install_handler, которая может обнаружить (и в некоторых случаях помочь вам восстановиться) переполнение стека.

person Norman Ramsey    schedule 17.10.2009

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

Интересное обсуждение трудностей с обработкой этого типа исключения см. В следующих сообщениях блога: 1 и 2 от Криса Брамма, которые сосредоточены на проблеме с точки зрения .NET, в частности, на хосте CLR.

person Rob Walker    schedule 14.10.2008

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

person dmityugov    schedule 14.10.2008

Я бы посоветовал вам использовать альтернативный сигнал-стек, если вы работаете в Linux.

  1. В этом случае весь сигнал будет обрабатываться через альтернативный стек.
  2. В случае переполнения стека система генерирует сигнал SEGV, который может обрабатываться через альтернативный стек.
  3. Если вы его не используете ... тогда вы не сможете обработать сигнал, и ваша программа может выйти из строя без какой-либо обработки / сообщения об ошибках.
person Shyam Sunder Verma    schedule 19.12.2008

В Visual Studio можно использовать editbin для изменения размера стека. Информацию можно найти на странице msdn.microsoft.com/en-us/library/35yc2tc3 .aspx.

person Richard    schedule 11.08.2011