Ошибка сегментации

редактировать
Ошибка компьютера вызвано доступ к ограниченной памяти

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

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

Многие языки программирования могут использовать механизмы, предназначенные для предотвращения ошибок сегментации и повышения безопасности памяти. Например, язык программирования Rust использует модель на основе «владения» для обеспечения безопасности памяти. Другие языки, такие как Lisp и Java, используют сборку мусора, которая позволяет избежать определенных классов ошибок памяти, которые могут привести к ошибкам сегментации.

Содержание

  • 1 Обзор
  • 2 Причины
  • 3 Обработка
  • 4 Примеры
    • 4.1 Запись в постоянную память
    • 4.2 Разыменование нулевого указателя
    • 4.3 Переполнение буфера
    • 4.4 Переполнение стека
  • 5 См. Также
  • 6 Ссылки
  • 7 Внешние ссылки

Обзор

Пример сигнала, сгенерированного человеком A нулевой указатель разыменование в Windows 8

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

Термин «сегментация» используется в вычислительной технике по-разному; в контексте «ошибки сегментации», термина, используемого с 1950-х годов, он относится к адресному пространству программы. С защитой памяти, только собственное адресное пространство программы доступно для чтения, и из него только стек и часть чтения / записи сегмента данных программы доступны для записи, в то время как чтение -только данные и сегмент кода не доступны для записи. Таким образом, попытка чтения за пределами адресного пространства программы или запись в сегмент адресного пространства, доступный только для чтения, приводит к ошибке сегментации, отсюда и название.

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

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

На уровне операционной системы эта ошибка перехватывается, и сигнал передается процессу-нарушителю, активируя обработчик процесса для этого сигнала. В разных операционных системах используются разные имена сигналов, указывающие на ошибку сегментации. В Unix-подобных операционных системах сигнал, называемый SIGSEGV (сокращенно от нарушения сегментации), отправляется нарушившему процессу. В Microsoft Windows нарушающий процесс получает исключение STATUS_ACCESS_VIOLATION .

Причины

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

Ниже приведены некоторые типичные причины ошибки сегментации:

  • Попытка доступа к несуществующему адресу памяти (вне адресного пространства процесса)
  • Попытка доступа к памяти, на которую программа не имеет прав (например, структуры ядра в контексте процесса)
  • Попытка записи в память только для чтения (например, сегмент кода)

Это, в свою очередь, часто вызвано ошибками программирования, которые приводят к недопустимому доступу к памяти:

  • Разыменование нулевого указателя, который обычно указывает на адрес, не являющийся частью адресного пространства процесса
  • Разыменование или присвоение неинициализированному указателю (дикий указатель, который указывает к случайному адресу памяти)
  • Разыменование или присвоение освобожденному указателю (висячий указатель, который указывает на память, которая была освобождена / освобождена / удалена)
  • A переполнение буфера
  • A stack overflow
  • Попытка выполнить некорректную компиляцию программы. (Некоторые компиляторы выводят исполняемый файл, несмотря на наличие ошибок времени компиляции.)

В коде C сбои сегментации чаще всего возникают из-за ошибок при использовании указателя, особенно в C dynamic выделение памяти. Разыменование нулевого указателя всегда будет приводить к ошибке сегментации, но дикие указатели и висячие указатели указывают на память, которая может существовать, а может и не существовать, и может быть или не быть доступной для чтения или записи, и, таким образом, может привести к временным ошибкам. Например:

char * p1 = NULL; // Нулевой указатель char * p2; // Дикий указатель: вообще не инициализирован. char * p3 = malloc (10 * sizeof (char)); // Инициализированный указатель на выделенную память // (при условии, что malloc не завершился ошибкой) free (p3); // p3 теперь является висящим указателем, так как память была освобождена

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

Обработка

Действие по умолчанию для ошибки сегментации или ошибки шины - аварийное завершение процесса, который ее инициировал. Для облегчения отладки может быть сгенерирован файл core, а также могут быть выполнены другие действия, зависящие от платформы. Например, системы Linux, использующие патч grsecurity, могут регистрировать сигналы SIGSEGV для отслеживания возможных попыток вторжения с помощью переполнения буфера.

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

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

Примеры

Ошибка сегментации на EMV клавиатура

Запись в постоянную память

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

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

int main (void) {char * s = "hello world"; * s = 'H'; }

При компиляции программы, содержащей этот код, строка «hello world» помещается в раздел rodata исполняемого файла программы : раздел только для чтения сегмент данных. После загрузки операционная система помещает его с другими строками и данными константы в сегмент памяти, доступный только для чтения. При выполнении переменная s устанавливается так, чтобы указывать на расположение строки, и делается попытка записать символ H через переменную в память, что вызывает ошибку сегментации. Компиляция такой программы с помощью компилятора, который не проверяет назначение мест только для чтения во время компиляции, и запуск ее в Unix-подобной операционной системе приводит к следующей ошибке времени выполнения :

$ gcc segfault.c - g -o segfault $./segfault Ошибка сегментации

Отслеживание основного файла из GDB :

Программа получила сигнал SIGSEGV, Ошибка сегментации. 0x1c0005c2 в main () на segfault.c: 6 6 * s = 'H';

Этот код можно исправить, используя массив вместо указателя на символ, так как он выделяет память в стеке и инициализирует ее значением строкового литерала:

char s = "hello world"; s [0] = 'H'; // эквивалентно * s = 'H';

Несмотря на то, что строковые литералы не должны изменяться (в стандарте C это имеет неопределенное поведение), в C они имеют тип static char, поэтому в исходном коде нет неявного преобразования (что указывает на a char *в этом массиве), тогда как в C ++ они имеют тип static const char, и, следовательно, существует неявное преобразование, поэтому компиляторы обычно обнаруживают эту конкретную ошибку.

Разыменование нулевого указателя

В языках C и C-подобных нулевые указатели используются для обозначения «указателя на отсутствие объекта» и в качестве индикатора ошибки, и разыменование нулевого указателя (чтение или запись через нулевой указатель) - очень распространенная ошибка программы. Стандарт C не говорит, что нулевой указатель совпадает с указателем на адрес памяти 0, хотя на практике это может иметь место. Большинство операционных систем отображают адрес нулевого указателя таким образом, что доступ к нему вызывает ошибку сегментации. Это поведение не гарантируется стандартом C. Разыменование нулевого указателя - это неопределенное поведение в C, и соответствующая реализация может предполагать, что любой указатель, который разыменован, не является нулевым.

int * ptr = NULL; printf ("% d", * ptr);

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

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

int * ptr = NULL; * ptr = 1;

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

int * ptr = NULL; * ptr;

Переполнение буфера

Переполнение стека

Другой пример - рекурсия без базового случая:

int main (void) {main (); возврат 0; }

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

См. Также

Ссылки

Внешние ссылки

Поиск segmentation fault в Wiktionary, свободном словаре.
Последняя правка сделана 2021-06-07 08:54:52
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте