Connexion
Abonnez-vous

[MàJ] Fiasco CrowdStrike : détails techniques, 8,5 millions de machines touchées selon Microsoft

78 minutes ont suffi

[MàJ] Fiasco CrowdStrike : détails techniques, 8,5 millions de machines touchées selon Microsoft

Flock

Mise à jour le 21 juillet à 11h50 : nous avons ajouté à la fin de l’actualité le thread de Tavis Ormandy qui revient sur le sujet des pointeurs NULL, avec une autre explication. Nous avons également ajouté le fait que, selon Microsoft, 8,5 millions de machines auraient été touchées par cette mise à jour problématique.

Le 21 juillet à 11h53

Article original le 20 juillet à 14 h : Impossible de passer à côté : hier, des pannes en cascades se sont produites un peu partout dans le monde (aéroports, bourses, hôpitaux et d’innombrables sociétés). Des ordinateurs sous Windows affichaient des écrans bleus de la mort, les fameux BSOD. La faute à une mise à jour de CrowdStrike, une société spécialisée dans la cybersécurité pour les entreprises. L’ANSSI a d’ailleurs publié un bulletin d’alerte.

78 minutes en ligne, des heures pour réparer les dégâts

De son côté, CrowdStrike vient de mettre en ligne un billet de blog avec des détails techniques sur le fiasco d’hier.

On commence par un résumé des éléments que l’on connait déjà : « Le 19 juillet 2024 à 04h09 UTC [6h09 heure de Paris, ndlr], dans le cadre des opérations courantes, CrowdStrike a publié une mise à jour de la configuration des capteurs pour les systèmes Windows. Cette mise à jour de configuration a déclenché une "erreur logique" entraînant un crash du système et un écran bleu (BSOD) sur les systèmes concernés ».

La mise à jour fautive a été corrigée à 07h27 heure de Paris (05h27 UTC). Elle est donc restée en ligne 78 minutes seulement. Un délai largement suffisant pour causer de gros dégâts à travers le monde. CrowdStrike le répète une nouvelle fois : « Ce problème n’est pas la conséquence ou lié à une cyberattaque ».

Moins de 1 % des machines sous Windows impactées

Dans un billet de blog, Microsoft fait les comptes : nous estimons actuellement que la mise à jour de CrowdStrike a affecté 8,5 millions d’appareils sous Windows, soit moins de un pour cent du parc ». L’entreprise rappelle encore une fois qu’il ne s'agit pas d’un incident Microsoft, mais ajoute qu’il a un eu « un fort impact » sur son écosystème.

Microsoft remet ce chiffre en perspective : « Bien que ce pourcentage soit faible, les vastes impacts économiques et sociétaux reflètent l’utilisation de CrowdStrike par des entreprises qui s’occupent de nombreux services critiques. Cet incident démontre la nature interconnectée de notre vaste écosystème ».

Attention, les escrocs sont de sortie

Si le souci a rapidement été réglé du côté de CrowdStrike, c’est une autre paire de manches chez les clients. Il faut parfois accéder physiquement aux machines (y compris des serveurs dans des datacenters) pour corriger le souci. « De nombreuses entreprises sont toujours aux prises avec les conséquences des perturbations », a indiqué ce midi le Bureau fédéral de la sécurité des technologies de l’information (BSI) allemand.

Et, au-delà des problèmes liés à l’indisponibilité des machines, le BSI met en garde contre « les cybercriminels qui exploitent les incidents pour diverses formes de phishing, d’escroquerie ou de faux sites Web ».

Le cas de CrowdStrike ne déroge pas à la règle et l’entreprise met d’ailleurs en garde. Elle « recommande aux organisations de s’assurer qu’elles communiquent avec les représentants de CrowdStrike par le biais des canaux officiels et de respecter les conseils techniques fournis par les équipes d’assistance de CrowdStrike ». Ici, elle met aussi en garde contre de faux sites.

Channel Files et le fameux C-00000291*.sys…

Pour revenir au cœur du problème, les fichiers dont il est question sont appelés « Channel Files », CrowdStrike propose un lien pour en savoir davantage… mais il nécessite un compte pour y accéder. C’était déjà le cas hier matin au début de la panne : les informations étaient pendant un temps réservées aux clients de l’entreprise. On a vu mieux sur la transparence, surtout dans un billet de blog public.

Quoi qu’il en soit, ces mises à jour « font partie du fonctionnement normal de Falcon Sensor et se produisent plusieurs fois par jour […] Ce n’est pas un nouveau processus », explique l’entreprise comme pour essayer de rassurer ses utilisateurs.

Ces Channel Files se trouvent dans le répertoire C:\Windows\System32\drivers\CrowdStrike\ et leur nom commence par « C- ». Nous le savons déjà, le fautif est un fichier sous la forme C-00000291*.sys. Il faut d’ailleurs le supprimer pour éviter les écrans bleus en série… à condition d’y arriver. Ce n’est pas toujours simple. Suivant les cas, il faut avoir accès à un compte local d’administration et/ou la clé de récupération Bitlocker.

… qui « ne sont pas des pilotes du noyau »

Les choses se compliquent un peu dans la suite des explications : « Bien que les Channel Files se terminent par l'extension SYS, ce ne sont pas des pilotes du noyau ». La fin de la citation est soulignée dans le billet de blog de l’entreprise.

« Channel File 291 contrôle la manière dont Falcon évalue l'exécution des canaux nommés [Named pipes en anglais dans le texte, ndlr] sur les systèmes Windows. Les canaux nommés sont utilisés pour la communication classique, interprocessus ou intersystèmes dans Windows ». Même son de cloche chez Microsoft, qui précise qu’ils offrent « plus de fonctionnalités que les canaux anonymes ».

Sur cette autre page, Microsoft ajoute que ces fameux « canaux nommés sont souvent utilisés par les attaquants afin […] de communiquer avec un implant malveillant. Il peut s’agir d’une activité légitime ou du signe que l’hôte est compromis ». Il est donc important pour les solutions de cybersécurité de les surveiller afin de détecter au plus tôt des risques.

CrowdStrike corrige son fichier

La mise à jour de 6h09 permettait justement de « cibler de nouveaux canaux nommés observés et utilisés par les frameworks C2 courants dans les cyberattaques ». Quelques précisions sur les « frameworks C2 » du billet de blog. Le C2 signifie Command and Control (avec deux C, vous l’avez ?) et framework renvoie vers une solution complète pour les pirates, avec serveur, client et agent. Bref, une « simple » (en apparence) mise à jour des signaux pour détecter au plus vite une potentielle attaque.

Au lieu de faire correctement son travail, la mise à jour « a déclenché une "erreur logique" qui a entraîné un crash du système d'exploitation ». CrowdStrike a « corrigé l'"erreur logique" en mettant à jour le contenu du Channel File 291 », mais l’entreprise ne donne aucune précision sur ce qu’elle appelle « erreur logique » et ses causes. Ses conséquences sont par contre bien connues.

Quoi qu’il en soit, l’entreprise affirme que sa solution Falcon continue de surveiller et de protéger contre les détournements de canaux nommés par des attaquants.

Inquiétude et rumeurs

L‘explication a quand même de quoi surprendre, comme l’explique Neil Madden sur Mastodon : « euh, c'est un peu inquiétant que leur réponse soit "nous avons corrigé le fichier de configuration", et non "nous avons corrigé le bug qui permet aux fichiers de configuration de faire tomber la moitié du monde" ». On se demande, en effet, comment des modifications dans un fichier de configuration (qui semblerait ensuite récupéré par le pilote) peut arriver à une telle catastrophe, sans être bloqué par le programme Falcon.

CrowdStrike en profite pour tordre le cou à une rumeur qui se répandait sur les réseaux sociaux : « Ceci n'est pas lié aux octets nuls contenus dans le Channel Files 291 ou dans tout autre Channel Files ». Et un rappel pour finir : Linux et macOS « n'utilisent pas Channel File 291 » et ne sont donc pas concernés par le « bug » du jour.

Reste que CrowdStrike ne s’explique pas sur un point crucial : comment une telle mise à jour a pu être déployée auprès de ses clients et comment un fichier de configuration peut entrainer une telle cacophonie. Vu la rapidité et l’étendue des dégâts, des tests auraient dû permettre de s’en apercevoir très rapidement.

L‘entreprise se contente d'expliquer qu’elle « comprend comment ce problème s'est produit », mais sans donner plus de détails. Elle promet de revenir avec de plus amples informations sur « les causes profondes au fur et à mesure que l’enquête progresse » dès que possible.

Pointeur NULL, le retour ? Pas si vite…

Sur les réseaux sociaux, la cause profonde pointerait pour certains vers un problème de… pointeur, comme l’indiquait Evis Drenova sur X hier en fin de journée. Zach Vorhies y va aussi de son explication dans un thread détaillé : « l'ordinateur a essayé de lire l'adresse mémoire 0x9c », mais elle est « non valide ». La conséquence est immédiate : la « mort » pour le programme qui tente d’y accéder. Lorsqu’il a des droits très importants, comme c’est le cas de Falcon Sensor, cela provoque le plantage complet du système, avec un écran bleu. À la question « pourquoi l’adresse mémoire 0x9c essaie-t-elle d’être lue ? Eh bien parce que… erreur du programmeur », affirme le développeur qui se présente comme un expert en C++.

Mais pour d’autres spécialistes, l’explication ne tient pas forcément la route. C’est le cas de Tavis Ormandy, qui travaille chez Google (Project Zero). Dans un thread, il indique que, « en fait, il y a une vérification NULL […] donc sa théorie est fausse ». « Le code lit les pointeurs d'une table dans une boucle, et certains ne sont pas valides. Peut-être qu'une erreur lors de l'analyse d'un fichier de configuration a laissé certaines entrées non initialisées, et l'une d'elles s'est avérée être 0x9c ? Ce n'est qu'une théorie, mais au moins la mienne correspond aux faits ».

Il donne un contre-exemple avec le crash analysé par Patrick Wardle (un autre expert du domaine) qui n’a pas la même adresse mémoire :

Quoi qu’il en soit, cela relance une nouvelle fois la question de la phase de test avant le déploiement et de l‘audit – au moins interne – du programme. Mais à ces questions, seul CrowdStrike est en mesure de répondre actuellement.

Commentaires (75)

Vous devez être abonné pour pouvoir commenter.

Abonnez-vous
votre avatar
Ça relance la question de la phase de test...... Oh, il y en avait une ? Arrête :D

J'ai du mal à croire que des tests soient passés à côté de ce carnage, il est plus pertinent de penser qu'il n'y en a jamais eu.
votre avatar
La question qui tue maintenant, c'est : est-ce que ce désastre sera suffisant pour que cette entreprise à $74Mds se dise que ce serait peut-être bien de (mieux) tester ses màj? Vu la com assez laconique, on peut se poser la question.
votre avatar
Technique classique :
Elle s'est peut-être déjà dit que "ça serait bien d'en avoir" : ça permet de calmer les inquiétudes en donnant l'impression d'une volonté à ceux qui s'en inquiète.
Et puis ensuite, certainement rien. Comme souvent.
Et puis lorsque des compte sont demandé, il est souligné que le conditionnel était employé sur un cosntat, et qu'aucun engagement n'avait été pris (et personne ne reproche ça, généralement).

Rapprocher une valorisation boursière ou des profit d'un manque d'action suppose que les moyens financiers disponibles signifient que les choses importantes seront adressées. Pour votre bien et celui des gens sur qui vous avez un impact : réveillez-vous.
C'est la même irrationalité qui frappent ceux qui pensent que le mérite signifie nécessairement une bonne rémunération ou le corolaire, pire, que le niveau de rémunération est une indication de la compétence.
votre avatar
La vrai question qui tue est de savoir si les gestionnaires de systèmes informatiques vont un jour prendre au sérieux les risques liés à l'injection de code tiers dans le composant le plus critique des systèmes qu'ils gèrent. Outre le risque évident de crash massif, on a aussi l'augmentation de la surface d'attaque permettant potentiellement de compromettre massivement les systèmes, que ce soit en attaquant le système d'analyse ou les protocoles de gestion.
votre avatar
Je pense qu'il y a aussi une question de design de l'applicatif. Un fichier de données invalides ne devrait pas pouvoir causer ce type de stop.
Je vais attendre la RCA mais ca me parait boiteux..
votre avatar
pourquoi 0x9c ? très certainement car pointeur NULL d'un structure, et comme faire ptr->champ est égal à valeur de ptr + décalage du champ, on arrive à 0 + 0x9c = 0x9c.
votre avatar
https://xeiaso.net/shitposts/no-way-to-prevent-this/OVE-20240719-0001/
votre avatar
Ils ont bien raison : il n'y a eu aucun travail sur le sujet, et il n'existe pas depuis C++ de langage de programmation garantissant la sécurité de la mémoire.
Il n'affirmeraient pas quelque chose de faux avec un tel aplomb si ce n'était pas le reflet honnête de la réalité.

Faisons comme eux, et comme ils le font si bien entre eux : blâmons le programmeur, afin de se sentir supérieurs.
votre avatar
Quel langage à part C/C++ permet d'écrire un driver à la fois pour Windows, MacOS et Linux ?

Je suis tout ouïe.
votre avatar
Même si je n'aime pas ce langage, je pense que Rust est une réponse possible. Et peut-être prochainement ZigBee.
votre avatar
Il y a eu des tentatives au niveau du noyau Linux pour utiliser Rust. Mais se pose des problèmes avec le modèle de gestion de la mémoire qui est radicalement différent et incompatible en l'état. Je ne sais pas si cela sera possible de résoudre ce problème ou pas.

Zigbee, je ne connais pas ce langage. Pour moi, c'est un protocole de communication. Et je ne trouve pas de référence en tant que langage. Tu es sûr ?
votre avatar
Rust est dans le noyau et il peut-être utilisé. Certains drivers pour des FS ou bien pour le scheduler l'utilse. Mais très généralement ce n'est qu'un wrapper sur les fonctions importantes du kernel.

Pour moi, Rust n'a pas le choix de devoir résoudre ce problème. S'il ne peut pas s'intégrer au noyau Linux (autrement qu'en étant un wrapper), alors perdra un certains coté promotionnel. Mais oui ce n'est pas gagné pour eux.

Exact ZigBee n'est pas le langage. C'est Zig que je voulais mentionner. My bad, il fait trop chaud dans ce pays, con !
votre avatar
Rust est dans le noyau et il peut-être utilisé. Certains drivers pour des FS ou bien pour le scheduler l'utilse. Mais très généralement ce n'est qu'un wrapper sur les fonctions importantes du kernel.
Tout à fait, ce qui en limite grandement les avantages.
Pour moi, Rust n'a pas le choix de devoir résoudre ce problème. S'il ne peut pas s'intégrer au noyau Linux (autrement qu'en étant un wrapper), alors perdra un certains coté promotionnel. Mais oui ce n'est pas gagné pour eux.
C'est même pas le côté promotionnel. C'est juste que Rust n'aura alors AUCUN intérêt a être utilisé au sein du noyau. Pire, on se retrouvera avec des portions écrites en Rust, d'autre en C. 2 langages au lieu d'un, sans avantage. Chouette :/
Exact ZigBee n'est pas le langage. C'est Zig que je voulais mentionner. My bad, il fait trop chaud dans ce pays, con !
Ah oui, Zig. J'étais tombé dessus il y a quelques mois. Mais je ne me suis pas penché dessus de manière approfondi (faut dire que si on devait se pencher sur tous les langages et toutes les technos qui sortent, on ne serait pas sorti de l'auberge :mdr:). J'avais regardé rapidement, j'ai vu un Rust like, la notoriété en moins (sans vouloir dénigrer le langage)
votre avatar
"C'est même pas le côté promotionnel. C'est juste que Rust n'aura alors AUCUN intérêt a être utilisé au sein du noyau. Pire, on se retrouvera avec des portions écrites en Rust, d'autre en C. 2 langages au lieu d'un, sans avantage. Chouette :/"

Je ne pense pas. Rust n'ayant pas "contaminé" beaucoup le noyau, je suis prête à parier que Linus et ses équipes feront le ménage. Au final ça ne restera qu'une expérimentation dans la mémoire du depot git pour les cyber-archéologues.

"J'avais regardé rapidement, j'ai vu un Rust like, la notoriété en moins (sans vouloir dénigrer le langage)"

De ce que j'ai vu Zig est plus un sérieux concurrent à C. Là où Rust a échoué, Zig pourrait être technologiquement un véritable concurrent du C, voir son futur. Affaire à suivre.
votre avatar
Pourquoi Rust aurait échoué ?
On parle programmation système et pas script ou dev web où on en est limite à concurrencer l'industrie du textile tellement les langues / bibliothèque/ canevas / whatever... arrivent aussi vite qu'ils repartent.

Le fait que Rust ai pu rentrer dans le nayau linux, même timidement est déjà énorme. Si cela permet déjà de limiter fortement la problématique de la gestion de la mémoire, ce cera très bien.
Et Rust reste très proche de la machine donc on évite les délires du c++ ou d'autres languages où le dev n'a aucune idée des conséquences de ses choix structurels.

