Введение

В вычислительной технике, ошибка сегментации (часто сокращается до segfault) или нарушение доступа – это ошибка или состояние сбоя, генерируемое аппаратным обеспечением с защитой памяти, уведомляющее операционную систему (ОС) о том, что программное обеспечение предприняло попытку доступа к запрещенной области памяти (нарушение доступа к памяти). На стандартных компьютерах x86 это является формой общей ошибки защиты. Ядро операционной системы в ответ обычно выполняет корректирующие действия, как правило, передавая ошибку нарушившему процессу, отправляя процессу сигнал. Процессы в некоторых случаях могут установить собственный обработчик сигналов, позволяя им самостоятельно восстанавливаться, но в противном случае используется обработчик сигналов по умолчанию ОС, что обычно приводит к аварийному завершению процесса (сбою программы), а иногда и к созданию дампа памяти. Ошибки сегментации – это распространенный класс ошибок в программах, написанных на языках, таких как C, которые предоставляют доступ к памяти низкого уровня и практически не осуществляют проверок безопасности. Они возникают главным образом из-за ошибок при использовании указателей для адресации виртуальной памяти, особенно при незаконном доступе. Другой тип ошибки доступа к памяти – ошибка шины, которая также имеет различные причины, но в настоящее время встречается гораздо реже; она возникает в основном из-за некорректной адресации физической памяти или из-за невыровненного доступа к памяти – это ссылки на память, которые аппаратное обеспечение не может адресовать, а не ссылки, к которым процесс не имеет права доступа. Многие языки программирования имеют механизмы, предназначенные для предотвращения ошибок сегментации и повышения безопасности памяти. Например, Rust использует модель, основанную на владении, для обеспечения безопасности памяти. Другие языки, такие как Lisp и Java, используют сборку мусора, что позволяет избежать определенных классов ошибок памяти, которые могут привести к ошибкам сегментации.

Обзор

Ошибка сегментации возникает, когда программа пытается получить доступ к области памяти, к которой у неё нет прав доступа, или пытается получить доступ к области памяти недопустимым способом (например, пытается записать в область, предназначенную только для чтения, или перезаписать часть операционной системы). Термин "сегментация" имеет различные значения в информатике; в контексте "ошибки сегментации" он относится к адресном пространству программы. При использовании защиты памяти доступно только собственное адресное пространство программы, и в нём только стек и область данных, доступная для чтения и записи, могут быть изменены. Данные, выделенные в сегменте `const` и сегменте кода, не могут быть изменены. Таким образом, попытка чтения за пределами адресного пространства программы или запись в сегмент адресного пространства, доступный только для чтения, приводит к ошибке сегментации, отсюда и название. В системах, использующих аппаратную сегментацию памяти для реализации виртуальной памяти, ошибка сегментации возникает, когда аппаратное обеспечение обнаруживает попытку обратиться к несуществующему сегменту, к области за пределами границ сегмента или обратиться к области способом, запрещённым разрешениями, установленными для этого сегмента. В системах, использующих только страничную организацию памяти, недействительная ошибка страницы обычно приводит к ошибке сегментации, и ошибки сегментации и ошибки страницы – это оба типа ошибок, генерируемых системой управления виртуальной памятью. Ошибки сегментации могут возникать и независимо от ошибок страниц: несанкционированный доступ к существующей странице является ошибкой сегментации, но не ошибкой недействительной страницы, а ошибки сегментации могут возникать в середине страницы (следовательно, без ошибки страницы), например, при переполнении буфера, которое остаётся в пределах страницы, но незаконно перезаписывает память. На аппаратном уровне ошибка первоначально генерируется блоком управления памятью (MMU) при несанкционированном доступе (если запрошенная область памяти существует) в рамках его функции защиты памяти, или при недействительной ошибке страницы (если запрошенная область памяти не существует). Если проблема заключается не в недействительном логическом адресе, а в недействительном физическом адресе, возникает ошибка шины, хотя эти ошибки не всегда различаются. На уровне операционной системы эта ошибка перехватывается, и сигнал отправляется нарушившему процесс, активируя обработчик этого сигнала в процессе. Различные операционные системы используют разные имена сигналов для обозначения ошибки сегментации. В Unix-подобных операционных системах нарушившему процессу отправляется сигнал SIGSEGV (сокращение от segmentation violation). В Microsoft Windows нарушающий процесс получает исключение STATUS ACCESS VIOLATION.

Обработка

Действие по умолчанию при ошибке сегментации или ошибке шины – это ненормальное завершение процесса, который её вызвал. Для облегчения отладки может быть создан дамп памяти, а также могут выполняться другие действия, зависящие от платформы. Например, системы Linux с патчем grsecurity могут регистрировать сигналы SIGSEGV для мониторинга возможных попыток вторжения, использующих переполнение буфера. На некоторых системах, таких как Linux и Windows, программа может самостоятельно обрабатывать ошибку сегментации. В зависимости от архитектуры и операционной системы, работающая программа может не только обработать событие, но и извлечь информацию о своём состоянии, например, получить трассировку стека, значения регистров процессора, строку исходного кода, вызвавшую ошибку, адрес памяти, к которой был осуществлён недопустимый доступ, и тип операции – чтение или запись. Хотя ошибка сегментации обычно указывает на наличие ошибки в программе, которую необходимо исправить, её также можно намеренно вызывать для тестирования, отладки и эмуляции платформ, требующих прямого доступа к памяти. В последнем случае система должна позволять программе продолжать работу даже после возникновения ошибки. Если система это позволяет, можно обработать событие и увеличить счётчик команд процессора, чтобы "перескочить" через проблемную инструкцию и продолжить выполнение.