Pourquoi Linux programme-t-il que les déréfrences (char *) 0 ne sont pas toujours fausses?

Je teste le code conçu pour détecter le moment où un processus enfant est en échec. Imaginez ma surprise quand ce code ne se sépare pas toujours:

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

Je cours sous un kernel Debian Linux 2.6.26; mon shell est le AT & T ksh93 du paquet Debian ksh , version M 93s + 2008-01-31. Parfois, ce programme se sépare mais il se termine simplement en mode silencieux avec un statut de sortie différent de zéro mais pas de message. Mon programme de détection de signal rapporte ce qui suit:

 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 

Courir sous ksh pur montre que le segfault est également rare:

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

Fait intéressant, bash détecte correctement le segfault à chaque fois .

J’ai deux questions:

  1. Quelqu’un peut-il expliquer ce comportement?

  2. Quelqu’un peut-il suggérer un programme en C simple qui bloquera de manière fiable chaque exécution? J’ai aussi essayé de kill(getpid(), SIGSEGV) , mais j’obtiens des résultats similaires.


EDIT: jbcreix a la réponse : mon détecteur de segfault était cassé. J’ai été dupe parce que ksh a le même problème. J’ai essayé avec bash et bash fait bien chaque fois.

Mon erreur était que je passais WNOHANG à waitpid() , où j’aurais dû passer zéro. Je ne sais pas ce que j’aurais pu penser! On se demande quel est le problème avec ksh , mais c’est une question distincte.

Si vous NULL à NULL , une erreur de segmentation ou une erreur de bus se produit.

Parfois, un système d’exploitation mappe une page en lecture seule sur l’adresse zéro. Ainsi, vous pouvez parfois lire à partir de NULL .

Bien que C définisse l’adresse NULL comme spéciale, l’implémentation de ce statut spécial est en fait gérée par le sous-système Virtual Memory (VM) du système d’exploitation.

WINE et dosemu doivent mapper une page à la compatibilité NULL pour Windows. Voir mmap_min_addr dans le kernel Linux pour reconstruire un kernel qui ne peut pas faire cela.

mmap_min_addr est actuellement un sujet d’actualité en raison d’un exploit apparent et d’une flamme publique envers Linus (de la renommée Linux, évidemment) de Theo de Raadt, de l’effort OpenBSD.

Si vous souhaitez coder l’enfant de cette façon, vous pouvez toujours appeler: raise(SIGSEGV);

En outre, vous pouvez obtenir un pointeur garanti à segfault à partir de: int *ptr_segv = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS, -1, 0);

PROT_NONE est la clé pour réserver de la mémoire PROT_NONE . Pour Intel Linux 32 bits, PAGE_SIZE est 4096.

Je ne sais pas pourquoi il n’a pas un comportement cohérent. Je pense que ce n’est pas aussi difficile à lire. Ou quelque chose comme ça, bien que je sois probablement complètement faux.

Essayez d’écrire à NULL. Cela semble être cohérent pour moi. Je n’ai aucune idée de la raison pour laquelle vous voulez l’utiliser. 🙂

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

La réponse à la question numéro deux de Wikipedia :

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