Perso, je regarde parfois le résultat en assembleur entre deux manières de faire pour voir la plus efficace sur la cible. J'ai déjà eu des grosses surprises.
votre avatar
Si Rust n'arrive à intégrer le noyau, alors il aurait perdu l'une de ses plus grandes cibles "promotionnels". Rust a débarqué en voulant prendre la place du C alors qu'il n'est pas un concurrent du C, fondamentalement. Rust est plus un concurrent d'Ada.
Oui qu'il est débarqué dans le noyau Linux même timidement, c'est bien. Mais faire un wrapper en Rust de fonctions critiques n'apporte rien de vraiment intéressant. Et pour le moment, on ne l'a pas vu véritablement attaqué des choses fondamentalement sensibles dans le kernel avec tout la problématique de gestions de mémoires qui va avec ou de gestions d'interruption ou autres.

Si je devais parier, je dirais que Rust ne rentrera jamais dans le coeur du noyau. Il est trop restrictif by- design pour faire des choses efficaces en terme de perf pour un kernel monolithique.
Par contre, pour les drivers il pourrait avoir un atout à jouer. Mais vu le niveau de qualité des drivers sous Linux (en comparaison de Windows), si Rust devait avoir une utilité réelle ça serait d'être imposé par M$ aux constructeurs et fournisseurs de matériel pour les machines livrées avec Windaube.
Parce que le BSOD à cause d'un driver pissé à l'arrache, j'en ai jamais vu sous Linux (sauf quand j'en suis la responsable... Mais ça c'est une autre histoire).


Je ne sais pas ce que tu nommes délires en c++, mais si un dev ne comprends pas ce qui se passe en C++ dans la machine, il ferait mieux d'aller coder en Python. Idem pour celui qui code en Rust ou en C. Quand on est proche de la machine on se doit savoir comment ça marche, à minima.
votre avatar
Je pense que c'est encore prématuré pour Rust pour que ça soit réellement opérationnel pour un produit de ce type.
Ça démarre à peine pour Linux pour de vrais drivers et encore plus récemment pour Windows. Je n'ai rien trouvé concernant MacOS (mais je n'ai peut-être pas assez cherché).
Donc, oui, ça va venir, mais c'est encore trop récent en particulier pour porter des modules/pilotes/drivers existants comme celui-ci.
votre avatar
Celui qui veut le fichier en question ...
votre avatar
Preums !!!
votre avatar
Cela relance une nouvelle fois la question de la phase de test avant le déploiement et de l‘audit – au moins interne – du programme.
https://live.staticflickr.com/4500/37150757250_00a4a2a533_c.jpg
votre avatar
Ça me rappelle un de mes profs d’info qui nous répétait souvent: « Les pointeurs, oui… mais non…trop casse-gueule »
.
votre avatar
Sinon, je me demande bien ce que vont faire les entreprises dans les semaines à venir une fois tout ce bordel terminé, dont la mienne. Changer d'EDR ?

Et niveau juridique, sur les pertes liées à cet incident ?

Il nous reste encore un peu de taff, mais on a fait le plus gros. On n'est pas passé loin du black-out complet...
Et apparemment ce n'est pas la première fois que Falcon pose problème, même sur linux. Pas de cette ampleur, mais quand même
votre avatar
De notre côté je sais pas trop, mais je parierais sur un changement d'EDR...
On était déjà presque full MDE sauf serveurs Windows avec Crowdstrike qui gère le MFA, donc à mon avis ça va être bascule full MDE vu le bordel que ça a généré -_-
votre avatar
Je dirais qu'en l'état sur les systèmes en HA critique il faudrait mixer les EDR sur les nœuds. Là le problème c'est que même si tu avais un cluster de X nœuds pour la redondance ça a planté l'ensemble des nœuds. Mixer les EDR ça peut être une bonne idée mais ça va être chiant pour maintenir une cohérence dans le cluster. Bref pas de solution miracle

Chez nous on était pas sur CrowdStrike donc on a rien vu. On a subi la baisse de capa de Entra ID qui a ralenti un chouya la plate-forme mais sinon RAS pour nous et on est à 95% sur du Windows Server.

Ce qui me choque le plus là dedans c'est les médias mainstream qui continuent dans les titres de faire croire que le problème vient de Microsoft. Il sont victime au même titre que l'ensemble des autres boîtes qui sont équipées de CrowdStrike.
votre avatar
Mixer les EDR ça peut être une bonne idée mais ça va être chiant pour maintenir une cohérence dans le cluster.
Et en matière de coût de license ? Je ne connais pas le modèle de ces produits, mais c'est peut-être pas forcément rentable.

L'autre risque étant d'avoir des erreurs de déploiement et de se retrouver avec deux EDR sur la même machine. Pour l'avoir déjà vécu, bah c'est la merde.
votre avatar
C'est à l'hôte donc je dirais faire 50/50. En terme de coût ça ne devrait pas forcément changer grand chose. Par contre effectivement ça veut dire maintenir 2 branches de déploiement et non plus qu'une seule
votre avatar
CrowdStrike broke Debian and Rocky Linux months ago, but no one noticed:

https://www.neowin.net/news/crowdstrike-broke-debian-and-rocky-linux-months-ago-but-no-one-noticed/
votre avatar
C'est le revers de la médaille quand on est le backend. Indispensable, mais invisible.

Ici, les Windows impactés ont été en front, donc l'incident très rapidement visible.
votre avatar
Beaucoup moins d'impact c'est sûr, mais pour l'avoir subi je confirme
https://www.reddit.com/r/debian/s/M9ksg6VWFB
votre avatar
Perso je retiens surtout un truc qui m'inquiète : des machines de production installent des mises à jour non qualifiées en mode automatique ? Un parc entier ?

Que l'éditeur se chie dessus et sorte une version foireuse, ça arrive.

Mais voir qu'un patch foireux se répand dans un SI aussi rapidement, cela m'inquiète quand à leur maîtrise de la part de toutes ces entreprises.

Je peux comprendre qu'un composant de sécurité nécessite d'être à jour le plus vite possible, car les enjeux sont presque en temps réel. Mais là, c'est inquiétant.

À mes yeux, ça démontre un mauvais choix stratégique : tout centraliser, tout confier à des services managés, tout externaliser, perdre la maîtrise, c'est une vision qui est appréciable sur une ligne comptable à court terme (moins d'ETP, le SI qui devient de l'OPEX, etc). Mais au moindre pépin de ce genre (et un Cloud Provider ou service SaaS, ça se chie dessus plus souvent qu'on ne l'évoque dans la presse, les devs dépendant de GitHub sauront de quoi je parle), c'est une catastrophe et il ne reste que ses yeux pour chialer.

De mon expérience avec le SaaS : c'est bien pour des petites entreprises qui ne peuvent se permettre d'avoir des ETP pour gérer leur IT, ou pour externaliser des services non critiques dont le RTO/RPO et le SLA ne mettent pas en danger l'entreprise en cas d'indisponibilité. Pour le reste, c'est une hérésie de perdre autant sa maîtrise.
votre avatar
Je ne suis pas admin de CrowdStrike dans mon entreprise, mais de ce que je sais, l'admin à la possibilité de gérer les versions de l'agent. Je crois qu'on garde une version d'écart avec la dernière version.

D'ailleurs on a déjà eu des problèmes sur des outils métiers, obligé de demander à l'admin de downgrade, ou de mettre la dernière version (et update de notre outil de notre côté).

Là, si j'ai bien compris, c'est l'update de contenu de Falcon, et je suis pas sûr que l'admin ait le choix (à vérifier, vais me renseigner demain)

Mais je suis bien d'accord avec toi !

Et d'ailleurs, on a déjà freiné des quatre fers auprès de la team sécu au sujet de l'installation de l'EDR sur des systèmes très critiques (l'automation de diffusion par exemple), mais c'est compliqué...

Peut-être qu'on va un peu plus nous écouter now :fumer:
votre avatar
Ce n'est pas un patch du client ou quoi que ce soit. C'est une MAJ du catalogue de détection qui, d'après ce que j'ai compris, était là pour ajouter de nouvelles détections mais qui l'a fait dans un espace mémoire ou il ne devrait pas y être. Du coup direct le Kernel Windows s'est mis en sécurité en balançant l'écran bleu.

C'est pour ça que cette MAJ est passé en masse car pour le catalogue c'est direct et sans contrôle
votre avatar
Ça reste quand même un problème pour un SI de mon point de vue : un changement de configuration non maîtrisé qui tank en masse une infra.

Pour moi, c'est comme tourner avec une nightly en prod ce genre de comportement.
votre avatar
Il faut je voir comme sur un bon vieux AV, quand tu as des nouvelles définitions de virus, tu n'attends pas 1 mois pour les appliquer, sinon le produit perd de son intérêt.
votre avatar
T'es pas obligé d'attendre un mois. Un cycle de test peut se faire en une journée.

