Почему я не получаю ошибку сегментации с этим кодом?(Ошибка шины) - PullRequest
4 голосов
/ 07 марта 2012

У меня была ошибка в моем коде, которая пошла следующим образом.

char desc[25];
char name[20];
char address[20];
sprintf (desc, "%s %s", name, address);

В идеале это должно дать ошибку. Тем не менее, я видел, что это дает ошибку шины. Википедия говорит что-то в порядке «Ошибка шины - это когда программа пытается получить доступ к не выровненной области памяти или когда вы пытаетесь получить доступ к физической (не виртуальной) области памяти, которая не существует или не разрешена. «

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

EDIT: - Довольно много людей упомянули контекст. Я не уверен, какой контекст понадобится, но это было переполнение буфера, лежащее внутри статической функции класса, которая вызывается из ряда других функций класса. Если есть что-то более конкретное, что я могу дать, и это поможет, спросите.

В любом случае, кто-то заметил, что я должен просто написать лучший код. Я предполагаю, что смысл задаваться этим вопросом заключался в том, «может ли разработчик приложения сделать что-либо из SIGBUS или SIGSEGV?» (выбрано из этого поста ниже)

Ответы [ 6 ]

4 голосов
/ 07 марта 2012

Как вы, вероятно, понимаете, основной причиной является неопределенное поведение в вашем программа. В этом случае это приводит к ошибке, обнаруженной аппаратным обеспечением, который перехватывается ОС и отображается на сигнал. Точное отображение на самом деле не указано (и я видел интегральное деление на нулевой результат в SIGFPE), но обычно: SIGSEGV происходит при доступе из границы, SIGBUS для других ошибок доступа и SIGILL для нелегального инструкция. В этом случае наиболее вероятным объяснением является то, что ваш ошибка границ перезаписала адрес возврата в стеке. Если обратный адрес выровнен неправильно, вы, вероятно, получите SIGBUS, и если это так, вы начнете выполнять все, что есть, что может результат в SIGILL. (Но возможность выполнения случайных байтов как Код это то, что комитет по стандартам имел в виду, когда они определили & ldquo; неопределенное поведение & rdquo ;. Особенно на машинах без памяти защита, где вы можете прыгнуть прямо в ОС.)

4 голосов
/ 07 марта 2012

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

То, что может быть недопустимым для программы на C ++, в целом может быть недопустимым для программы.Например, ОС не волнует, если вы выйдете за пределы массива.Он даже не знает, что такое массив.Однако заботится о , если вы касаетесь не принадлежащей вам памяти.

1 голос
/ 07 марта 2012

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

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

Однако, я думаю, вы также можете получить ошибку шины, если попытаетесь выполнить код из недопустимоговиртуальный адрес.Это вполне может быть тем, что здесь происходит - записывая конец массива local , вы перезаписываете важные части стекового фрейма, такие как адрес возврата функции.Это приведет к возврату функции на неверный адрес, что (я думаю) приведет к ошибке шины.Это мое лучшее предположение о том, какой конкретный вид неопределенного поведения вы испытываете здесь.

В общем, вы не можете полагаться на ошибки сегментации для определения переполнения буфера;лучший инструмент, о котором я знаю, это valgrind , хотя он все равно не сможет уловить некоторые переполнения.Лучший способ избежать переполнения при работе со строками - использовать std::string, а не делать вид, что вы пишете C.

1 голос
/ 07 марта 2012

В данном конкретном случае вы не знаете, какой вид мусора у вас в строке формата.Этот мусор потенциально может привести к тому, что остальные аргументы будут рассматриваться как аргументы типа «выровненный» (например, int или double)Обработка невыровненной области как выровненного аргумента определенно вызывает SIGBUS в некоторых системах.

0 голосов
/ 07 марта 2012
char desc[25];
char name[20];
char address[20];
sprintf (desc, "%s %s", name, address);

Просто взглянув на этот код, я могу предположить, что каждый name и address может иметь длину 20 символов.Если это так, то не означает ли это, что desc должно быть минимум 20+20+1 символов?(1 char для пробела между name и address, как указано в sprintf).

Это может быть единственной причиной сегфоута.Могут быть и другие причины.Например, что если name длиннее 20 символов?

Так лучше использовать std::string:

std::string name;
std::string address;
std::string desc = name + " " + address;
char const *char_desc = desc.str(); //if at all you need this
0 голосов
/ 07 марта 2012

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

Переменная desc должна иметь длину не менее 41 символа (20 + 20 + 1 [для пробела, который вы вставляете]).

Используйте valgrind или gdb, чтобы выяснить, почему вы получаете ошибку сегмента.

...