Temps lecture : 5 minutes - Posté le 23 juillet 2024 par Marc Hermeling
Le problème
Une erreur de codage courante dans une mise à jour de CrowdStrike Falcon a provoqué des pannes critiques du système dans le monde entier à partir du vendredi 19 juillet 2024. Le coupable ? Un déréférencement de pointeur nul (également connu sous le nom de CWE-476 ) dans un morceau de programme C++ qui s'exécutait avec un accès privilégié au système d'exploitation Windows.
Les internautes qui ont posté X ont émis toutes sortes d'hypothèses sur les erreurs de codage spécifiques et l'accès à Windows qui ont réussi à faire planter les systèmes dans le monde entier jusqu'à l'écran bleu. Par exemple, un internaute sur X a déclaré : « Il s'agit d'une région de mémoire non valide pour tout programme. Tout programme qui essaie de lire à partir de cette région SERA IMMÉDIATEMENT TUÉ PAR WINDOWS. » Un autre a suggéré que le pointeur menait à une boucle qui continuait à faire planter les systèmes, tandis que d'autres évoquent une corruption de mémoire . CrowdStrike lui-même a présenté la cause comme une « erreur logique » dans l'exécution sous Windows, ce qui ne nous dit pas grand-chose.
Une déréférence de pointeur nul est probablement la cause, mais elle peut être associée à un problème de variable non initialisée ( CWE-457 ). Ou, si la mémoire a été endommagée, un dépassement de tampon peut se produire ailleurs ( CWE-121 ou CWE-122 ).
Questions clés
En fin de compte, le crash a été causé par une ligne de code défectueuse. Alors, comment une ligne de code défectueuse peut-elle traverser tout le processus de publication sans être détectée par les tests de sécurité des applications statiques (SAST), les révisions de code, les tests unitaires, les tests de régression, etc. ? Et comment une mise à jour défectueuse peut-elle être déployée sur un si grand nombre d'ordinateurs sans que l'erreur dommageable ne soit détectée à temps pour annuler le déploiement ?
Au-delà de cela, pourquoi ce morceau de code a-t-il eu accès à une partie privilégiée du noyau Microsoft Windows lui permettant de faire planter l'ensemble de la machine au lieu de seulement l'application ? Je parie que l'équipe de CrowdStrike examine actuellement de près ses pratiques de développement et de déploiement de logiciels.
SAST profond
Depuis le crash, des questions ont afflué vers CodeSecure (le fournisseur de CodeSonar, une solution approfondie de test de sécurité des applications statiques) pour savoir si SAST peut détecter une telle erreur.
Étant donné que le code à l'origine du problème n'a pas encore été publié, il est difficile de répondre à cette question. Pour détecter ce type de problème, une solution SAST approfondie doit effectuer une analyse statique unitaire de compilation croisée avec exécution abstraite, ce qui signifie que la solution doit prendre en compte tous les chemins à travers le code source.
Toutes les solutions SAST ne disposent pas de cette fonctionnalité, mais CodeSonar , un outil SAST avancé destiné aux développeurs, en est doté. CodeSonar est bien connu pour sa capacité d'exécution abstraite et pour les profondeurs qu'il parcourt pour calculer tous les chemins possibles dans le code source afin de trouver des problèmes tels que les déréférencements de pointeurs NULL.
Le problème est que la plupart des outils SAST doivent approximer tous les chemins possibles et effectuer des calculs sur eux, tout en évitant de générer trop de faux positifs (c'est-à-dire des avertissements signalés qui ne sont pas en réalité des problèmes réels). Avant de signaler à un développeur des problèmes potentiels, l'outil doit développer une confiance suffisante dans le fait que quelque chose de grave peut se produire à la suite de ce défaut potentiel.
Le plus grand défi pour les outils SAST consiste à équilibrer les faux négatifs (problèmes que l’outil ne parvient pas à détecter) et les faux positifs (avertissements qui ne sont pas réellement des problèmes).
Les fonctionnalités SAST avancées de CodeSonar incluent des algorithmes de vérification de déréférencement de pointeur Null, qui sont suffisamment étendus pour signaler les déréférencements de NULL lui-même, ainsi que les déréférencements de valeurs proches de NULL. L'erreur de codage dans le cas de CrowdStrike implique des déréférencements de programme « 0x9c », soit 156 octets dans une structure éventuellement pointée par un pointeur. CodeSonar utilise sa valeur de configuration NULL_POINTER_THRESHOLD pour déterminer quand signaler un déréférencement de pointeur Null, la valeur par défaut étant 4096.
Dans ce cas, la déréférence s'est produite dans un pilote appelé csagent.dll. Si CodeSonar avait accès au pilote, il aurait pu calculer le chemin à travers le code dans le cadre de sa capacité d'analyse globale du programme et aurait détecté cela… Cela dépend de la complexité du chemin du code, du niveau d'indirections dans le chemin, etc. Difficile à dire sans avoir accès au code source de CrowdStrike lui-même, mais il y a de fortes chances que CodeSonar aurait détecté cette erreur s'il avait eu cet accès au code source.
Les algorithmes avancés de CodeSonar analysent et détectent également les types de variables non initialisées et les dépassements de tampon basés sur le tas et la pile qui pourraient également être impliqués ici. Voici un exemple d'une variable non initialisée qui avait auparavant un impact sur certains programmes de la NASA (mais qui est désormais corrigée).
MISRA
Suite à la panne, les utilisateurs ont également demandé à CodeSecure si le respect de la norme de codage MISRA C++ 2023 aurait permis d'éviter cette erreur. Les normes MISRA, généralement associées aux logiciels automobiles, ont défini un sous-ensemble de C/C++ qui limite le développeur à des versions de C/C++ sans risque de perte de mémoire.
En réponse à ces questions, quelques règles MISRA viennent à l’esprit, notamment :
- Catégories fourre-tout : les catégories fourre-tout imposent aux développeurs d'empêcher les comportements indéfinis ou non spécifiés et de limiter les échecs d'exécution. Plus précisément, MisraC++2023:4.1.3 stipule qu'aucun comportement indéfini ou critique non spécifié ne doit se produire. MISRA classe cette règle comme « indécidable », ce qui signifie qu'il est impossible pour un outil SAST de décider complètement à chaque fois si la règle est violée. CodeSonar inclut un ensemble de vérificateurs spécifiques à la règle 4.1.3 pour aider les développeurs à éviter de tels échecs.
- Règles spécifiques : Sans le code de mise à jour Falcon lui-même, nous ne pouvons que faire des suppositions éclairées sur ce qui s'est passé pour que le pointeur devienne nul. Cela dit, plusieurs règles MISRA demandent spécifiquement aux développeurs de garder le code sûr, facile à lire et à maintenir afin d'éviter ce type d'erreurs qui conduisent à d'énormes problèmes.
Par exemple:
- MisraC++2023 : 7.11.1 nullptr doit être la seule forme de la constante de pointeur nul
- MisraC++2023 : 8.2.6 Un objet de type intégral, énuméré ou pointeur vers void ne doit pas être converti en objet de type pointeur
- MisraC++2023 : 8.2.7 Un cast ne doit pas convertir un type de pointeur en un type intégral
- MisraC++2023 : 8.7.1 L'arithmétique du pointeur ne doit pas former un pointeur non valide
- MisraC++2023 : 8.7.2 La soustraction entre les pointeurs ne doit être appliquée qu'aux pointeurs qui adressent des éléments du même tableau
- MisraC++2023 : 11.6.1 Toutes les variables doivent être initialisées
- MisraC++2023 : 11.6.2 La valeur d'un objet ne doit pas être lue avant d'avoir été définie
- 7.11.1, 8.26, 8.2.7 et 11.6.1 sont décidables, tandis que 8.7.1, 8.7.2 et 11.6.2 sont indécidables, ce qui signifie que pour ces dernières règles, il est impossible de garantir que le code ne viole pas la règle.
Dans le cas de la mise à jour de CrowdStrike Falcon, la norme MISRA 7.11.1 peut s'appliquer ici. Elle indique au développeur de ne pas utiliser NULL et/ou 0 pour un pointeur, mais d'utiliser la constante null-pointer. Cela aide le compilateur et l'outil SAST à détecter les problèmes plus tôt, avant que le code ne sorte et ne cause des dommages.
Résumé
La norme MISRA C++ 2023 contient de nombreuses règles qui aident les développeurs à écrire du code C++ sécurisé en mémoire et à éviter ainsi de nombreux problèmes. L'utilisation de SAST avancé dans le processus de développement et de déploiement de logiciels est essentielle pour éviter les problèmes dans les logiciels déployés.
Advanced SAST détecte de nombreux déréférencements de pointeurs nuls, variables non initialisées et dépassements de tampon avant que le code n'atteigne les systèmes de production, ce qui permet aux entreprises de produits d'économiser de l'argent et de l'embarras, tout en protégeant leurs clients des dommages et des pannes comme celle survenue la semaine dernière. Si CrowdStrike avait le temps de fournir son code source, nous serions heureux de le tester avec CodeSonar. Mais je m'attends à ce que l'équipe de CrowdStrike soit un peu occupée en ce moment.