Surtout si le truc fait un BSOD sur la machine :p
votre avatar
Peut être, mais sur un update journalier, c'est une personne a temps plein qu'il te faut pour tester..
votre avatar
Les tests ça s'automatise ;)
votre avatar
Le propre de nombreuses solutions de sécurité moderne, c'est l'attitude cowboy qui consiste à vriller du code de qualité inconnue dans les couches les plus profondes des systèmes. 26 ans que je suis dans le métier et ça continue, tous les 2 ans j'ai un client qui vient avec une de ces cochonneries et qui veut mordicus que ce soit intégré partout le plus vite possible et sans regarder sous le capot. Ce qui arrive avec Crowdstrike est hautement prévisible, c'est juste une nième solution miracle qui vous apporte sécurité, bonheur, prospérité et amour si vous l'installez sans poser de question.

Après avoir préparé le POC et lu la doc, c'était clair pour moi que ce produit était plus un risque inconsidéré qu'une solution.
votre avatar
les pseudos journalistes s'en sont donné à cœur joie, après on a eu les pseudos techniciens qui nous chantaient la même messe c'est " Microsoft " franchement niveau tech c'est à revoir côté journalistes tech on n'est vraiment pas au top. Même si la boite est pas touché car on tourne tout sur un autre os j'imagine le manque à gagner pour ceux qui ont été impactés.
votre avatar
La question des tests vient peut être du fait que ça vient de bugs intempestifs, surtout avec des pointeurs mémoire en C/C++. En effet, on test le programme dans un écosystème avec une organisation mémoire du système qui reste la même, et même si certaines adresse mémoire sont incorrectes, on ne s'en rend pas compte car elles accèdent à une zone mémoire du programme valide (donc il ne plante pas). Puis lorsqu'on met en production le programme, la configuration mémoire chez les clients n'est plus du tout la même, et les pointeurs incorrectes pointent vers une zone mémoire non valide, puis le programme plante. Ce scénario, s'il est avéré, est du au langage C/C++ qui permet de faire tout et n'importe quoi, sans que des erreur ne soient toujours générées au moment des tests. C'est une des raisons qui m'ont poussé à abandonner le C++ depuis bien longtemps.
votre avatar
Un test "!= null" ou un try/catch, c'est pas hors de portée non plus.

Ce bug, bien que minimisé dans les médias, aura de lourdes répercussions. Quand un bug touche uniquement les techs, ça va, c'est une mauvaise réputation chez les techs. Quand ça atteint les utilisateurs, on en est pour 5 à 10 ans de perte de confiance.

Quand un employé perdu utilise le joker "on a un problème informatique" au téléphone avec un client, l'employé DOIT avoir mauvaise conscience. Là, il pouvait fièrement le dire. On n'est pas près qu'il se repose des question sur son usage: ce sera l'ordi qui bug/se trompe/lag/freeze.

Le plus gros mal en informatique, c'est le peu de confiance qu'ont les utilisateurs dans l'outil, souvent par manque de formation.
C'est un énorme travail de la part des chefs de projet pour animer cela et rendre la confiance dans l'outil. On doit souvent se porter "garant" du résultat.
Le moindre faux pas, le moindre résultat inexpliqué et c'est comme un lent poison pour le projet.

Résultat: si l'outil n'est pas fiable, pas la peine de passer du temps dessus - et on se retrouve avec un outil dont les données sont pourries, inutilisable, rejeté. Du coup, on se voit obligé de changer d'outil parce que le directeur des employés l'exige - pour un outil moins bon parfois mais qui était dans les pages de 01informatique.

Bref, l'étendue de cet incident est bien plus large que 8,5M ordinateurs: que ce soit de bonne ou mauvaise foi, les reprogrammations de visites à l'hôpital, les retards logistiques, les décalages dans les transports ne se résolvent pas en prenant le prochain horaire: c'est plusieurs jours de replanification!

Je rappelle que Crowdstrike est censé garantir disponibilité et uptime. Là, leur bug est l'équivalent EXACT d'une attaque de virus réussi pour une équipe informatique. Sans parler qu'avoir un EDR, c'est une charge de travail de maintient supplémentaire pour débloquer les postes injustement bloqués, et une gêne régulièrement dans l'usage des ordis + une charge en ressources informatiques...

La com de Crowdstrike me paraît du coup totalement décalée. Le PDG devrait démissionner, la boîte offrir l'année de presta et baisser le coût de moitié: l'attaque qu'elle vient de faire surpasse largement un gros virus tellement c'était étendu et généralisé. Ils viennent de prouver qu'ils sont aussi dangereux que les attaques contre lesquelles ils sont censés nous prémunir.
votre avatar
Un peu de passif pour le CEO apparemment...
https://www.businesstoday.in/technology/news/story/microsoft-outage-not-the-first-time-crowdstrike-ceo-george-kurtz-is-facing-a-global-disruption-438035-2024-07-22
votre avatar
Mais dans l'article, on parle bien de l'adresse 0x9c et non de NULL (qui est représenté par 0 si ma mémoire est bonne). Ici 0x9c pointe vers la page 0 si j'ai bien compris et qui n'est pas à l'adresse 0 dans la mémoire. Du coup votre test if (v!=null) ne devrait pas passer.
votre avatar
Il a pourtant raison. Bien que l'adresse 0x9C ne soit pas une adresse nulle, cela ressemble fortement au déréférencement d'un pointeur NULL.

Exemple :

if (test != null) {
test->machin++;
}


test est une structure plus ou moins complexe, dont l'adresse de base est représentée par la valeur contenue dans test, et dont chaque champs ce déduit ensuite à partir de cette valeur de base.

Maintenant, ce n'est pas parce que cela ressemble a un pointeur NULL que c'est effectivement le cas. Ce n'est pas la seule chose qui puisse provoquer ce genre de comportement.
votre avatar
Je viens de vérifier avec ce code :

int main()
{
printf("Hello World");
int v=(int)0x9c;
if (v==NULL)
printf("0x9c est un pointeur NULL");
else
printf("0x9c n'est pas un pointeur NULL");


return 0;
}

Ca m'affiche "0x9c n'est pas un pointeur NULL". Vous pouvez tester le code ici : https://onlinegdb.com/oi9IbrDM5
Donc je dirais que vous avez tous les deux tord.
votre avatar
Relis l'exemple de code que je t'ai donné. Je te parle de déréférencement de pointeur. Le problème n'est pas d'accéder au pointeur, mais à un des attributs de la structure pointée par le pointeur (sans compter que ton code ne manipule pas de pointeurs, juste des entiers).

https://onlinegdb.com/03Sut0yrO
Donc je dirais que vous avez tous les deux tord.
Je vais utiliser un argument d'autorité, même si je n'aime pas ça : ça fait plus de 30 ans que je développe et au moins 25 que je fais du C plus ou moins régulièrement, dont de la programmation système et sur système embarqué.

Donc oui, je suis sûr de mon coup et tu te trompes. Ce que je t'énonce là, cela fait partie de la base du C.
votre avatar
Mais dans l'exemple que vous donnez, il n'y a pas "if (v!=null)", pourtant c'est de ça dont parlait "Wogien" plus haut. Si vous parlez d'autre chose, je ne peux pas vous répondre puisqu'on parlait pas de ça au départ.
votre avatar
if (test != null) {
test->machin++;
}

Alors c'est pas "v", mais "test", mais je parlais bien de ça depuis le début... Cf. mon commentaire #12.5
votre avatar
Vous dites ici "if (test!=null), mais dans le commentaire d'avant, vous faisiez référence au lien qui donnait ce code
: typedef struct {
int field1;
int field2;
} test_s;

int main()
{
test_s* test = NULL;

printf("Adresse de l'attribut field2 : %lx", (unsigned long int)&(test->field2));

return 0;
}
Or il n'y a pas de "if(test!=null)" dans ce code. Donc on ne parle de pas de la même chose.
Encore une fois, si vous testez :
int main()
{
printf("Hello World");
int v=(int)0x9c;
if (v==NULL)
printf("0x9c est un pointeur NULL");
else
printf("0x9c n'est pas un pointeur NULL");


return 0;
}

Ca m'affiche "0x9c n'est pas un pointeur NULL". Vous pouvez tester le code ici : https://onlinegdb.com/oi9IbrDM5
C'est de ça dont au parlait au début et uniquement de ça. NULL correspond à l'adresse 0, et if "(test!=null)" sera validé si test n'est pas égal à 0, et non pas si test n'est pas égal à 0x9a ce dont on parle dans l'article.
votre avatar
Oui, et ce code était là pour montrer qu'avec un pointeur NULL, on pouvait facilement accéder à un espace mémoire non nul, en montrant l'adresse en mémoire d'un des attributs de la structure.

