Question

Alors que je travaillais sur un projet récent, j'ai reçu la visite d'un représentant de l'assurance qualité client, qui m'a posé une question à laquelle je n'avais pas vraiment réfléchi auparavant :

Comment savez-vous que le compilateur que vous utilisez génère du code machine qui correspond exactement aux fonctionnalités du code C et que le compilateur est entièrement déterministe ?

À cette question, je n’ai eu absolument aucune réponse car j’ai toujours pris le compilateur pour acquis.Il prend du code et génère du code machine.Comment puis-je vérifier que le compilateur n'ajoute pas réellement des fonctionnalités que je ne lui ai pas demandées ?ou encore plus dangereusement implémenter du code d'une manière légèrement différente de celle à laquelle je m'attends ?

Je suis conscient que ce n'est peut-être pas vraiment un problème pour tout le monde, et en effet la réponse pourrait bien être..."vous êtes au-dessus d'un baril et faites-le".Cependant, lorsque vous travaillez dans un environnement embarqué, vous faites implicitement confiance à votre compilateur.Comment puis-je prouver à moi-même et au contrôle qualité que j'ai raison de le faire ?

Était-ce utile?

La solution

Pour les applications embarquées critiques en matière de sécurité, les agences de certification doivent satisfaire à l'exigence « éprouvée en utilisation » pour le compilateur.Il existe généralement certaines exigences (un peu comme les « heures d'ouverture ») qui doivent être respectées et prouvées par une documentation détaillée.Cependant, la plupart des gens ne peuvent pas ou ne veulent pas répondre à ces exigences car cela peut être très difficile, en particulier lors de votre premier projet avec une nouvelle cible/compilateur.

Une autre approche consiste essentiellement à NE PAS faire confiance du tout à la sortie du compilateur.Toute déficience du compilateur et même dépendante du langage (Annexe G de la norme C-90, ça vous dit ?) doit être couverte par un ensemble strict d'analyses statiques, de tests unitaires et de couverture en plus des tests fonctionnels ultérieurs.

Une norme comme MISRA-C peut aider à limiter l'entrée du compilateur à un sous-ensemble "sûr" du langage C.Une autre approche consiste à limiter l'entrée d'un compilateur à un sous-ensemble d'un langage et à tester quelle est la sortie pour l'ensemble du sous-ensemble.Si notre application est uniquement construite à partir de composants du sous-ensemble, on suppose que l'on sait quelle sera la sortie du compilateur.Cela s'appelle généralement "qualification du compilateur".

Le but de tout cela est de pouvoir répondre à la question du représentant QA par "Nous ne nous appuyons pas uniquement sur le déterminisme du compilateur mais c'est ainsi que nous le prouvons...".

Autres conseils

Vous pouvez appliquer cet argument à n’importe quel niveau :faites-vous confiance aux bibliothèques tierces ?faites-vous confiance au système d'exploitation ?faites-vous confiance au processeur ?

Un bon exemple de la raison pour laquelle cela peut être une préoccupation valable, bien sûr, est la façon dont Ken Thompson a inséré une porte dérobée dans le programme de « connexion » d'origine...et modifié le compilateur C de sorte que même si vous recompilez la connexion, vous obtenez toujours la porte dérobée.Regarde ça affectation pour plus de détails.

Des questions similaires ont été soulevées à propos des algorithmes de chiffrement : comment pouvons-nous savoir qu'il n'existe pas de porte dérobée dans DES permettant à la NSA de fouiner ?

À la fin, vous devez décider si vous faites suffisamment confiance à l’infrastructure sur laquelle vous construisez pour ne pas vous en soucier, sinon vous devez commencer à développer vos propres puces de silicium !

Vous le savez en testant.Lorsque vous testez, vous testez à la fois votre code et le compilateur.

Vous constaterez que les chances que vous ou l'auteur du compilateur ayez commis une erreur sont bien inférieures aux chances que vous fassiez une erreur si vous écriviez le programme en question dans un langage assembleur.

Il existe des combinaisons de validation de compilateur disponibles.
Celui dont je me souviens est "Vivace".

Lorsque j'ai travaillé sur un compilateur C pour un processeur SOC intégré, nous avons dû valider le compilateur par rapport à ceci et à deux autres combinaisons de validation (dont j'ai oublié le nom).La validation du compilateur à un certain niveau de conformité à ces combinaisons de tests faisait partie du contrat.

Tout se résume à la confiance.Votre client fait-il confiance à un compilateur ?Utilisez-le, ou au moins comparez le code de sortie entre le vôtre et le leur.

S'ils ne font confiance à aucun, existe-t-il une implémentation de référence pour le langage ?Pourriez-vous les convaincre de lui faire confiance ?Comparez ensuite le vôtre avec la référence ou utilisez la référence.

Tout cela en supposant que vous vérifiez réellement le code réel que vous obtenez du fournisseur/fournisseur et que vous vérifiez que le compilateur n'a pas été falsifié, ce qui devrait être la première étape.

Quoi qu'il en soit, cela laisse encore la question de savoir comment vérifieriez-vous, sans avoir de références, un compilateur, à partir de zéro.Cela ressemble certainement à une tonne de travail et nécessite une définition du langage, qui n'est pas toujours disponible, parfois la définition vient du compilateur.

Comment savez-vous que le compilateur que vous utilisez génère du code machine qui correspond exactement aux fonctionnalités du code C et que le compilateur est entièrement déterministe ?

Ce n'est pas le cas, c'est pourquoi vous testez le binaire résultant et pourquoi vous vous assurez d'expédier le même binaire avec lequel vous avez testé.Et pourquoi, lorsque vous apportez des modifications « mineures » au logiciel, vous effectuez un test de régression pour vous assurer qu’aucune des anciennes fonctionnalités n’est cassée.

Le seul logiciel que j'ai certifié est l'avionique.La certification FAA n'est pas assez rigoureuse pour prouver que le logiciel fonctionne correctement, tout en vous obligeant à franchir un certain nombre d'obstacles.L'astuce consiste à structurer votre « processus » afin qu'il améliore la qualité autant que possible, avec le moins de sauts d'obstacles superflus possible.Donc, tout ce que vous savez ne vaut rien et ne trouvera pas de bugs, vous pourrez probablement vous en sortir.Et tout ce que tu sais que tu devrais faire parce que volonté trouvez des bogues qui ne sont pas explicitement demandés par la FAA, le mieux est de tordre les mots jusqu'à ce que vous ayez l'impression de donner à la FAA/à vos responsables de l'assurance qualité ce qu'ils ont demandé.

En fait, ce n'est pas aussi malhonnête que je l'ai dit, en général, la FAA se soucie davantage de votre conscience et de votre confiance dans votre travail. en essayant faire du bon travail, que ce que vous faites exactement.

On peut trouver quelques munitions intellectuelles dans Crosstalk, un magazine destiné aux ingénieurs en logiciels de défense.C’est le genre de question sur laquelle ils passent de nombreuses heures d’éveil. http://www.stsc.hill.af.mil/crosstalk/2006/08/index.html (Si je peux retrouver mes anciennes notes d'un ancien projet, je reviendrai ici...)

Vous ne pouvez jamais faire entièrement confiance au compilateur, même à ceux fortement recommandés.Ils pourraient publier une mise à jour contenant un bogue et votre code compile la même chose.Ce problème est aggravé lors de la mise à jour de l'ancien code avec le compilateur bogué, des tests et de l'expédition des marchandises uniquement pour que le client vous appelle 3 mois plus tard avec un problème.

Tout revient aux tests, et s'il y a une chose que j'ai apprise, c'est de tester minutieusement après tout changement non trivial.Si le problème semble impossible à trouver, jetez un œil à l'assembleur compilé et vérifiez qu'il fait ce qu'il devrait faire.

A plusieurs reprises j'ai trouvé des bugs dans le compilateur.Une fois, il y avait un bug où les variables 16 bits étaient incrémentées mais sans report et seulement si la variable 16 bits faisait partie d'une structure externe définie dans un fichier d'en-tête.

...vous faites implicitement confiance à votre compilateur

Vous arrêterez de faire cela la première fois que vous rencontrerez un bug du compilateur.;-)

Mais en fin de compte, c’est à cela que servent les tests.Peu importe pour votre programme de tests la façon dont le bug est entré dans votre produit en premier lieu, tout ce qui compte c'est qu'il n'ait pas réussi votre programme de tests approfondi.

Bien..vous ne pouvez pas simplement dire que vous faites confiance à la sortie de votre compilateur, en particulier si vous travaillez avec du code intégré.Il n'est pas difficile de trouver des divergences entre le code généré lors de la compilation du même code avec différents compilateurs.C'est le cas parce que la norme C elle-même est trop souple.De nombreux détails peuvent être implémentés différemment par différents compilateurs sans enfreindre la norme.Comment pouvons-nous gérer ce genre de choses ?Nous évitons autant que possible les constructions dépendantes du compilateur.Nous pouvons y remédier en choisissant un sous-ensemble plus sûr de C comme Misra-C comme mentionné précédemment par l'utilisateur cschol.Je dois rarement inspecter le code généré par le compilateur mais cela m'est aussi arrivé parfois.Mais en fin de compte, vous comptez sur vos tests pour vous assurer que le code se comporte comme prévu.

Existe-t-il une meilleure option ?Certaines personnes prétendent que oui.L'autre option est d'écrire votre code dans ÉTINCELLE/Ada.Je n'ai jamais écrit de code dans SPARK mais je crois comprendre que vous devrez toujours le lier à des routines écrites en C qui traiteraient du "bare metal".La beauté de SPARK/Ada est que vous avez la garantie absolue que le code généré par n'importe quel compilateur sera toujours le même.Aucune ambiguïté.En plus de cela, le langage vous permet d'annoter le code avec des explications sur la façon dont le code est censé se comporter.L'ensemble d'outils SPARK utilisera ces annotations pour prouver formellement que le code écrit fait effectivement ce que les annotations ont décrit.On m'a donc dit que pour les systèmes critiques, SPARK/Ada est un très bon pari.Cependant, je ne l'ai jamais essayé moi-même.

Vous ne savez pas avec certitude que le compilateur fera exactement ce que vous attendez.La raison est bien sûr que un compilateur est un morceau de logiciel, et est donc sensible aux bugs.

Les rédacteurs de compilateurs ont l'avantage de travailler à partir d'une spécification de haute qualité, tandis que le reste d'entre nous doit comprendre ce que nous créons au fur et à mesure.Cependant, les spécifications du compilateur aussi ont des bugs et des pièces complexes avec des interactions subtiles.Il n’est donc pas vraiment trivial de comprendre ce que le compilateur doit faire.

Néanmoins, une fois que vous avez décidé ce que signifie, selon vous, la spécification linguistique, vous pouvez rédiger un bon test rapide et automatisé pour chaque nuance.C’est là que l’écriture du compilateur présente un énorme avantage par rapport à l’écriture d’autres types de logiciels :en test.Chaque bug devient un cas de test automatisé et la suite de tests peut être très approfondie.Les fournisseurs de compilateurs ont beaucoup plus de budget que vous à investir dans la vérification de l’exactitude du compilateur (vous avez déjà un travail quotidien, n’est-ce pas ?).

Qu'est-ce que cela signifie pour toi?Cela signifie que vous devez être ouvert aux possibilités de bugs dans votre compilateur, mais il y a de fortes chances que vous n'en trouviez pas vous-même.

Je choisirais un fournisseur de compilateurs qui ne risque pas de faire faillite de si tôt, qui a un historique de haute qualité dans ses compilateurs et qui a démontré sa capacité à entretenir (corriger) ses produits.Les compilateurs semblent devenir plus corrects avec le temps, donc j'en choisirais un qui date d'environ une décennie ou deux.

Concentrez votre attention sur la réussite de votre code. Si c'est clair et simple, alors quand tu faire Si vous rencontrez un bug du compilateur, vous n'aurez pas à réfléchir très sérieusement pour décider où se situe le problème. Écrire de bons tests unitaires, ce qui garantira que votre code fait ce que vous attendez de lui.

Essayez les tests unitaires.

Si cela ne suffit pas, utilisez différents compilateurs et comparez les résultats de vos tests unitaires.Comparez les sorties strace, exécutez vos tests dans une VM, conservez un journal des E/S disque et réseau, puis comparez-les.

Ou proposez d'écrire votre propre compilateur et dites-leur ce que cela va coûter.

Tout au plus que vous puissiez facilement certifier, c'est que vous utilisez un compilateur non falsifié du fournisseur X.S'ils ne font pas confiance au fournisseur X, c'est leur problème (si X est raisonnablement digne de confiance).S’ils ne font confiance à aucun fournisseur de compilateur, ils sont totalement déraisonnables.

