Почему переполнение буфера вызывает ошибки сегментации при доступе к целому числу? - PullRequest
4 голосов
/ 04 декабря 2009

Во время вызова функции B () из функции A (), B () выделяет массив из 100 символов и заполняет его несколько раз, в том числе один раз строкой из 101 символа и один раз строкой из 110 символов. Это очевидная ошибка.

Позже, функция A () пытается получить доступ к совершенно не связанной переменной int i, и возникает ошибка сегментации.

Я понимаю, почему происходит переполнение буфера, но почему я получаю ошибку сегментации, когда я получаю доступ к этому целому числу? Почему я просто не получаю данные об мусоре?

Ответы [ 5 ]

14 голосов
/ 04 декабря 2009

Переполнение буфера может засорить ранее сохраненную версию указателя кадра в стеке. Когда функция возвращается, эта поврежденная версия загружается в регистр указателя кадра, вызывая описанное вами поведение.

Страница Википедии содержит рисунок и определения.

5 голосов
/ 04 декабря 2009

Когда A() вызывает B(), инструкции преамбулы B сохраняют указатель кадра A - место в стеке, где A хранит локальные переменные, прежде чем заменить его собственным указателем кадра B. Это выглядит так:

Stack Frame

Когда B переполняет свои локальные переменные, он портит значение, которое будет перезагружено в указатель кадра. Это мусор в качестве значения указателя кадра, поэтому все локальные переменные A удаляются. Хуже того, будущие записи в локальные переменные портят память, принадлежащую кому-то другому.

3 голосов
/ 04 декабря 2009

Наиболее вероятное объяснение из вашего описания состоит в том, что переполнение в B повреждает указатель сохраненного кадра в стеке для A. Таким образом, после возврата B, A имеет мусор в своем указателе кадра и аварийно завершает работу, когда пытается получить доступ к локальной переменной.

0 голосов
/ 04 декабря 2009

Важно помнить, что вы выделяете достаточно памяти плюс один для завершающего символа nul (проницательные читатели укажут этот nul, который в основном присутствует по причине - nul с одним 'l' равно '\0' [Спасибо Software Monkey за указание на ошибку!], Ноль с двумя 'l' - указатель, указывающий на ничто).

Вот пример того, как может возникнуть ошибка сегмента

int main(int argc, char **argv){
    int *x = NULL;
    *x = 5;
    // boom
}

Поскольку x является указателем и имеет значение null, мы пытаемся разыменовать указатель и присвоить ему значение. Гарантированный способ генерации ошибки сегментации.

Существует старый трюк, заключающийся в том, что вы можете перехватить ошибку сегмента и получить трассировку стека, более распространенную в среде Unix, установив обработчик сигнала для перехвата SIGSEGV, и в своем обработчике сигнала вызвать процесс, подобный это:

char buf[250];
buf[0] = '\0';
sprintf(buf, "gdb -a %d | where > mysegfault.txt", getpid());
system(buf);

Прикрепляет текущую исполняемую C-программу, отлаживает к отладчику и присоединяется к нему, часть where показывает трассировку стека ошибочной строки, которая вызвала ошибку seg, и перенаправляет вывод в файл в текущий каталог.

Примечание: это определяется реализацией, в зависимости от установки, в AIX присутствует отладчик gnu, и, следовательно, это будет работать, ваш пробег может отличаться.

Надеюсь, это поможет, С наилучшими пожеланиями, Том.

0 голосов
/ 04 декабря 2009

Если вы обращаетесь к i через указатель, проблема в том, что указатель является мусором.

...