Un code qui génère un accès non autorisé à une adresse très très basse (du style de 0x9C) a de très forte chance d'être provoqué par le déréférencement d'un pointeur NULL, dont un simple test if (v != NULL) protège. Ce n'est pas l'unique possibilité d'avoir ce genre d'erreurs, mais dans la grande majorité des cas, c'est ça.

Donc l'article parle bien de l'adresse 0x9C. Un accès à ce type d'adresse est très majoritairement dû à l'usage non testé d'un pointeur NULL (le fameux déréférencement). Donc, le test proposé par Wosgien (qui protège contre le déréférencement d'un pointeur NULL) sera efficace dans la grande majorité des cas.

Maintenant, et sans vouloir être désagréable, notre échange m'indique clairement que vous n'êtes pas développeur (ou si c'est le cas, j'en suis désolé, mais vous avez de grave lacune sur la notion de pointeur). Dans les deux cas, il me parait inutile de continuer ce dialogue de sourd.
votre avatar
Heureusement que vous n'êtes pas désagréable !
votre avatar
Concernant l'argument d'autorité, je ne vois pas le rapport ! J'ai donné un exemple qui explicitait ce que je disais. Si vous êtes compétent, vous devriez comprendre mon propos. Si vous parlez d'autre chose, je ne peux rien pour vous, car nous allons avoir un dialogue de sourd, et cette conversation ne sert à rien. Partez de "if (v!=null)" qui est l'exemple donné plus haut par Wogien que j'ai contredis.
votre avatar
Votre exemple ne dit rien du tout. Votre exemple dit que l'entier 0x9C est différent de 0 (et je dis bien entier, pas pointeur). Ce qui est d'une évidence qu'il n'est même pas besoin de souligner.
Partez de "if (v!=null)" qui est l'exemple donné plus haut par Wogien que j'ai contredis.
Cf. mon commentaire #12.5
votre avatar
Si c'est évident, pourquoi me contredire ? Ai-je dis autre chose ?
votre avatar
Vous dites que mon exemple parle de l'entier 0x9C, car l'éditeur de Next.ink a modifié mon texte. Si vous cliquez sur mon lien, vous pourrez constater que mon code de départ parle bien du pointeur qui a l'adresse 0x9c. Mais ce pointeur est bien un entier au final. Cela revient au même.
votre avatar
Et en plus vous avez la malhonnêteté de modifier le code sur onlinedb pour utiliser des pointeurs...

Pas de bol, vous avez oublier de modifier votre commentaire #12.15 qui en reprenait le copier/coller...

Cela en dit long sur votre état d'esprit. Fin de la discussion.
votre avatar
Non, cela n'est pas vrai, je n'ai rien modifié sur le site en ligne. Le copier/collé était le bon dès le départ, mais int * n'est simplement passé. Vous jugez vraiment n'importe comment sans savoir.
votre avatar
D'ailleurs, le (int*) est toujours présent dans mon commentaire initial lorsque je cherche à l'éditer, mais il ne s'affiche pas une fois validé. Preuve que je l'avais bien copier/coller.
votre avatar
Laisse tomber, il ne comprend rien à ce que tu dis.

Que 156 ne soit pas un entier nul, on le sait tous depuis longtemps.
Comme tu le dis, il ne teste pas un pointeur mais un entier.

