Pour le développement C++ pour des systèmes 32 bits (que ce soit Linux, Mac OS ou Windows, PowerPC
ou x86) j'ai initialisé des pointeurs qui seraient sinon indéfinis (par exemple, ils ne peuvent pas immédiatement avoir une valeur propre) de cette manière :
int *pInt = reinterpret_cast(0xDEADBEEF);
(Pour éviter de taper et de rester DRY, le côté droit serait normalement dans une constante, par exemple BAD_PTR.)
Si pInt est déréférencé avant d'avoir une valeur propre, alors il crashera immédiatement sur la plupart des systèmes (au lieu de planter bien plus tard lorsque certaines mémoires sont écrasées ou que l'on entre dans une boucle infinie).
Évidemment, le comportement dépend du matériel sous-jacent (récupérer un entier de 4 octets à partir de l'adresse impaire 0xDEADBEEF d'un processus utilisateur peut être parfaitement valide), mais le crash a été à 100% fiable pour tous les systèmes pour lesquels j'ai développé jusqu'à présent (Mac OS 68xxx, Mac OS PowerPC, Linux Redhat Pentium, Windows GUI Pentium, Windows console Pentium). Par exemple, sur PowerPC, il est illégal (défaut d'accès) de récupérer un entier de 4 octets à partir d'une adresse impaire.
Quelle est une bonne valeur pour cela sur les systèmes 64 bits ?
5 votes
J'ai vu des systèmes où les premiers 1K de mémoire sont définis comme non valides. Donc, si un pointeur NULL est déréférencé, le processus mourra rapidement. 0xDEADBEEF pourrait être une position valide.
2 votes
@Robert : J'ai vu des systèmes où le vecteur d'interruption commence à 0, donc déréférencer un pointeur de fonction NULL semble simplement redémarrer le système (mais ne réinitialise pas les piles, etc.). N'importe quelle adresse pourrait être un emplacement valide pour quelque chose.
9 votes
@bk1e: IVT ne doit JAMAIS être accessible depuis le mode utilisateur. Mais vous avez raison en disant qu'il n'y a aucune raison pour laquelle l'adresse
0
ne peut pas être mappée. Sous Linux, il est facile de mapper vers l'adresse0
en modifiant une option dans le kernel. En tout cas, la leçon apprise ici est de ne pas utiliser des schémas stupides pour marquer les pointeurs comme invalides, utiliseznull
ou un drapeau distinct dans la structure. Supposer que ça va juste planter est complètement irresponsable et ignorant, si vous avez de la chance, vous aurez seulement une segfault, il est probable que cela pourrait conduire à une exécution de code à distance, et cela l'a déjà fait de nombreuses, nombreuses fois dans le passé.1 votes
C'est pourquoi C devrait être interdit. Personne ne sait comment l'utiliser. D'autre part, vous pourriez simplement vous assurer que votre programme mappe 0xdeadbeef sur une page de garde avant de l'exécuter, par exemple, mais tous les systèmes d'exploitation n'ont pas d'équivalent, vous pourriez donc toujours rencontrer la faille de sécurité sur certains systèmes d'exploitation. De plus, déclencher un segfault en soi ne permet pas nécessairement d'empêcher l'exécution de code à distance.
0 votes
@Longpoke: Je suis d'accord, mais tous les processeurs n'ont pas de MMU. Sur certains systèmes embarqués, NULL pointe vers quelque chose d'important et vous ne pouvez rien y faire. :)
2 votes
@bk1e, mais la norme C garantit que NULL (et respectivement 0 converti en pointeur) sera un pointeur invalide que vous ne pouvez pas derefencer.
2 votes
@iconiK : Que signifie "peut pas déréférencer"? Vous pouvez certainement déréférencer NULL, mais le résultat est un comportement indéfini (selon la section 6.5.3.2 de C99). Un pointeur nul est garanti de ne pas être égal à aucun objet ou fonction (selon la section 6.3.2.3 de C99), donc s'il y a un objet C ou une fonction C à l'adresse 0, le compilateur doit convertir
(void*)0
pour pointer quelque part ailleurs qu'à l'adresse 0. Cependant, la table des vecteurs d'interruption n'est pas un objet C ou une fonction C, donc je ne pense pas que les compilateurs soient obligés de garantir que NULL ne pointe pas vers la table des vecteurs d'interruption.2 votes
@bk1, eh bien, sauf si vous voulez explicitement un comportement indéfini, vous ne devriez pas déréférencer NULL, c'est pourquoi c'est le bon à utiliser pour les pointeurs initialisés mais non utilisés. Je n'ai pas dit que c'était l'adresse 0 ; la norme C garantit que 0 converti en pointeur donne le pointeur NULL. Que ce comportement soit modifié par une implémentation, ou que le déréférencement du pointeur NULL soit défini est en dehors du champ de la norme C (ou de C++ pour cela).
2 votes
@iconiK: Revenons au sujet d'origine : Vous pouvez accidentellement déréférencer 0xDEADBEEF, ce qui peut ou non causer un crash. Vous pouvez accidentellement déréférencer NULL, qui peut ou non être l'adresse 0, et qui peut ou non causer un crash. De nombreuses plates-formes utilisent du matériel de protection mémoire pour détecter lorsqu'un pointeur invalide est déréférencé, mais la spécification C ne l'exige pas. Par conséquent, il est préférable d'initialiser les pointeurs à NULL plutôt que de les initialiser à 0xDEADBEEF, mais cela ne garantit pas la détection des déréférences accidentelles sur toutes les plates-formes.