Perché programma Linux che derefrences (char*)0 non sempre segmentation fault?
-
20-09-2019 - |
Domanda
Sto testando il codice che è stato progettato per rilevare quando un processo figlio ha segfaulted.Immaginate la mia sorpresa quando questo codice non sempre segmentation fault:
#include <stdio.h>
int main() {
char *p = (char *)(unsigned long)0;
putchar(*p);
return 0;
}
Io sono in esecuzione su un Debian Linux kernel 2.6.26;il mio guscio è AT&T ksh93
da Debian ksh
pacchetto, la Versione M 93s+ 2008-01-31.A volte questo programma segfault ma per il resto è semplicemente termina in silenzio con uno stato di uscita diverso da zero, ma nessun messaggio.Il mio segnale di rilevamento programma segnala il seguente:
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
L'esecuzione in puro ksh
mostra che il segmentation fault è anche raro:
Running...
Running...
Running...
Running...
Running...
Running... Memory fault
Running...
È interessante notare che, bash
rileva correttamente il segfault ogni volta.
Ho due domande:
Qualcuno può spiegare questo comportamento?
Qualcuno può suggerire un semplice programma in C che segfault affidabile su ogni esecuzione?Ho anche provato
kill(getpid(), SIGSEGV)
, ma io ottenere risultati simili.
EDIT: jbcreix ha la risposta:il mio segmentation fault detector era rotto.Sono stato ingannato, perché ksh
ha lo stesso problema.Ho provato con bash
e bash
si è giusto ogni volta.
Il mio errore era che stavo passando WNOHANG
per waitpid()
, dove avrei dovuto passare zero.Non so cosa avrei potuto pensare!Uno si chiede che cosa è la questione con ksh
, ma che è una questione a parte.
Soluzione
La scrittura per NULL
sarà affidabile segmentation fault o errore di bus.
A volte un OS mappa una pagina di sola lettura a zero indirizzo.Così, a volte si può leggere NULL
.
Anche se C definisce il NULL
indirizzo di speciale, la 'realizzazione' di quello speciale stato è in realtà gestita dal Sistema Operativo Memoria Virtuale (VM) sottosistema.
VINO e dosemu bisogno di una mappa di una pagina NULL
per la compatibilità con Windows.Vedere mmap_min_addr
nel kernel Linux per ricompilare un kernel che non può fare questo.
mmap_min_addr
è attualmente un tema caldo grazie a una valorizzazione e di un pubblico di fiamma verso Linus (di Linux fama, ovviamente) da Theo de Raadt, di OpenBSD sforzo.
Se siete disposti a codice il bambino in questo modo, si può sempre chiamare: raise(SIGSEGV);
Inoltre, è possibile ottenere un garantite-di-segfault puntatore:
int *ptr_segv = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS, -1, 0);
Dove PROT_NONE
è la chiave di riserva di memoria, che non può essere letta.Per la versione a 32 bit di Intel Linux, PAGE_SIZE è di 4096.
Altri suggerimenti
Io non sono sicuro perché non hanno un comportamento coerente.Mi piacerebbe pensare che non è così esigente a livello con la lettura.O qualcosa di simile, anche se probabilmente sarei totalmente sbagliato.
Prova a scrivere a NULL.Questa sembra essere una costante per me.Non ho idea del perché si desidera utilizzare questo però.:)
int main()
{
*(int *)0 = 0xFFFFFFFF;
return -1;
}
La risposta alla domanda numero due da Wikipedia :
int main(void)
{
char *s = "hello world";
*s = 'H';
}