Et pour finir, il est désagréable. Il a failli être le premier mis en liste noire depuis que la fonction existe sur le nouveau site.
votre avatar
D'autre part, vous proposez des tests pour éviter le problème dans le code en production, mais vous ne pouvez pas faire un try/catch à chaque fois que vous lisez une variable. Et même si vous le faites, il faut vous attendre à deux catégories de problèmes ; les exceptions (ouvrir un fichier qui n'existe pas), ou les erreurs (arriver à un état du programme qui n'aurait pas du arriver). A moins de considérer qu'une mauvaise adresse mémoire pointé par une variable soit un comportement normal, il s'agit au tout état de cause d'une erreur et non d'une exception. Dans ce cas, quand un programme génère une erreur, il faut laisser le programme planter et ne surtout pas continuer l'exécution pour éviter qu'un comportement improbable (et pire) ne se produise. Donc même si vous faites un try/catch, si ça ne doit pas arriver, il faut quand même terminer le programme. Le try/cath ne servirait dans cet exemple qu'à journaliser la trace de l'erreur et/ou à fermer correctement les verrous obtenus sur le système.

Le sens de mon premier commentaire était de dire que les langages C et C++ ne génèrent pas les mêmes exceptions en fonction de la configuration mémoire dans laquelle on se trouve à cause du laxisme des deux langages dans la gestion de la mémoire, ce qui pose problème quand on test le programme, que le test est validé dans un environnement et que l'erreur se produit quand même quand l'environnement et la configuration mémoire du programme changent, i.e. lorsque le programme passe en production. Ce type de problèmes intempestifs ne se produit pas dans d'autres langages de programmation, où la gestion de la mémoire est plus cadrée. Maintenant, je n'ai pas dit que c'est ceci qui s'est passé dans le cas de cet article d'où le "peut-être" dans mon premier commentaire.
votre avatar
Le sens de mon premier commentaire était de dire que les langages C et C++ ne génèrent pas les mêmes exceptions en fonction de la configuration mémoire dans laquelle on se trouve à cause du laxisme des deux langages dans la gestion de la mémoire
Il sera bon de ne pas tenter de réécrire l'histoire. C et C++ ne sont pas laxistes. Ils permettent des choses que d'autres ne permettent pas. Un grand pouvoir implique de grandes responsabilités.

Certains langages proposent effectivement des mécanismes pour éviter les problèmes de gestion de mémoire, et utilisent plusieurs mécanismes, parfois complémentaire, à cette fin.

Maintenant, nous parlons ici d'un cas particulier, qui est de la programmation système, nécessitant un très bas niveau d'abstraction, ce qu'offre le C et dans une moindre mesure, le C++. Pour de la programmation système, on oublie tous les langages qui dépendent d'un runtime et/ou d'une machine virtuelle.

Ne pas oublier non plus que le C a été initialement inventé pour réécrire de manière "portable" Unix. La gestion de la mémoire se devait donc d'offrir un contrôle important au développeur. Ce n'est pas du laxisme, mais une volonté forte.

Avant son invention, le seul moyen utilisable pour écrire un OS était l'assembleur. Ce qui n'était absolument pas portable, puisqu'on devait alors réécrire entièrement les OS pour chaque architecture et chaque processeur.

A ma connaissance, il n'y a que très peu de langages qui permettent aujourd'hui de faire de la programmation système :
- le C
- le C++ (privé de certaines fonctionnalités comme la gestion des exceptions)
- l'Ada
- le Rust
- l'assembleur

Il en existe surement d'autres, mais dans les plus connus/répandus, je ne pense pas en oublier.

Pour terminer, le coup de la remonté des erreurs par le programme jusqu'à le faire crasher, c'est effectivement plutôt une bonne pratique (pour les exceptions, ça va dépendre du type d'exception et de la robustesse que l'on souhaite donner à son applicatif). Mais ça, c'est possible pour les programmes justement, pas lorsqu'on fait de la programmation système.

Si on faisait ça en programmation système, les BSOD ou les kernel panic seraient légions. Un OS ne doit pas planter. Et on le voit bien aujourd'hui, Windows, Linux, MacOS, FreeBSD, etc. sont très stable et fiable. Un programme qui plante ne fera pas planter le système.

Par contre, lorsque le problème est dans un driver / module noyau... les choses se corsent.
votre avatar
Je ne réécrit pas l'histoire ! Je dis juste mon appréciation de c/c++ !
votre avatar
Dans les langages permettant la programmation système j'ajouterai PL/1 et ses variantes (dont le PL/M d'Intel). Multics dont la complexité a poussé Ken Thompson et Dennis Ritchie à écrire UNIX (d'abord appelé UNICS) était écrit en PL/1 et assembleur.
votre avatar
Bon, je vous ai lu à tête reposé. La dernière fois, j'avais une journée très chargées et j'ai lu beaucoup de choses en diagonale. Désolé, mais si vous commencez votre message par "il ne faut pas réécrire l'histoire", ça fait procès d'intention, et de mon point de vue, vous m'avez déjà jugé, je n'ai aucune chance que je me preniez pour votre égal. Donc, j'ai déjà plus trop envie de vous lire.

Puis vous avez commencé un historique des langages, et j'ai arrêté à ce moment de vous lire. Partir du principe que je ne comprend rien à ce que je raconte m'indiquait que vous jugiez sur les apparences. Alors certes, j'aurais du attendre un moment où j'avais plus de disponibilité pour vous lire, mais étant donné que vous ne donniez aucun crédit à ma façon d'aborder les choses, je ne voyais pas pourquoi je devais en faire plus que vous.

Pourtant après avoir relu tout vos commentaires, j'ai compris tout ce que vous aviez dit, même si j'estime que ça n'avait pas trop rapport avec mon commentaire initial, et comme je n'avais pas le temps de digresser, je n'ai pas vraiment compris pourquoi toutes ces digressions, et ces tons péremptoires que vous aviez avec moi. Le mal entendu naissait et grandissait de plus en plus.

Donc oui, un pointeur P de valeur nulle, qui pointe vers une structure à la position relative 4, aura l'adresse absolue 4, donc non égale à zéro. Je vous rassure, ce n'est pas une découverte pour moi. Le problème, c'est que comme c'est évident, il est impossible pour ma part de comprendre le malentendu avec des commentaires qui répondent à côté de mon commentaire initial avec des présomptions fortes sur ce que je sais ou pense dès le départ, et avec moi vous lit en diagonale n'ayant pas eu la patience d'attendre 1 jour pour répondre.

Très bien. Au moment de mon commentaire initial, je ne suis pas dans l'esprit de faire la différence entre le contexte du noyau et celui du programme, ou entre les adresses valides ou non. Je ne pense pas non plus "translation d'adresse". Mon commentaire initial n'en avait pas besoin, puisque je commentais le problème inhérent que pose le C, car la question n'est pas de savoir si on doit tester un pointeur NULL, mais pourquoi on arrive dans un état qui ne s'est pas produit pendant les tests. Le pointeur NULL n'a peut être rien à voir, et s'est peut être la résultante d'un bug plus en amont.

Dire qu'un noyau doit être stable, mais comment dire, vous pensez vraiment que quelqu'un pense que le noyau peut être bâclé dès lors que l'on comprend ne serait-ce qu'à minima ses fonctions ? Quiconque a déjà utilisé Windows Millenium sais ce que cela implique un noyau instable, qu'il soit informaticien, ou utilisateur de Word. J'ai l'impression que tous les utilisateurs de Windows Millenium étaient devenus superstitieux, tellement il était imprévisible. Pas besoin d'utiliser un argument d'autorité pour le savoir.

Cela étant dit, lorsque je dis qu'un programme qui arrive dans un état incohérent doit tout arrêter, vous dites en retour que l'on ne peut se permettre d'en faire autant avec le noyau en tant que dev, avec en creux l'idée qu'il faille continuer coûte que coûte, ce qui n'est pas ce qu'a choisi de faire le noyau de Windows. C'est votre appréciation, mais si ne vous faites pas planter un noyau sans savoir si c'est bien raisonnable, comment être sûr que ce ne sera pas pire ? Ce qui s'applique pour un programme, s'applique aussi pour le noyau.

Si l'on se place du point de vu d'un programme, l'objectif principal est que ce dernier ne plante pas et remplisse ses rôles. Si à un moment un problème se pose et que cette éventualité avait été anticipée, on peut alors prévoir plusieurs stratégies pour fonctionner en mode dégradé. On peut déléguer la tâche à un système de secondaire, on peut considérer une autre option, on peut simplement désactiver une fonctionnalité, on peut considérer que le problème n'est pas si important et qu'il suffit juste de faire remonter l'information dans un rapport, on peut appliquer un autre algorithme contournant le problème en le résolvant d'une autre façon, on peut développer un système avec une tolérance à la panne, etc.

Mais si le problème n'avait pas été anticipé, et s'il arrive quand même alors qu'il ne devrait pas arriver, c'est à dire au moment d'une assertion qui vérifie un pointeur non NULL par exemple, alors il faut faire planter le programme.

Imaginez un outil de contrôle aérien qui arrive dans un état mémoire théoriquement impossible alors qu'il a été testé mille fois, de manière empirique et par un système de preuve. Vaut-il mieux tout faire planter, ou continuer ?
- Si vous continuez, vos tests unitaires ne détecterons rien. Peut être aurez vous un rapport de généré, mais si vous oubliez le lire à temps, votre programme aura quand même poursuivi son excécution.
- Vous pouvez aussi ne pas avoir la même stratégie pendant les tests ou en production, mais dans ce cas, vos tests ne sont pas tout à fait valides car il ne testent pas la même chose.
- Si vous faites tout planter, et que cela arrive pendant les tests, vous corrigez vos tests. Mais si vous poursuivez l'exécution du programme en production sans savoir si cela est bien raisonnable, et alors que vous êtes sûr que cela ne devrait pas arriver (assertion), vous prenez le risque pour le dire vite d'afficher à l'écran des avions à une mauvaise position.
- Si vous faites tout planter et que cela arrive en production, vous avertissez les contrôleurs aérien que le système n'est plus fiable, et qu'il faut qu'ils se débrouillent autrement. Faut-il tout faire pour en arriver là ? Non, car un oui serait trop évident -> que voulez-vous que je vous dise d'autre ? Tout ceci est banal, et je suis sûr que je ne vous apprend rien.

Maintenant, la question soulevée au départ est, pourquoi un programme qui ne plante pas pendant les tests peut finir par planter en production avec le C ? Etant donné que le C permet de faire n'importe quoi avec la mémoire (oui oui), il suffit que la configuration mémoire soit différente au moment de l'exécution pour que le comportement du programme soit différent.

Car justement, le langage C ne vérifie pas si un accès mémoire est autorisé ou non. Tant que la zone pointée ne déborde pas de l'espace d'adressage du processus, le noyau ne dit rien (enfin le processeur, ça serait trop lent si le noyau testait chaque accès mémoire sans accélération processeur). Si vous faites un dépassement mémoire, ou si vous lisez un pointeur non égal à 0, parfois le programme plante, parfois c'est le noyau qui le fait planter, parfois il ne plante pas.

Rien que de changer les options de compilation fera donner un comportement différent au programme avec le même bug, voir cela fera virtuellement disparaître le bug, le programme n'ayant pas planté.

On peut même avoir un pointeur non initialisé qui vaut une valeur aléatoire au moment des tests, car une variable non initialisée prend par défaut la valeur de son état mémoire au moment de son allocation, probablement non NULL. Donc si cette valeur par défaut correspond à une valeur acceptable de l'espace d'adressage du programme, le processus ne plante pas. Si vous relancez plusieurs fois le test sur la même machine, en relançant plusieurs fois le même programme, la configuration mémoire de l'ordinateur de test n'ayant pas changé, le bug n’apparaît jamais, le pointeur n'est jamais à NULL.

Pendant la prod, disons pour des raisons d'options de compilation différentes, le pointeur non initialisé aura la valeur par défaut à zéro, et le programme plante à se moment, là où il n'avait pas planté pendant les tests. Voilà un scénario possible. Mais il peut y en avoir d'autres.

Est-ce que c'est cela qui s'est passé ici ? Je ne sais pas, je ne faisais qu'émettre une hypothèse.

Ce qui nous amène au fond du sujet, puisque vous avez quand même répondu à mon commentaire initial à un moment, c'est à dire dans le commentaire auquel je répond présentement. Vous dites que C est un langage à remettre dans son contexte. Très bien, mais là encore, vous enfoncez une porte ouverte. Le problème n'est pas celui là, ni l'histoire du C, mais plutôt que faisons nous en 2024 avec un tel langage ?

Vous dites en creux qu'un OS ne peut se passer d'un tel langage qui permet tant de liberté. Peut être avez-vous raison, personnellement, n'étant pas dev sys, je me contente d'observer que personne n'a envie de redévelopper plusieurs millions de ligne de code d'un noyau dans un autre langage, d'autant que beaucoup de dev sys développent en C. Est-ce que les deux raisons qui font que le C soit toujours aussi populaire ne sont pas les suivantes :
1. il n'y a pas assez de monde dans les dev sys qui maîtrisent un autre langage que le C
2. il est très démotivant de redévelopper tout un noyau dans un autre langage dont on ne sais même pas s'il est plus mature que le C .

Mais comme ici, il s'agissait que d'un (parait-il faux) pilote, peut être qu'en fait si, on peut progressivement développer dans un autre langage, qui plante dès qu'un accès mémoire n'est pas licite à l'intérieur même du processus, et non plusieurs centaines de lignes plus loin, et non "jamais jusqu'au jour où on le met en prod". Peut être qu'à la périphérie du noyau et dans les couches du dessus, il n'est plus nécessaire de développer dans un langage devenu pour moi bien trop vieux. C'est bien là mon droit d'émettre une opinion. Personne ne vous oblige d'être d'accord. Mais ne croyez pas qu'une seule opinion doit survivre. Ce n'est pas Highlander !

Je pense franchement qu'on aurait pu éviter ces digressions et cette perte de temps, car je soupçonne que dès le départ, que vous comprenez ce type de position.
votre avatar
Je n'avais pas l'intention de déclencher un cours de prog. La gestion des erreurs, c'est un indispensable, il faut bien établir où sont les sections critiques et comment en sortir proprement.
Bien spur, dans le cas d'un EDR, s'il suffit de pourrir son fichier de config pour le désamorcer, c'est balo. La bonne pratique que je conseille dans ce cas: livrer la config par défaut, si erreur de lecture du fichier de config on tombe dans la config par défaut.

On en revient à la base de la programmation, y compris en automatisme: sortir de l'erreur sans tout péter. cf le cas du Therac-25. C'est en gros la même chose à vue de nez.


Parfois les grands éditeurs me font plaisir en programmant moins bien que moi et mon équipe :)
votre avatar
Aller, je craque:
"D'autre part, vous proposez des tests pour éviter le problème dans le code en production, mais vous ne pouvez pas faire un try/catch à chaque fois que vous lisez une variable."

