Blog 5 min de lecture

Rendez votre code C++ plus sûr en activant les vérifications natives du compilateur à l’exécution

Share this article
Rendez votre code C++ plus sûr en activant les vérifications natives du compilateur à l’exécution

Les vérifications à l'exécution en C++ sont des mécanismes ou des outils utilisés pour détecter des erreurs, des vulnérabilités ou des comportements inattendus pendant l'exécution d'un programme. Ces vérifications sont effectuées dynamiquement à l'exécution plutôt qu'à la compilation, et elles peuvent aider à identifier des problèmes qui pourraient ne pas apparaître lors d'une analyse statique ou d'une revue de code.

Dans le contexte du développement logiciel, le terme « sanitizer » désigne ce type de vérifications à l'exécution. Les sanitizers fonctionnent en instrumentant le code avec des vérifications supplémentaires ou des mécanismes de suivi pour détecter les erreurs de programmation courantes ou les vulnérabilités de sécurité. Lorsqu'un problème est détecté, le sanitizer fournit généralement un retour ou génère des informations de diagnostic pour aider les développeurs à comprendre et à résoudre le problème.

Cependant, tous les développeurs C++ ne savent pas que la quasi-totalité des compilateurs C++ fournissent des vérifications natives à l'exécution. Clang, GCC et le compilateur Microsoft offrent des sanitizers avancés pour renforcer la sûreté de votre code C++.

Le compilateur Microsoft active ces vérifications à l'exécution par défaut en mode débogage. En revanche, avec Clang et GCC, il faut les activer manuellement.

Voici une liste de quelques-uns des sanitizers les plus populaires :

  1. AddressSanitizer (ASan): AddressSanitizer détecte les erreurs de corruption mémoire telles que les dépassements de tampon, les utilisations après libération et les accès hors limites. Il fonctionne en allouant une région de mémoire fantôme (shadow memory) à côté de la mémoire du programme et en vérifiant les accès mémoire par rapport à cette mémoire fantôme pour détecter les violations.
  2. MemorySanitizer (MSan): MemorySanitizer détecte l'utilisation de mémoire non initialisée, qui peut conduire à des comportements indéfinis et à des vulnérabilités de sécurité. Il suit l'état d'initialisation des emplacements mémoire à l'exécution et signale toute tentative d'utilisation de mémoire non initialisée.
  3. UndefinedBehaviorSanitizer (UBSan): UndefinedBehaviorSanitizer détecte les comportements indéfinis dans le code C et C++, comme les dépassements d'entiers, les déréférencements de pointeurs nuls et les dépassements d'entiers signés. Il instrumente le code pour détecter et signaler les cas de comportement indéfini à l'exécution.
  4. ThreadSanitizer (TSan): ThreadSanitizer détecte les courses de données et autres bugs liés à la concurrence dans le code C et C++ multithread. Il fonctionne en analysant l'exécution du programme pour identifier les accès conflictuels à la mémoire partagée par différents threads.
  5. DataFlowSanitizer (DFSan): DataFlowSanitizer détecte les vulnérabilités de type « taint » en suivant le flux des données à travers le programme et en identifiant les opérations potentiellement dangereuses impliquant des données contaminées, comme les injections SQL ou les injections de commandes.

Clang

Clang fournit une liste d'options pour personnaliser les vérifications à l'exécution, comme décrit ici.

Par exemple :

% clang++ -fsanitize=signed-integer-overflow,null,alignment -fno-sanitize-recover=null -fsanitize-trap=alignment a.cc

Le programme poursuivra son exécution après les dépassements d'entiers signés, se terminera après la première utilisation invalide d'un pointeur nul, et déclenchera un piège après la première utilisation d'un pointeur mal aligné.

L'utilisation des sanitizers de Clang peut aider à identifier les bugs et les vulnérabilités tôt dans le processus de développement, avant qu'ils ne se manifestent comme des erreurs d'exécution ou des vulnérabilités de sécurité dans le code de production. Ils sont particulièrement précieux pour débusquer des problèmes difficiles à diagnostiquer que les méthodes de test traditionnelles pourraient ne pas détecter.

Compilateur C++ de Microsoft

Le compilateur Microsoft fournit également de nombreuses vérifications à l'exécution, comme décrit ici.

