В предыдущей статье описывающей что означают сообщения ядра Linux о segfault программах, было описано, что входит в коды 'error N' и как выяснить, что означает тот или иной код. Вместо того, чтобы разбираться, что означает тот или иной код ошибки, в данном статье перечислены здесь, в порядке возрастания.
Основное сообщение ядра выглядит следующим образом:
1 | testp[1234]: segfault at 0 ip 0000000000401271 sp 00007ffd33b088d0 error 4 in testp[400000+98000]. |
Нас интересует часть 'error N', и немного часть 'at N' (которая является адресом неисправности).
Во всех этих случаях сбой происходит в пользовательском режиме, поэтому я не буду упоминать его конкретно для каждого из них. Кроме того, список возможных причин этих ошибок не является исчерпывающим или полностью подробным.
error 4: (Данные) прочитаны из не отображенной области.
Это классическое чтение по произвольному указателю. На 64-битном x86 большая часть адресного пространства не отображена, поэтому даже в программе, использующей относительно большой объем памяти, большинство плохих указателей будут попадать в память, которая вообще не имеет отображений.
Ошибочный адрес 0 является NULL-указателем и попадает в нулевую страницу, самую нижнюю страницу в памяти. Ядро не позволяет людям отображать нулевую страницу, и в целом низкая память никогда не отображается, поэтому чтение с маленьких ошибочных адресов всегда должно быть ошибкой 4s.
error 5: чтение из области памяти, которая отображена, но не доступна для чтения.
Вероятно, это чтение указателя, который настолько произвольный, что указывает куда-то в область адресного пространства ядра. Это может быть защитная страница, но, по крайней мере, в некоторых случаях mmap()'ing вещи с PROT_NONE, кажется, заставляет Linux рассматривать их как неотмеченные области, так что вы получаете код ошибки 4 вместо этого.
Вы можете подумать, что это может быть область mmap()'d с другими разрешениями, но без PROT_READ, но, похоже, что на практике другие разрешения подразумевают возможность чтения памяти.
(Я предполагаю, что ядро Linux оптимизирует отображения PROT_NONE, даже не создавая записей в таблице страниц для этой области памяти, а не тщательно собирая PTE, которые запрещают все разрешения. Биты ошибки приходят прямо от CPU, так что если нет PTE, CPU говорит "ошибка из-за неразмеченной области", независимо от того, что Linux думает и сообщает, например, в /proc/PID/maps).
error 6: (данные) запись в неотмеченную область.
Это классическая запись в случайны или поврежденный указатель, в том числе в нулевой указатель (или через него). Как и при чтении, запись на защитные страницы mmap()'d с PROT_NONE будет обычно проявляться именно так, а не как "запись в отображенную область, на которую запрещены разрешения".
(Как и в случае с чтением, все записи с маленькими ошибочными адресами должны быть ошибкой 6, потому что никто в здравом уме не разрешает маппить низкую память).
error 7: запись в сопоставленную область, которая недоступна для записи.
Это либо "случайный" указатель, которому не повезло попасть на часть памяти, которая была отображена, либо попытка изменить данные, доступные только для чтения, например, классическая ошибка языка Си - попытка изменить строковую константу (как показано в первой записи). Вы также можете пытаться записать в файл, который был создан mmap() только для чтения, или вообще в память, в которой отсутствует PROT_WRITE.
(Все попытки записи в область адресного пространства ядра также приводят к этой ошибке, вместо ошибки 6).
error 14: попытка выполнить код из не отображенной области.
Это признак попытки вызова через искаженный указатель функции (или NULL-указатель), или, возможно, возврата из вызова, когда стек находится в неожиданном или поврежденном состоянии, так что адрес возврата недостоверен. Одним из источников искаженных указателей функций являются проблемы с использованием после освобождения, когда (освобожденный) объект содержит встроенные указатели функций.
(Ошибка 14 с ошибочным адресом 0 часто означает вызов функции через указатель NULL, что, в свою очередь, часто означает "косвенный вызов функции без проверки ее определения". В коде есть различные более масштабные причины этого).
error 15: попытка выполнить код из неисполняемой области памяти.
Вероятно, это все еще искаженный указатель функции или адрес возврата, просто вам не повезло (или повезло), и там есть отображенная память, а не пустота.
(Ваш код мог каким-то образом перепутать указатель функции с указателем данных, но это гораздо более редкая ошибка, чем перепутать данные, доступные для записи, с данными, доступными только для чтения).
Если вы сообщаете об ошибке segfault в чужой программе, код ошибки может дать полезные подсказки о том, что не так. В сочетании с адресом неисправности и указателем инструкции в тот момент времени, этого может быть достаточно, чтобы разработчики смогли обнаружить проблему даже без дампа ядра. Если вы отлаживаете свои собственные программы, то, надеюсь, у вас есть дампы ядра; они дадут вам много дополнительной информации (начиная с трассировки стека).
Выводы
В 64-битном x86 Linux, как правило, любой неисправный адрес больше 0x7fffffffffff сообщается как имеющий отображение, и поэтому вы получите коды ошибок 5, 7 или 15, соответствующие для чтения, записи и попытки выполнения. Это всегда случайные или поврежденные указатели (или адреса в более общем случае), поскольку у вас никогда не будет действительных адресов пространства пользователя.
Ошибочный адрес 0 (иногда печатается как '(null)', как описано в первой записи) сам по себе является указателем NULL. Маленький ошибочный адрес, например 0x18 или 0x200, обычно является смещением от указателя NULL. Вы получаете эти смещения, если у вас есть NULL-указатель p