Почему программа Linux, которая разыменовывает (char*)0, не всегда выполняет segfault?

StackOverflow https://stackoverflow.com/questions/1735349

Вопрос

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

#include <stdio.h>

int main() {
  char *p = (char *)(unsigned long)0;
  putchar(*p);
  return 0;
}

Я работаю под управлением ядра Debian Linux 2.6.26;моя оболочка - это AT & T ksh93 из Debian ksh комплект поставки, Версия M 93s+ 2008-01-31.Иногда эта программа дает сбой, но в противном случае она просто молча завершается с ненулевым статусом выхода, но без сообщения.Моя программа обнаружения сигналов сообщает следующее:

segfault terminated by signal 11: Segmentation fault
segfault terminated by signal 53: Real-time signal 19
segfault terminated by signal 11: Segmentation fault
segfault terminated by signal 53: Real-time signal 19
segfault terminated by signal 53: Real-time signal 19
segfault terminated by signal 53: Real-time signal 19
segfault terminated by signal 53: Real-time signal 19

Работает под чистыми ksh показывает, что ошибка сегмента также встречается редко:

Running... 
Running... 
Running... 
Running... 
Running... 
Running... Memory fault
Running... 

Интересно, bash каждый раз корректно обнаруживает ошибку сегмента.

У меня есть два вопроса:

  1. Кто-нибудь может объяснить такое поведение?

  2. Кто-нибудь может предложить простую программу на языке Си, которая будет надежно выполнять сегментацию при каждом выполнении?Я тоже пробовалkill(getpid(), SIGSEGV), но я получаю похожие результаты.


Редактировать: у jbcreix есть ответ:мой детектор ошибок был сломан.Я был одурачен , потому что ksh у него та же проблема.Я пытался с bash и bash каждый раз делает это правильно.

Моя ошибка заключалась в том, что я проходил мимо WNOHANG Для waitpid(), где я должен был проходить мимо нуля.Я не знаю, о чем я могла думать!Возникает вопрос, в чем дело с ksh, но это отдельный вопрос.

Это было полезно?

Решение

Написание Для NULL надежно приведет к сбою сегмента или ошибке шины.

Иногда операционная система сопоставляет страницу, доступную только для чтения, с нулевым адресом.Таким образом, иногда вы можете читать из NULL.

Хотя C определяет NULL если адрес является специальным, "реализация" этого специального статуса фактически обрабатывается подсистемой виртуальной памяти (VM) операционной системы.

WINE и dosemu необходимо отобразить на странице по адресу NULL для обеспечения совместимости с Windows.Видишь mmap_min_addr в ядре Linux перестроить ядро, которое не может этого сделать.

mmap_min_addr в настоящее время это горячая тема из-за связанного с ней эксплойта и публичного обращения к Linus (очевидно, прославившему Linux) со стороны Тео де Раадта из OpenBSD.

Если вы готовы закодировать дочерний элемент таким образом, вы всегда можете вызвать: raise(SIGSEGV);

Кроме того, вы можете получить указатель guaranteed-to-segfault из: int *ptr_segv = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS, -1, 0);

Где PROT_NONE это ключ к резервированию памяти, к которой невозможно получить доступ.Для 32-разрядной версии Intel Linux размер СТРАНИЦЫ равен 4096.

Другие советы

Я не уверен, почему у него нет последовательного поведения.Я бы подумал, что он не так придирчив к чтению.Или что-то в этом роде, хотя я, вероятно, был бы совершенно неправ.

Попробуйте записать в NULL.Мне кажется, это вполне логично.Хотя я понятия не имею, зачем вам понадобилось это использовать.:)

int main()
{
    *(int *)0 = 0xFFFFFFFF;
    return -1;
}

Ответ на вопрос номер два из Википедия :

 int main(void)
 {
     char *s = "hello world";
     *s = 'H';
 }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top