Lorsque vous déboguez un programme pour lequel les vérifications à l'exécution sont activées, l'action par défaut consiste à arrêter le programme et à basculer vers le débogueur lorsqu'une erreur d'exécution se produit. Vous pouvez modifier ce comportement par défaut pour n'importe quelle vérification à l'exécution. Pour plus d'informations, consultez Gérer les exceptions avec le débogueur.

GCC

GCC prend en charge un certain nombre d'options de ligne de commande qui contrôlent l'ajout d'instrumentation à l'exécution au code qu'il génère normalement. Par exemple, l'un des objectifs de l'instrumentation est de collecter des statistiques de profilage pour repérer les points chauds du programme, analyser la couverture de code ou réaliser des optimisations guidées par profil. Une autre catégorie d'instrumentation consiste à ajouter des vérifications à l'exécution pour détecter des erreurs de programmation comme les déréférencements de pointeurs invalides ou les accès hors limites aux tableaux, ainsi que des attaques délibérément hostiles comme l'écrasement de pile (stack smashing) ou le détournement de vtable C++. Une liste complète des sanitizers est disponible ici.

Comment faire de l'utilisation des sanitizers C++ une habitude ?

  1. Renseignez-vous sur les sanitizers: formez-vous, vous et votre équipe, aux différents types de sanitizers disponibles en C++, tels qu'AddressSanitizer (ASan), UndefinedBehaviorSanitizer (UBSan) et ThreadSanitizer (TSan). Comprenez comment chaque sanitizer détecte des types précis de bugs et de vulnérabilités dans votre code.
  2. Intégrez les sanitizers à votre processus de build: faites de l'activation des sanitizers une pratique standard lors du build de vos projets C++. Modifiez vos scripts de build ou vos fichiers de configuration (par exemple CMakeLists.txt) pour inclure les options de compilation qui activent les sanitizers souhaités.
  3. Commencez par un petit projet: commencez par intégrer les sanitizers dans un petit projet ou un module non critique de votre base de code. Cela vous permet de vous familiariser avec le processus sans vous submerger, ni vous ni votre équipe.
  4. Activez les sanitizers pour les tests et le débogage: prenez l'habitude d'exécuter vos tests et vos builds de débogage avec les sanitizers activés. Cela permet de détecter les bugs tôt dans le cycle de développement, ce qui les rend plus faciles et moins coûteux à corriger.
  5. Examinez régulièrement la sortie des sanitizers: prêtez attention à la sortie générée par les sanitizers pendant le processus de build. Prenez le temps de comprendre les problèmes détectés et leurs causes profondes. Utilisez ce retour pour améliorer votre code et prévenir des problèmes similaires à l'avenir.
  6. Intégrez les sanitizers aux revues de code: encouragez les membres de l'équipe à activer les sanitizers pendant les revues de code. Discutez des problèmes détectés par les sanitizers et travaillez ensemble pour les résoudre.
  7. Proposez formation et accompagnement: offrez des sessions de formation ou des ateliers pour aider les membres de l'équipe à comprendre comment utiliser efficacement les sanitizers. Fournissez soutien et conseils si nécessaire, en particulier à ceux qui découvrent les sanitizers.
  8. Documentez les bonnes pratiques: documentez les bonnes pratiques d'utilisation des sanitizers au sein de votre organisation. Incluez des directives sur le moment et la manière d'activer les sanitizers, sur l'interprétation de leur sortie et sur la résolution des problèmes courants qu'ils détectent.
  9. Fixez des objectifs et suivez les progrès: fixez des objectifs pour accroître l'utilisation des sanitizers au sein de votre équipe ou de votre organisation. Suivez régulièrement les progrès et célébrez les étapes franchies pour renforcer l'habitude d'utiliser les sanitizers.
  10. Amélioration continue: évaluez et améliorez en continu vos processus d'utilisation des sanitizers. Sollicitez les retours des membres de l'équipe et adaptez votre approche en fonction des leçons apprises.

En suivant ces étapes et en faisant de l'utilisation des sanitizers une pratique standard au sein de votre équipe, vous pouvez prendre l'habitude d'utiliser efficacement les sanitizers C++ pour améliorer la qualité et la sûreté de votre code.

Share this article