2009-11-14 9 views
6

Ich teste Code, der entwickelt wurde, um zu erkennen, wenn ein untergeordneter Prozess segfailed ist. Stellen Sie sich meine überrascht, wenn dieser Code nicht immer segfault:Warum deklariert Linux-Programm (char *) 0 nicht immer segfault?

#include <stdio.h> 

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

Ich bin unter einem Debian Linux 2.6.26-Kernel ausgeführt wird; Meine Shell ist die AT & T ksh93 aus dem Debian ksh Paket, Version M 93s + 2008-01-31. Manchmal wird dieses Programm segfault, aber ansonsten wird es einfach still mit einem Nicht-Null-Exit-Status, aber ohne Nachricht beendet. Mein signaldetektierenden Programm meldet folgende:

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 

Laufen unter reinen ksh zeigt, dass der segfault auch selten:

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

Interessanterweise bash richtig erkennt die segfault jedes Mal.

Ich habe zwei Fragen:

  1. Kann jemand dieses Verhalten erklären?

  2. Kann jemand ein einfaches C-Programm vorschlagen, das bei jeder Ausführung zuverlässig segfault? Ich habe auch versucht kill(getpid(), SIGSEGV), aber ich bekomme ähnliche Ergebnisse.


EDIT: jbcreix hat die Antwort: mein segfault Detektor defekt war. Ich wurde getäuscht, weil ksh das gleiche Problem hat. Ich habe versucht mit bash und bash bekommt es jedes Mal richtig.

Mein Fehler war, dass ich WNOHANG an waitpid() übergeben habe, wo ich Null hätte übergeben sollen. Ich weiß nicht, was ich hätte denken können! Man fragt sich, was ist los mit ksh, aber das ist eine separate Frage.

+0

Was mit 'Ausgang falsch (0)'? Wenn Sie möchten, dass das Kind beendet wird ... – pmg

+0

Ich möchte nicht, dass das Kind austritt --- Ich möchte, dass es segfault. Warum? Ich baue einen Segfault-Detektor und ich muss ihn testen! –

+1

Norman, ich habe es auf Ihre Anfrage getestet. Mein Code funktioniert genau so, wie ich es sagte, danke. Nicht sicher, was auf Ihrem System falsch ist, vielleicht der Elternprozess, den Sie verwenden, um Signale UND ksh abzufangen? Ich habe den Kernel 2.6.28.7 verwendet, um die mmap-Antwort zu testen. Funktioniert auch mit Raise (SIG_SEGV). –

Antwort

12

Schreiben-NULL zuverlässig oder Busfehler segfault.

Manchmal wird ein Betriebssystem eine schreibgeschützte Seite der Nulladresse zuordnen. So können Sie manchmal von NULL lesen.

Obwohl C die Adresse NULL als speziell definiert, wird die 'Implementierung' dieses speziellen Status tatsächlich vom Subsystem des virtuellen Speichers (VM) des Betriebssystems gehandhabt.

WINE und dosemu müssen eine Seite unter NULL für Windows-Kompatibilität zuordnen. Siehe mmap_min_addr im Linux-Kernel, um einen Kernel wiederherzustellen, der das nicht kann.

mmap_min_addr ist derzeit ein heißes Thema wegen eines verwandten Exploits und einer öffentlichen Flamme in Richtung Linus (von Linux-Ruhm offensichtlich) von Theo de Raadt, von der OpenBSD-Bemühung.

Wenn Sie bereit sind, das Kind auf diese Weise zu kodieren, können Sie jederzeit anrufen: raise(SIGSEGV);

Sie können aber auch eine garantierte zu segfault Zeiger aus erhalten: int *ptr_segv = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS, -1, 0);

Wo PROT_NONE ist der Schlüssel um Speicher zu reservieren, auf den nicht zugegriffen werden kann. Für 32-Bit-Intel-Linux, ist PAGE_SIZE 4096.

+0

Heh, die beiden größten Flamer in OSS treffen sich, es ist ... wie ... trifft die unwiderstehliche Kraft auf das unbewegliche Objekt ... wie ... zwei Lokomotiven, die frontal zusammenstoßen ... Ich wünschte, ich könnte Tickets verkaufen – DigitalRoss

+0

Heath , Würde ich gerne jetzt k, wenn Sie Ihre Antwort auf einem Linux-System getestet. Ich kann es nicht schaffen ... –

+0

Heath, ich habe es versucht und habe '' raise (SIGSEGV) 'auch versucht. Gleiches Problem. Nicht überraschend wie raise (SIGSEGV) soll äquivalent zu kill (getpid(), SIGSEGV) in einem single-threaded Programm sein. Hast du deine Vorschläge getestet? Ich würde gerne wissen, ob meine (schrecklichen) Ergebnisse reproduzierbar sind. –

1

Ich bin mir nicht sicher, warum es kein konsistentes Verhalten hat. Ich würde denken, dass es beim Lesen nicht so pingelig ist. Oder so etwas, obwohl ich wahrscheinlich völlig falsch liegen würde.

Versuchen Sie, bei NULL zu schreiben. Dies scheint für mich konsistent zu sein. Ich habe keine Ahnung, warum du das trotzdem benutzen willst. :)

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

Die Antwort Nummer zwei von Wikipedia in Frage:

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

Ich stimme nicht mit dem überein, was Wikipedia sagt. Das Programm * kann * einen Segmentierungsfehler erzeugen, aber wir können nicht sicher sein. Das Verhalten ist nicht definiert IIRC. –

+0

Ja, einige Compiler dürfen Literale im (Lese-/Schreib-) Datensegment platzieren. Ich erinnere mich an den Schock, als gcc die Änderung vorgenommen hat. Ich denke, ich bin mit mir selbst verabredet ... –