Existe-t-il un moyen de déterminer (par programme, bien sûr) si un pointeur donné est "valide"? Vérifier NULL est facile, mais qu'en est-il des choses comme 0x00001234? Lorsque vous essayez de déréférencer ce type de pointeur, une exception / un crash se produit.
Une méthode multiplateforme est préférable, mais spécifique à la plate-forme (pour Windows et Linux) est également acceptable.
Mise à jour pour clarification: le problème ne vient pas des pointeurs périmés / libérés / non initialisés; à la place, j'implémente une API qui prend des pointeurs de l'appelant (comme un pointeur vers une chaîne, un descripteur de fichier, etc.). L'appelant peut envoyer (intentionnellement ou par erreur) une valeur non valide comme pointeur. Comment éviter un crash?
Réponses:
Vous ne pouvez pas faire ce chèque. Il n'y a tout simplement aucun moyen de vérifier si un pointeur est "valide". Vous devez avoir confiance que lorsque les gens utilisent une fonction qui prend un pointeur, ces gens savent ce qu'ils font. S'ils vous transmettent 0x4211 comme valeur de pointeur, vous devez alors faire confiance qu'il pointe vers l'adresse 0x4211. Et s'ils heurtaient "accidentellement" un objet, alors même si vous utilisiez une fonction du système d'exploitation effrayante (IsValidPtr ou autre), vous vous glisseriez toujours dans un bogue et n'échoueriez pas rapidement.
Commencez à utiliser des pointeurs nuls pour signaler ce genre de chose et dites à l'utilisateur de votre bibliothèque qu'il ne doit pas utiliser de pointeurs s'ils ont tendance à passer accidentellement des pointeurs invalides, sérieusement :)
la source
Voici trois façons simples pour un programme C sous Linux de s'introspecter sur l'état de la mémoire dans laquelle il s'exécute, et pourquoi la question a des réponses sophistiquées appropriées dans certains contextes.
Sous Microsoft Windows, il y a la fonction QueryWorkingSetEx qui est documentée sous l'API d'état du processus (également dans l'API NUMA). Comme corollaire de la programmation sophistiquée de l'API NUMA, cette fonction vous permettra également de faire de simples «tests de pointeurs de validité (C / C ++)», en tant que telle, il est peu probable qu'elle soit obsolète pendant au moins 15 ans.
la source
Empêcher un crash causé par l'appelant qui envoie un pointeur non valide est un bon moyen de créer des bogues silencieux difficiles à trouver.
N'est-il pas préférable que le programmeur utilisant votre API reçoive un message clair indiquant que son code est faux en le plantant plutôt que de le cacher?
la source
Sur Win32 / 64, il existe un moyen de le faire. Tentative de lecture du pointeur et attrape l'exception SEH résultante qui sera lancée en cas d'échec. S'il ne lance pas, c'est un pointeur valide.
Le problème avec cette méthode est cependant qu'elle renvoie simplement si vous pouvez ou non lire les données du pointeur. Il ne donne aucune garantie sur la sécurité du type ou sur un certain nombre d'autres invariants. En général, cette méthode ne sert à rien d'autre que de dire "oui, je peux lire cet endroit particulier en mémoire à un moment qui est maintenant passé".
Bref, ne faites pas ça;)
Raymond Chen a un article de blog sur ce sujet: http://blogs.msdn.com/oldnewthing/archive/2007/06/25/3507294.aspx
la source
AFAIK il n'y a aucun moyen. Vous devez essayer d'éviter cette situation en définissant toujours les pointeurs sur NULL après avoir libéré de la mémoire.
la source
Jetez un œil à ceci et à cette question. Jetez également un œil aux pointeurs intelligents .
la source
En ce qui concerne la réponse un peu plus haut dans ce fil:
Mon conseil est de rester loin d'eux, quelqu'un a déjà posté celui-ci: http://blogs.msdn.com/oldnewthing/archive/2007/06/25/3507294.aspx
Un autre article sur le même sujet et du même auteur (je pense) est celui-ci: http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx ("IsBadXxxPtr devrait vraiment s'appeler CrashProgramRandomly ").
Si les utilisateurs de votre API envoient de mauvaises données, laissez-les planter. Si le problème est que les données transmises ne sont utilisées que plus tard (et cela rend plus difficile la recherche de la cause), ajoutez un mode de débogage où les chaînes, etc. sont enregistrées à l'entrée. S'ils sont mauvais, ce sera évident (et probablement un crash). Si cela se produit trop souvent, cela peut valoir la peine de retirer votre API du processus et de les laisser planter le processus API au lieu du processus principal.
la source
Premièrement, je ne vois aucun intérêt à essayer de vous protéger de l'appelant qui tente délibérément de provoquer un crash. Ils pourraient facilement le faire en essayant d'accéder eux-mêmes via un pointeur invalide. Il existe de nombreuses autres façons - elles pourraient simplement écraser votre mémoire ou la pile. Si vous avez besoin de vous protéger contre ce genre de choses, vous devez exécuter un processus séparé en utilisant des sockets ou un autre IPC pour la communication.
Nous écrivons pas mal de logiciels qui permettent aux partenaires / clients / utilisateurs d'étendre les fonctionnalités. Inévitablement, tout bogue nous est signalé en premier, il est donc utile de pouvoir montrer facilement que le problème est dans le code du plug-in. De plus, il y a des problèmes de sécurité et certains utilisateurs sont plus fiables que d'autres.
Nous utilisons un certain nombre de méthodes différentes en fonction des exigences de performance / débit et de fiabilité. De la plus préférée:
processus séparés utilisant des sockets (passant souvent des données sous forme de texte).
processus séparés utilisant la mémoire partagée (si de grandes quantités de données doivent être transmises).
même processus séparer les threads via la file d'attente de messages (si des messages courts fréquents).
le même processus sépare les threads de toutes les données transmises allouées à partir d'un pool de mémoire.
même processus via un appel de procédure direct - toutes les données transmises allouées à partir d'un pool de mémoire.
Nous essayons de ne jamais recourir à ce que vous essayez de faire lorsque vous traitez avec des logiciels tiers - en particulier lorsque les plug-ins / bibliothèque nous sont fournis sous forme de code binaire plutôt que source.
L'utilisation d'un pool de mémoire est assez facile dans la plupart des cas et n'a pas besoin d'être inefficace. Si VOUS allouez les données en premier lieu, il est trivial de vérifier les pointeurs par rapport aux valeurs que vous avez allouées. Vous pouvez également stocker la longueur allouée et ajouter des valeurs «magiques» avant et après les données pour vérifier le type de données valide et les dépassements de données.
la source
J'ai beaucoup de sympathie pour votre question, car je suis moi-même dans une position presque identique. J'apprécie ce que beaucoup de réponses disent, et elles sont correctes - la routine fournissant le pointeur devrait fournir un pointeur valide. Dans mon cas, il est presque inconcevable qu'ils aient pu corrompre le pointeur - mais s'ils avaient réussi, ce serait MON logiciel qui plante, et MOI qui serait blâmé :-(
Mon exigence n'est pas de continuer après une erreur de segmentation - ce serait dangereux - je veux juste signaler ce qui est arrivé au client avant de résilier afin qu'il puisse réparer son code plutôt que de me blâmer!
Voici comment j'ai trouvé pour le faire (sous Windows): http://www.cplusplus.com/reference/clibrary/csignal/signal/
Pour donner un synopsis:
Maintenant, cela semble se comporter comme je l'espère (il affiche le message d'erreur, puis met fin au programme) - mais si quelqu'un peut repérer une faille, faites-le moi savoir!
la source
exit()
, cela contourne RAII et peut donc provoquer des fuites de ressources.exit()
et ma cloche d'alarme portable C ++ a commencé à sonner. Cela devrait convenir dans cette situation spécifique à Linux, où votre programme se fermerait de toute façon, désolé pour le bruit.man 2 signal
sur Linux a un paragraphe expliquant pourquoi.Sous Unix, vous devriez pouvoir utiliser un appel système du noyau qui vérifie le pointeur et renvoie EFAULT, tel que:
retour:
Il y a probablement un meilleur appel système à utiliser que open () [peut-être access], car il y a une chance que cela puisse conduire à la création d'un chemin de code réel, et à une exigence de fermeture ultérieure.
la source
Il n'y a aucune disposition en C ++ pour tester la validité d'un pointeur en tant que cas général. On peut évidemment supposer que NULL (0x00000000) est mauvais, et divers compilateurs et bibliothèques aiment utiliser des «valeurs spéciales» ici et là pour faciliter le débogage (par exemple, si jamais je vois un pointeur apparaître comme 0xCECECECE dans Visual Studio, je sais J'ai fait quelque chose de mal) mais la vérité est que comme un pointeur n'est qu'un index en mémoire, il est presque impossible de dire simplement en regardant le pointeur s'il s'agit du "bon" index.
Il existe diverses astuces que vous pouvez faire avec dynamic_cast et RTTI pour vous assurer que l'objet pointé est du type souhaité, mais elles nécessitent toutes que vous pointiez vers quelque chose de valide en premier lieu.
Si vous voulez vous assurer que votre programme peut détecter des pointeurs "non valides", mon conseil est le suivant: définissez chaque pointeur que vous déclarez soit sur NULL, soit sur une adresse valide immédiatement lors de la création et définissez-le sur NULL immédiatement après avoir libéré la mémoire vers laquelle il pointe. Si vous faites preuve de diligence dans cette pratique, la vérification de NULL est tout ce dont vous avez besoin.
la source
Il n'y a pas de moyen portable de le faire, et le faire pour des plates-formes spécifiques peut être n'importe où entre difficile et impossible. Dans tous les cas, vous ne devriez jamais écrire de code qui dépend d'une telle vérification - ne laissez pas les pointeurs prendre des valeurs invalides en premier lieu.
la source
Définir le pointeur sur NULL avant et après l'utilisation est une bonne technique. C'est facile à faire en C ++ si vous gérez des pointeurs au sein d'une classe par exemple (une chaîne):
la source
Ce n'est pas une très bonne politique d'accepter des pointeurs arbitraires comme paramètres d'entrée dans une API publique. Il est préférable d'avoir des types de "données simples" comme un entier, une chaîne ou une structure (je veux dire une structure classique avec des données simples à l'intérieur, bien sûr; officiellement, tout peut être une structure).
Pourquoi? Eh bien parce que, comme d'autres le disent, il n'y a pas de moyen standard de savoir si vous avez reçu un pointeur valide ou un pointant vers des fichiers indésirables.
Mais parfois, vous n'avez pas le choix - votre API doit accepter un pointeur.
Dans ces cas, il est du devoir de l'appelant de passer un bon pointeur. NULL peut être accepté comme valeur, mais pas comme pointeur vers des fichiers indésirables.
Pouvez-vous vérifier de quelque manière que ce soit? Eh bien, ce que j'ai fait dans un cas comme celui-là, c'était de définir un invariant pour le type vers lequel pointe le pointeur, et de l'appeler quand vous l'avez (en mode débogage). Au moins si l'invariant échoue (ou plante), vous savez que vous avez reçu une mauvaise valeur.
la source
Comme d'autres l'ont dit, vous ne pouvez pas détecter de manière fiable un pointeur invalide. Considérez certaines des formes qu'un pointeur non valide peut prendre:
Vous pourriez avoir un pointeur nul. C'est celui que vous pouvez facilement vérifier et faire quelque chose.
Vous pourriez avoir un pointeur vers quelque part en dehors de la mémoire valide. Ce qui constitue une mémoire valide varie en fonction de la façon dont l'environnement d'exécution de votre système configure l'espace d'adressage. Sur les systèmes Unix, il s'agit généralement d'un espace d'adressage virtuel commençant à 0 et allant jusqu'à un grand nombre de mégaoctets. Sur les systèmes embarqués, cela peut être assez petit. Il peut ne pas commencer à 0, dans tous les cas. Si votre application s'exécute en mode superviseur ou équivalent, votre pointeur peut faire référence à une adresse réelle, qui peut ou non être sauvegardée avec de la mémoire réelle.
Vous pourriez avoir un pointeur vers quelque part dans votre mémoire valide, même à l'intérieur de votre segment de données, bss, pile ou tas, mais ne pointant pas sur un objet valide. Une variante de ceci est un pointeur qui pointait vers un objet valide, avant que quelque chose de mauvais n'arrive à l'objet. Les mauvaises choses dans ce contexte incluent la désallocation, la corruption de la mémoire ou la corruption du pointeur.
Vous pouvez avoir un pointeur illégal, tel qu'un pointeur avec un alignement illégal pour l'objet référencé.
Le problème s'aggrave encore lorsque vous considérez les architectures basées sur le segment / décalage et d'autres implémentations de pointeurs étranges. Ce genre de chose est normalement caché au développeur par de bons compilateurs et une utilisation judicieuse des types, mais si vous voulez percer le voile et essayer de déjouer le système d'exploitation et les développeurs de compilateurs, eh bien, vous pouvez, mais il n'y a pas de moyen générique. pour le faire qui gérera tous les problèmes que vous pourriez rencontrer.
La meilleure chose à faire est d'autoriser le crash et de publier de bonnes informations de diagnostic.
la source
Cet article MEM10-C. Définir et utiliser une fonction de validation de pointeur indique qu'il est possible de faire une vérification dans une certaine mesure, en particulier sous Linux OS.
la source
En général, c'est impossible à faire. Voici un cas particulièrement méchant:
Sur de nombreuses plates-formes, cela imprimera:
Vous forcez le système d'exécution à interpréter de manière incorrecte les bits de mémoire, mais dans ce cas, il ne va pas planter, car les bits ont tous un sens. Cela fait partie de la conception de la langue (regardez le polymorphisme C-style avec
struct inaddr
,inaddr_in
,inaddr_in6
), de sorte que vous ne pouvez pas protéger efficacement contre sur toute plateforme.la source
C'est incroyable la quantité d'informations trompeuses que vous pouvez lire dans les articles ci-dessus ...
Et même dans la documentation Microsoft MSDN, IsBadPtr est censé être interdit. Eh bien, je préfère une application qui fonctionne plutôt que de planter. Même si le travail à terme peut ne pas fonctionner correctement (tant que l'utilisateur final peut continuer avec l'application).
En googlant, je n'ai trouvé aucun exemple utile pour Windows - j'ai trouvé une solution pour les applications 32 bits,
http://www.codeproject.com/script/Content/ViewAssociatedFile.aspx?rzp=%2FKB%2Fsystem%2Fdetect-driver%2F%2FDetectDriverSrc.zip&zep=DetectDriverSrc%2FDetectDriver%2FsrcF%2Fdrobid.c. = 2
mais je dois également prendre en charge les applications 64 bits, donc cette solution n'a pas fonctionné pour moi.
Mais j'ai récolté les codes sources de wine et j'ai réussi à cuisiner un type de code similaire qui fonctionnerait également pour les applications 64 bits - en attachant le code ici:
Et le code suivant peut détecter si le pointeur est valide ou non, vous devez probablement ajouter une vérification NULL:
Cela obtient le nom de la classe du pointeur - je pense que cela devrait suffire à vos besoins.
Une chose qui, je le crains encore, est la performance de la vérification des pointeurs - dans l'extrait de code ci-dessus, il y a déjà 3-4 appels d'API en cours - pourrait être excessive pour les applications critiques en temps.
Ce serait bien si quelqu'un pouvait mesurer la surcharge de la vérification du pointeur par rapport, par exemple, aux appels C # / c ++ gérés.
la source
En effet, quelque chose pourrait être fait sous une occasion spécifique: par exemple si vous voulez vérifier si une chaîne de pointeur de chaîne est valide, l'utilisation de write (fd, buf, szie) syscall peut vous aider à faire la magie: laissez fd être un descripteur de fichier temporaire fichier que vous créez pour le test, et buf pointant sur la chaîne que vous testez, si le pointeur n'est pas valide, write () renverrait -1 et errno mis à EFAULT, ce qui indique que buf est en dehors de votre espace d'adressage accessible.
la source
Ce qui suit fonctionne sous Windows (quelqu'un l'a suggéré avant):
La fonction doit être une méthode statique, autonome ou statique d'une classe. Pour tester en lecture seule, copiez les données dans la mémoire tampon locale. Pour tester l'écriture sans modifier le contenu, écrivez-les. Vous ne pouvez tester que les premières / dernières adresses. Si le pointeur n'est pas valide, le contrôle sera passé à «doSomething», puis hors des crochets. N'utilisez simplement rien nécessitant des destructeurs, comme CString.
la source
Sous Windows, j'utilise ce code:
Usage:
la source
IsBadReadPtr (), IsBadWritePtr (), IsBadCodePtr (), IsBadStringPtr () pour Windows.
Celles-ci prennent un temps proportionnel à la longueur du bloc, donc pour vérifier la cohérence, je vérifie simplement l'adresse de départ.
la source
J'ai vu diverses bibliothèques utiliser une méthode pour vérifier la mémoire non référencée et autres. Je crois qu'ils "écrasent" simplement les méthodes d'allocation de mémoire et de désallocation (malloc / free), qui ont une certaine logique qui garde la trace des pointeurs. Je suppose que c'est exagéré pour votre cas d'utilisation, mais ce serait une façon de le faire.
la source
Techniquement, vous pouvez remplacer l'opérateur new (et supprimer ) et collecter des informations sur toute la mémoire allouée, de sorte que vous pouvez avoir une méthode pour vérifier si la mémoire du tas est valide. mais:
vous avez toujours besoin d'un moyen de vérifier si le pointeur est alloué sur stack ()
vous devrez définir ce qui est un pointeur `` valide '':
a) la mémoire sur cette adresse est allouée
b) la mémoire à cette adresse est l' adresse de début de l'objet (par exemple, l'adresse n'est pas au milieu d'un énorme tableau)
c) la mémoire à cette adresse est l' adresse de début de l'objet du type attendu
Conclusion : l'approche en question n'est pas une méthode C ++, vous devez définir certaines règles qui garantissent que la fonction reçoit des pointeurs valides.
la source
Il n'y a aucun moyen d'effectuer cette vérification en C ++. Que devez-vous faire si un autre code vous transmet un pointeur non valide? Vous devriez vous écraser. Pourquoi? Consultez ce lien: http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx
la source
Addendum à la (aux) réponse (s) acceptée (s):
Supposons que votre pointeur ne puisse contenir que trois valeurs - 0, 1 et -1 où 1 signifie un pointeur valide, -1 un pointeur non valide et 0 un autre non valide. Quelle est la probabilité que votre pointeur soit NULL, toutes les valeurs étant également probables? 1/3. Maintenant, retirez le cas valide, donc pour chaque cas invalide, vous avez un ratio de 50:50 pour attraper toutes les erreurs. Ça a l'air bien non? Mettez cela à l'échelle pour un pointeur de 4 octets. Il y a 2 ^ 32 ou 4294967294 valeurs possibles. Parmi ceux-ci, une seule valeur est correcte, une est NULL et il vous reste 4294967292 autres cas non valides. Recalculer: vous avez un test pour 1 cas invalide sur (4294967292+ 1). Une probabilité de 2.xe-10 ou 0 pour la plupart des raisons pratiques. Telle est la futilité du contrôle NULL.
la source
Vous savez, un nouveau pilote (au moins sur Linux) capable de cela ne serait probablement pas si difficile à écrire.
D'un autre côté, ce serait une folie de construire vos programmes comme ça. À moins que vous n'ayez une utilisation vraiment spécifique et unique pour une telle chose, je ne le recommanderais pas. Si vous construisez une application volumineuse chargée de contrôles de validité de pointeur constants, elle sera probablement terriblement lente.
la source
S'ils ne fonctionnent pas, la prochaine mise à jour de Windows résoudra le problème? S'ils ne fonctionnent pas au niveau du concept, la fonction sera probablement complètement supprimée de l'API Windows.
La documentation MSDN prétend qu'ils sont interdits, et la raison en est probablement un défaut de conception ultérieure de l'application (par exemple, en général, vous ne devriez pas manger de pointeurs invalides en silence - si vous êtes en charge de la conception de l'application entière bien sûr), et performances / temps de vérification du pointeur.
Mais vous ne devriez pas prétendre qu'ils ne fonctionnent pas à cause d'un blog. Dans mon application de test, j'ai vérifié qu'ils fonctionnent.
la source
ces liens peuvent être utiles
_CrtIsValidPointer Vérifie qu'une plage de mémoire spécifiée est valide pour la lecture et l'écriture (version de débogage uniquement). http://msdn.microsoft.com/en-us/library/0w1ekd5e.aspx
_CrtCheckMemory Confirme l'intégrité des blocs de mémoire alloués dans le tas de débogage (version de débogage uniquement). http://msdn.microsoft.com/en-us/library/e73x0s4b.aspx
la source