Non, question de contexte, mais en général on englobe toute un section de code.

" Et même si vous le faites, il faut vous attendre à deux catégories de problèmes ; les exceptions (ouvrir un fichier qui n'existe pas), ou les erreurs (arriver à un état du programme qui n'aurait pas du arriver). A moins de considérer qu'une mauvaise adresse mémoire pointé par une variable soit un comportement normal, il s'agit au tout état de cause d'une erreur et non d'une exception."

C'est une bonne remarque. Si les environnements managés (java, C#) considèrent à peu près pareil et gèrent de la même façon les erreurs de pointeur et les erreurs "managées", ce n'est pas pareil en C et en C++ (qui eux-mêmes sont différents).
Quand je parlais de try/catch, c'était sur l'idée générale.
Et donc c'est une mauvaise remarque: en C, on peut aussi dire comment le soft doit gérer les exceptions SYSTEMES (div/0, accès mémoire illégal, instruction illégale). Ce qui fait l'équivalent d'un try/catch (je ne me rappelle plus bien, je n'ai pas fait de C de ce genre depuis 2000).
On ne peut pas catcher toutes les erreurs mémoires, mais quelque soit le système, taper dans la RAM système (comme 0x9c) est catchable. Ce qu'on ne peut pas détecter ainsi, c'est quand le programme écrit trop loin dans sa propre mémoire.

Bref il y a des mécanismes basiques, en place depuis le début de la "mémoire protégée" qui existent en quasi standard.

Donc de la part d'un programmeur d'EDR, c'est pour moi impardonnable.
votre avatar
Mais la question n'est pas de faire un catch, mais quoi faire du catch ? Si vous décider de poursuivre l'exécution alors que l'état du programme est incohérent, vous n'aurez rien résolu du problème initial et vous déplacez le problème ailleurs. S'il y a un écran bleu sous Windows, ce n'est pas parce que le noyau fonctionne mal (ou pas forcément), mais parce que le noyau est arrivé dans un état incohérent où il ne peut poursuivre son exécution sans potentiellement aggraver le problème. Un catch pour empêcher l'écran bleu ne changerait rien au fait que cela n'aurait pas du arriver dans le sens où le bug n'a pas été anticipé (sinon, il n'y aura pas de bug en fait).

Ce qui peut arriver à l'échelle du noyau peut aussi arriver à l'échelle d'un programme. Faire un catch de quelque chose d'incohérent dans le contexte, ne changera rien au fait que cela n'aurait pas du arriver. Et donc, dans ces conditions, il vaut mieux que tout plante plutôt que continuer l'exécution avec un cas non prévu. C'est tout ce que j'ai essayé de dire, et c'est pas la peine de me prendre de haut en entrant dans les détails technique du C pour noyer le poisson dans l'eau. Ce que je dis est une banalité. Votre façon de me rabaisser est intolérable.
votre avatar
Au hasard, passer au fichier .sys suivant et ne pas charger la protection contre les named pipes foireux ?

Il est aussi possible d'envoyer un rapport d'erreur pour remonter le problème et envisager un correctif.
votre avatar
Si le bug est prévisible, ce n'est pas vraiment un bug, vu que vous qu'en l'anticipant, une correction s'impose d'elle même. A partir du moment où vous n'avez pas prévu le bug, il se produira, ce qui s'est produit ici. Maintenant, oui, si le correctif consiste à dire qu'on peut passer au fichier sys suivant, c'est pas grave, OK. Mais les bugs se produiront toujours parceque vous n'avez pas anticiper un problème, jamais autrement, à moins de travailler dans l'objectif de saboter le projet (ce qui peut être une piste cela dit). On peut toujours dire après coup, on aurait pu faire ceci ou cela, mais bon, résoudre le problème à postériori ne résou pas le problème à priori. Impossible de remonter dans le temps.

Donc soit vous décidez à priori que certains problèmes peuvent se poser et que vous pouvez ignorer le chargement fichier sys à priori en toute conscience, soit vous décidez à priori qu'aucun problème ne devrait se poser, car au moment où vous écrirez le code, vous considérerez que le problème que vous envisagez est impossible (à tord ou à raison), et que s'il se produit quand même, c'est que au moment où vous y pensez, vous ne savez pas pourquoi cela se produit. Soit vous tolérez l'erreur sans trop savoir ce que ça va donner (et éventuellement ce que cela va empirer), soit vous faite tout planter, en disant qu'il vaut mieux préserver l'intégrité du système, et notamment l'intégrité des données qu'il ne faut pas corrompre. Si le noyau arrive par exemple à un état non anticipé, est-ce qu'il vaut mieux générer un écran bleu ou continuer au risque de bousier le système de fichier ? Par exemple, et petite digression, j'ai entendu du dire que le noyau Linux était tellement stable qui ne générais pas de kernel panic même avec une mémoire instable. Personnellement, cela m'est déjà arrivé d'avoir une mémoire instable, le noyau n'a pas planté, et pourtant mon ordinateur faisait n'importe quoi (j'ai mis du temps à comprendre que c'était la mémoire le problème, enfin 2h). J'estime avoir eu de la chance que ça n'ait pas eu plus de conséquences sur mon système de fichiers. J'aurais préféré un kernel panic. Maintenant, dans le cas de cet article, je ne sais pas trier ce que peut être toléré comme erreur de ce qui ne peut pas l'être.

Ce que je dis en revanche, c'est que les bugs arrivent quelque soit les précautions prises. Donc par définition, si on n'anticipe pas un bug et son sens, c'est qu'on ne sais pas s'il faut faire tout planter, ou si on peut tolérer l'erreur. Pour tester un programme, on doit faire des tests, et mon propos initial supposait que des tests sur Crowdstrike avait bien été effectués avant la mise en production. Je me suis alors demandé comment des tests peuvent passer sur une machine de test et pas sur une machine en production ? Du fait qu'un programme C ne fait pas toujours planter un programme à cause d'un mauvais accès mémoire, seul le noyau fait planter le programme s'il se rend compte que l'accès porte sur un espace mémoire non autorisé. Mais si l'accès mémoire est autorisé au moment du test, bien qu'incohérent, le noyau n'arrête rien, et le programme C n'arrête rien non plus, ce qui n'aurait pas été le cas d'autres langages (Pour les langages système, on peut citer Rust). Cela peut être une hypothèse (entre autres) de pourquoi le bug a passé les tests : l'environnement mémoire n'était pas le même, et dans un cas, le noyau ne fait pas planter le programme, dans l'autre il faitt planter le programme (et lui même), et pas de chance, ça tombe en prod. Ben ça, ça arrive typiquement avec C/C++.

Donc au final, un langage moins laxiste sur la gestion de la mémoire, si les hypothèses de départ sont les bonnes, aurait peut être permis d'éviter le problème, car le dit programme aurait planté dès la phase de test.
En disant cela je comprend qu'on ne peut pas se permettre de tout recoder en Rust, mais tout de même, je n'ai jamais trouvé bien pratique cette façon d'accepter n'importe quoi en C en matière de gestion de mémoire, en pariant sur le fait qu'un⋅e développeur⋅e doit être parfait (impossible !), et que cela ne devrait jamais arriver.
votre avatar
Donc si je comprend bien, comme il n'est pas possible de prévenir de tous les bugs, évitons d'essayer de prévenir de quelque bug que ce soit...