Répondant à leur question :Je m'assure d'utiliser un compilateur non altéré de X via ces moyens.X a une bonne réputation et j'ai également une belle série de tests qui montrent que notre application se comporte comme prévu.

Tout le reste commence à ouvrir la boîte de Pandore.Il faut s'arrêter quelque part, comme le dit Rob.

Parfois, vous obtenez des changements de comportement lorsque vous demandez des niveaux d’optimisation agressifs.

Et l'optimisation et les nombres à virgule flottante ?Oublie ça!

Pour la plupart des développements logiciels (pensez aux applications de bureau), la réponse est probablement que vous ne le savez pas et que vous ne vous en souciez pas.

Dans les systèmes critiques pour la sécurité (pensez aux centrales nucléaires et à l'avionique commerciale), vous faire les agences de soins et de réglementation vous demanderont de le prouver.D'après mon expérience, vous pouvez procéder de deux manières :

  1. Utilisez un compilateur qualifié, où « qualifié » signifie qu'il a été vérifié conformément aux normes établies par l'agence de réglementation.
  2. Effectuer une analyse du code objet.Essentiellement, vous compilez un morceau de code de référence, puis analysez manuellement la sortie pour démontrer que le compilateur n'a inséré aucune instruction qui ne puisse être retracée à votre code source.

Vous obtenez celui que Dijkstra a écrit.

Sélectionnez un compilateur formellement vérifié, comme le compilateur Compcert C.

  1. Changer le niveau d'optimisation du compilateur modifiera la sortie.
  2. De légères modifications apportées à une fonction peuvent rendre le compilateur en ligne ou ne plus intégrer une fonction.
  3. Les modifications apportées au compilateur (versions gcc par exemple) peuvent modifier la sortie
  4. Certaines fonctions de la bibliothèque peuvent être intrinsèques (c'est-à-dire émettre un assemblage optimisé) tandis que d'autres ne le sont pas.

La bonne nouvelle est que pour la plupart des choses, cela n’a pas vraiment d’importance.Lorsque c'est le cas, vous souhaiterez peut-être envisager l'assemblage si cela compte vraiment (par exemple, dans un ISR).

Si vous êtes préoccupé par un code machine inattendu qui ne produit pas de résultats visibles, le seul moyen est probablement de contacter le fournisseur du compilateur pour obtenir une certification qui satisfera votre client.

Sinon, vous le saurez de la même manière que vous connaissez les bugs dans votre code : les tests.

Le code machine des compilateurs modernes peut être très différent et totalement incompréhensible pour les humains chétifs.

Je pense qu'il est possible de réduire ce problème au Problème d'arrêt d'une manière ou d'une autre.

Le problème le plus évident est que si vous utilisez un programme pour analyser le compilateur et son déterminisme, comment savez-vous que ton le programme est-il compilé correctement et produit-il le résultat correct ?

Si vous utilisez un autre compilateur "sûr", je n'en suis pas sûr.Ce dont je suis sûr, c'est qu'écrire un compilateur à partir de zéro serait probablement une tâche plus facile.

Même un compilateur qualifié ou certifié peut produire des résultats indésirables.Gardez votre code simple et testez, testez, testez.Cela ou parcourir le code machine à la main sans autoriser aucune erreur humaine.PLus le système d'exploitation ou tout autre environnement sur lequel vous utilisez (de préférence pas de système d'exploitation, juste votre programme).

Ce problème a été résolu dans les environnements critiques depuis le début des logiciels et des compilateurs.Comme beaucoup d’autres qui ont répondu le savent également.Chaque secteur a ses propres règles, des compilateurs certifiés au style de programmation (vous devez toujours programmer de cette façon, ne jamais utiliser ceci ou cela ou l'autre), de nombreux tests et examens par les pairs.Vérifier chaque chemin d'exécution, etc.

Si vous n’appartenez pas à l’une de ces industries, vous obtenez ce que vous obtenez.Un programme commercial sur un système d'exploitation COTS sur du matériel COTS.Cela échouera, c’est une garantie.

Si tu t'inquiètes pour mal intentionné bogues dans le compilateur, une recommandation (IIRC, une exigence de la NSA pour certains projets) est que le binaire du compilateur soit antérieur à l'écriture du code.Au moins, tu sais que personne n'a ajoutée bogues ciblés sur ton programme.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top