Почему это НЕ дает нарушение сегментации? - PullRequest
3 голосов
/ 30 марта 2010

Считается, что приведенный ниже код дает нарушение сегментации:

#include <stdio.h> 
#include <string.h> 

void function(char *str) {
   char buffer[16];

   strcpy(buffer,str);
}

int main() {
  char large_string[256];
  int i;

  for( i = 0; i < 255; i++)
    large_string[i] = 'A';

  function(large_string);
  return 1;
}

Он скомпилирован и работает так:

gcc -Wall -Wextra hw.cpp && a.exe

Но ничего не выводится.

Примечание

Приведенный выше код действительно перезаписывает адрес возврата и так далее, если вы действительно понимаете, что происходит под ним.

Адрес ret будет 0x41414141, если быть точным.

Внимание Это требует глубокого знания стека

Ответы [ 10 ]

6 голосов
/ 30 марта 2010

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

3 голосов
/ 30 марта 2010

Вероятно, в вашей реализации buffer находится сразу ниже large_string в стеке. Поэтому, когда вызов strcpy переполняет buffer, он просто записывает большую часть пути в large_string без какого-либо особого ущерба. Он будет записывать не менее 255 байтов, но то, что он записывает больше, зависит от того, что выше large_string (и неинициализированного значения последнего байта large_string). Кажется, он остановился перед тем, как нанести какой-либо ущерб или ущерб.

По счастливой случайности, обратный адрес вызова на function не удаляется. Либо он находится ниже buffer в стеке, либо в регистре, либо, может быть, функция встроена, я не могу вспомнить, что не делает оптимизация. Если вы не можете проверить разборку, я тоже не могу ;-). Итак, вы возвращаетесь и выходите без проблем.

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

[Редактировать: я проверил мой компилятор (GCC на cygwin), и для этого кода он использует стандартное соглашение о вызовах x86 и код входа / выхода. И это делает segfault.]

2 голосов
/ 30 марта 2010

Вы компилируете программу .cpp (c ++), вызывая gcc (вместо g ++) ... не уверен, является ли это причиной, но в системе linux (кажется, что вы работаете в windows по умолчанию). exe output) выдает следующую ошибку при попытке компиляции, как вы указали:

/ tmp / ccSZCCBR.o :(. Eh_frame + 0x12): неопределенная ссылка на `__gxx_personality_v0 ' collect2: ld вернул 1 статус выхода

1 голос
/ 30 марта 2010

Скорее всего, длинная строка фактически заканчивается нулевым байтом в i. Предполагая, что переменные в main располагаются в порядке, в котором они объявлены - что не требуется ни в одной из спецификаций языка, о которых я знаю, но кажется вероятным на практике - тогда large_string будет первым в памяти, за которым следует i , Цикл устанавливает i в 0 и считает до 255. Независимо от того, хранится ли i с прямым или младшим порядком байтов, в любом случае он содержит нулевой байт. Поэтому при обходе large_string в байтах 256 или 257 вы получите нулевой байт.

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

Но, как говорят другие, «неопределенный» означает «непредсказуемый».

1 голос
/ 30 марта 2010

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

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

1 голос
/ 30 марта 2010

Вы можете проверить это другими способами:

#include <stdlib.h>
int main() {
    int *a=(int *)malloc(10*sizeof(int));
    int i;
    for (i=0;i<1000000; i++) a[i] = i;
    return 0;
}

В моей машине это вызывает SIGSEGV только при i = 37000! (проверено осмотром ядра с помощью gdb).

Чтобы защититься от этих проблем, протестируйте свои программы с помощью отладчика malloc ... и используйте много malloc, поскольку я не знаю библиотек отладки памяти, которые могли бы просматривать статическую память. Пример: Электрический забор

gcc -g -Wall docore.c -o c -lefence

И теперь SIGSEGV запускается, как только i=10, как и следовало ожидать.

1 голос
/ 30 марта 2010

В стеке есть нулевой байт, который останавливает strcpy(), и в стеке достаточно места, чтобы не попасть на защищенную страницу. Попробуйте распечатать strlen(buffer) в этой функции. В любом случае результатом будет неопределенное поведение .

Привыкайте использовать strlcpy(3) семейство функций.

1 голос
/ 30 марта 2010

Это неопределенное поведение, что означает, что может случиться что угодно. Программа может даже работать правильно.

Кажется, что вы просто не перезаписали какие-либо части памяти, которые все еще необходимы остальной части (короткой) программы (или находятся вне адресного пространства программ / защищены от записи / ...), так что ничего особенного случается. По крайней мере, ничего, что могло бы привести к какому-либо выводу.

1 голос
/ 30 марта 2010

Его UB (неопределенное поведение). Strcpy, возможно, скопировал больше байтов в память, указанную буфером, и это может не вызвать проблем в этот момент.

0 голосов
/ 30 марта 2010

В вашем буфере символов [16] может быть что угодно, включая \ 0. strcpy копирует до тех пор, пока не найдет первый \ 0 - таким образом, не выходя за пределы вашей границы из 16 символов.

...