On parle bien du bug qui concerne cette news-ci. Le process parcourt une séquence de fichiers de définitions pour définir divers types d'attaques ou signatures de virus. Si un fichier est invalide, il est tout à fait possible de soit l'ignorer, soit d'afficher un warning à l'attention de l'utilisateur et de faire un reporting pour détecter activement de cette anomalie.

Le processus tournait bien avant l'ajout du fichier de définition invalide, le fait de l'ignorer est envisageable.

Je ne dis pas que c'est LA solution à privilégier, je réagis simplement à votre remarque qui dit que finalement, à quoi bon tenter de catcher la moindre exception, étant donné qu'il y en aura toujours bien une qui sera lancée un jour où l'autre et qu'on aura pas prévu...

Dans ce cas-ci, on aurait évité des BSOD qui ont causé des pertes économiques certaines, sans parler des risques pour les personnes dans le cas où ça concerne des postes utilisés en milieu sensible.
votre avatar
Si l'exception pouvait être catché, très bien, il aurait fallu le faire. Je n'ai pas de problème avec l'idée et ne dit pas le contraire. Sauf que là, c'est une exception non prévue, puisque de fait, elle n'était pas attendue ni anticipée, sans doute à tord. Je dis juste que vous ne pouvez pas catcher toutes les exceptions sans réfléchir à chaque fois pourquoi vous le faites.

Je vais partir du cas général, pour arriver au contexte du bug cité plus haut.

Admettons que vous faites "a=1/2". Vous ne pouvez pas faire un catch d'une exception générique de cette opération, en disant j'attrape tout. Si vous avez une division par zéro, c'est incohérent car elle ne devrait pas arriver, donc autant tout faire tout planter (la mémoire peut être instable ou que sais-je). Si vous continuez l'exécution, vous arrivez de toute façon dans un état incohérent.

Autre exemple, si vous n'avez plus assez d'espace mémoire et avez une exception de ce type, vous pouvez essayer de catcher l'exception, mais si vous le faites, vous devez savoir comment vous en sortir sachant que vous n'avez pas assez de mémoire. Et peut être qu'il n'y a pas de bonne façon de s'en sortir ici. Donc en faisant un catch de cela sans y réfléchir, vous risquez de générer une boucle infinie d'exceptions indiquant qu'il n'y a pas assez de mémoire et comme vous catchez l'exception sans prendre des mesures adéquates, ça ne s'arrête jamais, ce qui sans doute n'a pas fait planter le noyau, mais je ne sais pas trop quel serait son état si un de ses drivers tourne à l'infini.

Mais c'est valable en général pour un algorithme. Si votre algorithme arrive dans état complètement illogique (enfin à priori), je dis qu'il faut tout faire planter, ne serait-ce que pour s'en rendre compte pendant les tests. C'est le principe des assertions.

Donc ici, pour revenir au contexte du "sensor"/driver qui ne s'en sortait pas à cause d'un accès mémoire invalide, vous pouvez faire un catch du tout et n'importe quoi, mais si vous faites ça, vous devez vous assurer que ce n'est pas une fausse bonne idée. Alors bien sûr, dans le contexte du noyau, sans doute aurait-il fallu simplement ignorer le chargement du drivers, émettre un rapport comme vous le dite, et passer à autre chose. Mais du coup, vous faites un catch en conscience de ce que vous faites, et vous acceptez de faire sauter un "sensor"/drivers lors du chargement de tous les drivers. Je n'ai pas du tout de problème avec l'idée. Mais au moment où prenez cette décision, vous acceptez que des drivers peuvent être manquants, et devez en tenir dans toute votre architecture, sinon, cela pourrait planter ailleurs, et ainsi de suite. Donc oui, avoir une tolérance à la panne, et faire simplement remonter un rapport aurait sans aucun doute été la bonne solution ici, mais ça ne s'improvise pas, il faut peut être en tenir compte ailleurs dans le reste de l'architecture.

Si vous faites des catchs partout sans prendre la mesure de ce vous êtes en train de faire, cela risque in fine de quand même planter mais beaucoup plus loin dans le programme, et là bon courage pour remonter le bug, surtout si vous avez pleins de rapports qui été pondus pendant 2 jours avant que ça ne plante quand même (je ne dis pas que c'est ce qui se serait passé ici, je n'en sais rien). D'autre part, comme c'est dans l'espace noyau, cela pourrait avoir des conséquences délétères non prévues si vous ne l'avez pas bien encadré. Vous évoquez les problèmes économiques, mais là, on parle d'une machine immobilisée par un BSOD. Étant donné qu'on est dans l'espace noyau, imaginez qu'en catchant tout et n'importe quoi, le noyau se mette à faire tout et n'importe quoi en dézinguant le système de fichier. Là, c'est carrément la cohérence des données du système de fichier que vous pouvez flinguer. Je ne sais pas si on peu en arriver là, et si les gardes fou du noyau sont suffisants, mais je veux dire que dans un contexte économique, il y a pire qu'un BSOD.

Mais encore une fois, dans le contexte de ce bug, il était sans doute possible d'accepter une tolérance à la panne, mais cette tolérance doit tout de même être anticipée et encadrée, ce qui n'a pas été le cas ici et c'est bien dommage. Mais ce n'est pas une décision qu'il faut prendre de manière légère et aveugle comme une règle générale (en tout cas pas pour moi).

Donc je ne dis pas "Donc si je comprend bien, comme il n'est pas possible de prévenir de tous les bugs, évitons d'essayer de prévenir de quelque bug que ce soit...", je dis "essayez de prévenir tous les bugs, y compris ceux que vous comprenez pas à l'avance, acceptez la tolérance à la panne, mais si un bug ou une panne non anticipée arrive quand même alors que vous ne l'avez pas prévu ni encadré, je ne crois pas qu'il faille faire un catch aveugle sans en mesurer les conséquences, d'autant qu'à force de faire ça, vous prenez le risque de rendre un bug silencieux lors des tests, et rendre vos remontées de bug plus pénibles et fastidieuses.
votre avatar
"Mais la question n'est pas de faire un catch, mais quoi faire du catch ?"

Si telle était la question de base, alors dans le cas de figure où on est en train d'analyser les différents fichiers sources pour les ajouter à une liste de règles à appliquer, "que faire de ce catch" peut simplement être répondu par "passer le fichier source en cours dont le contenu à provoquer l'exception catchée".

Mon commentaire ne visait qu'à répondre à cette question précise de ce qu'on aurait bien pu faire de ce catch ;)
votre avatar
"Mais la question n'est pas de faire un catch, mais quoi faire du catch ?"
La réponse appartient au programmeur.

Si c'est dans une section de lecture des paramètres, ma réponse la plus simple est: "application des paramètres de repli".
C'est le cas dans je pense 99% des mes dev depuis 25 ans.
En gros: on commence un dev: on met des paramètres par défaut intégrés dans l'exe (genre mode sans échec), modifiables par fichier de config, un général et un user. Le fichier d'origine fourni pour overrider le défaut peut être celui qui déclenche le 1er paramétrage par un admin. Ou pas.

Planter tout n'est pas une option à ce niveau - bloquer l'ordi, signaler au centre ce commande, utiliser un broadcast/multicast pour se signaler et choper une vraie config ... il y a tant de choix du plus simple au plus complexe.

Euh, ils ont choisi l'amateurisme. Même mes dev IOT à la maison ont un mode fallback.

Quand on développe niveau noyau ou pilote, oui on teste pleins de cas d'erreur pour ne pas tout planter.
votre avatar
C'est pas le principe du fuzzing qui permet de trouver des bugs sans avoir besoin savoir quoi chercher à l'avance.
Je n'ai vue personne l'évoquer, ici ou dans d'autres média.

Il y a même un article sur le blog de Crowdstrike de 2021 qui donne des détails sur leurs utilisation en interne de la méthode. Du coup je suis curieux de savoir ce qui a pu les empêcher de trouver le bug de cette manière. (Et si 3 ans après c'est bien toujours en place)
votre avatar
La panne étant aussi aberrante que les explications, ça laisserait presque croire que c'était un test-en-production pour voir à quel point ce bug pouvait paralyser les infrastructures humaines.
Le succès a été quasi-instantané.

#complotiste

[MàJ] Fiasco CrowdStrike : détails techniques, 8,5 millions de machines touchées selon Microsoft

  • 78 minutes en ligne, des heures pour réparer les dégâts

  • Moins de 1 % des machines sous Windows impactées

  • Attention, les escrocs sont de sortie

  • Channel Files et le fameux C-00000291*.sys…

  • … qui « ne sont pas des pilotes du noyau »

  • CrowdStrike corrige son fichier

  • Inquiétude et rumeurs

  • Pointeur NULL, le retour ? Pas si vite…

Fermer