Problème d’écrasement de la mémoire

J’ai une application de code C. que je construisais en utilisant MS-VS2005. J’avais un tampon de données de sortie qui était alloué dynamicment à l’aide de malloc.

Pour certains cas de test, la taille de la mémoire en cours de traitement par malloc’d était inférieure à la taille de sortie réelle en octets générée. Cette sortie de taille plus grande a été écrite dans la mémoire tampon de taille plus petite, provoquant un dépassement de capacité de la mémoire tampon. À la suite de quoi le test s’était écrasé avec MSVS-2005 affichant une fenêtre “Heap corruption ….”

Je savais que cela avait à voir avec une allocation de mémoire dynamic, mais j’ai mis beaucoup de temps à trouver la cause du problème, car je ne doutais pas de l’allocation de mémoire car j’allouais une taille suffisante pour la sortie. Mais un cas de test particulier générait plus de sortie que ce que j’avais calculé, d’où le crash résultant.

Ma question est:

1.) Quels outils je peux utiliser pour détecter de telles conditions de débordement de mémoire tampon dynamic. Peuvent-ils également aider à détecter les éventuels débordements de mémoire tampon (que la mémoire tampon / la masortingce se trouve sur le tas, la stack ou la zone de mémoire globale)?

2.) Des outils de fuite de mémoire (comme par exemple Purify) ou des outils d’parsing de code tels que Lint, klocworks auraient-ils été utiles dans certains cas? Je crois qu’ils doivent être des outils d’parsing au moment de l’exécution.

Je vous remercie.

-UN D.

Une solution, que j’ai découverte pour la première fois dans le livre Writing Solid Code , consiste à “envelopper” l’API malloc() avec du code de diagnostic.

Tout d’abord, le diagnostic malloc() s’organise pour allouer des octets supplémentaires à une dernière sentinelle. Par exemple, quatre octets supplémentaires suivant la mémoire allouée sont réservés et contiennent les caractères ‘FINE’.

Plus tard, lorsque le pointeur de malloc() est passé à free() , une version de diagnostic correspondante de free() est appelée. Avant d’appeler l’implémentation standard de free() et d’abandonner la mémoire, la dernière sentinelle est vérifiée; il devrait être non modifié. Si la sentinelle est modifiée, le pointeur de bloc a été utilisé à mauvais escient après avoir été renvoyé par le diagnostic malloc() .

Il existe des avantages à utiliser une page de garde de protection de la mémoire plutôt qu’un modèle sentinelle pour détecter les dépassements de mémoire tampon. En particulier, avec un procédé basé sur un modèle, l’access illégal à la mémoire n’est détecté qu’après coup. Seules les écritures illégales sont détectées par la méthode du modèle sentinelle. La méthode de protection de la mémoire intercepte les lectures et les écritures illégales, qui sont immédiatement détectées.

Les fonctions de wrapper de diagnostic pour malloc() peuvent également traiter d’autres utilisations abusives de malloc() , telles que plusieurs appels à free() pour le même bloc de mémoire. De plus, realloc() peut être modifié pour toujours déplacer les blocs lorsqu’il est exécuté dans un environnement de débogage, afin de tester les appelants de realloc() .

En particulier, les enveloppes de diagnostic peuvent enregistrer tous les blocs alloués et libérés et signaler les memory leaks lorsque le programme se ferme. Les memory leaks sont des blocs alloués par non libérés pendant l’exécution du programme.

Lorsque vous enveloppez l’API malloc() , vous devez envelopper toutes les fonctions associées, y compris calloc(), realloc(), et strdup() .

La manière typique d’encapsuler ces fonctions est via des macros de préprocesseur:

 #define malloc (s) diagnostic_malloc (s, __FILE__, __LINE__)
 /* etc.... */

Si vous avez besoin de coder un appel à l’implémentation standard (par exemple, le bloc alloué sera transmis à une bibliothèque tierce, réservée uniquement à un binary, qui s’attend à libérer le bloc à l’aide de l’implémentation standard free() , les noms de fonction d’origine peut être accédé plutôt que par la macro du préprocesseur en utilisant (malloc)(s) – c’est-à-dire, placez des parenthèses autour du nom de la fonction.

Vous pouvez essayer d’allouer suffisamment de pages + 1 à l’aide de VirtualAlloc. Utilisez VirtualProtect avec PAGE_READONLY | PAGE_GUARD marque sur la dernière page, puis aligne l’allocation suspectée de sorte que la fin de l’object se trouve au début de la page protégée. Si tout se passe bien, vous devriez obtenir une violation d’access lors de l’access à la page de garde. Cela aide si vous savez approximativement quelle allocation est écrasée. Sinon, il faut outrepasser toutes les allocations, ce qui peut nécessiter beaucoup de mémoire supplémentaire (au moins 2 pages par allocation). Une variante de cette technique que je baptise ici “garde de page statistique” consiste à n’allouer de la mémoire au hasard que pour un pourcentage relativement faible d’allocations de cette manière, afin d’éviter une surcharge excessive des petits objects. Sur un grand nombre d’exécutions, vous devriez être capable de taper l’erreur. Dans ce cas, le générateur de nombres aléatoires devrait être paramétré. De même, vous pouvez allouer la page de garde devant l’object si vous suspectez un écrasement à une adresse inférieure (vous ne pouvez pas faire les deux en même temps, mais vous pouvez également mélanger de manière aléatoire).

Une mise à jour: il s’avère que l’utilitaire microsoft de gflags.exe (auparavant appelé pageheap.exe) prend déjà en charge le “gardien de page statistique”. J’ai donc réinventé la roue 🙂 Il suffit d’exécuter gflags.exe / p / enable [/ random 0-100] YourApplication.exe et exécutez votre application. Si vous utilisez un tas personnalisé ou des gardes personnalisés sur vos allocations de tas, vous pouvez simplement passer à utiliser HeapAlloc au moins pour attraper les bogues, puis revenir en arrière. Gflags.exe fait partie du package d’outils de support et peut être téléchargé à partir du centre de téléchargement Microsoft. Faites une recherche à cet emplacement.

PC-Lint peut détecter certaines formes de problèmes de malloc / nouvelle taille, mais je ne suis pas sûr que le vôtre l’aurait trouvée.

VS2005 a un bon débordement de tampon pour la vérification des objects de la stack en mode débogage (s’exécute à la fin de la fonction). Et il vérifie périodiquement le tas.

Pour ce qui est de l’aider à localiser les problèmes, j’ai tendance à commencer par utiliser des macros pour vider toutes les allocations afin de les comparer ultérieurement à la mémoire corrompue (lorsqu’elle est détectée).

Processus douloureux, alors je suis désireux d’apprendre de meilleures façons aussi.

Pensez à notre vérification de sécurité de la mémoire . Je pense qu’il va attraper toutes les erreurs que vous décrivez. Oui, il s’agit de la vérification en temps réel de chaque access, avec des frais généraux considérables (pas si mal que nous le pensons), avec l’avantage de diagnostiquer la première action du programme qui est erronée.