L'utilisation d'un compilateur C obsolète représente-t-elle un risque pour la sécurité?

140

Nous avons des systèmes de construction en production dont personne ne se soucie et ces machines exécutent des versions anciennes de GCC comme GCC 3 ou GCC 2.

Et je ne peux pas persuader la direction de le mettre à niveau vers un plus récent: ils disent, "si ce n'est pas cassé, ne le réparez pas".

Puisque nous maintenons une base de code très ancienne (écrite dans les années 80), ce code C89 se compile très bien sur ces compilateurs.

Mais je ne suis pas sûr que ce soit une bonne idée d'utiliser ces vieux trucs.

Ma question est:

L'utilisation d'un ancien compilateur C peut-elle compromettre la sécurité du programme compilé?

METTRE À JOUR:

Le même code est construit par Visual Studio 2008 pour les cibles Windows, et MSVC ne prend pas encore en charge C99 ou C11 (je ne sais pas si le nouveau MSVC le fait), et je peux le construire sur ma machine Linux en utilisant le dernier GCC. Donc, si nous abandonnions simplement un GCC plus récent, il serait probablement aussi bien construit qu'avant.

Calmaire
la source
5
Question intéressante - cela vaut peut-être aussi la peine d'être lu rapidement - developer.slashdot.org/story/13/10/29/2150211/… .. donc les nouveaux compilateurs pourraient également compromettre la sécurité lors de l'optimisation.
Neil
6
Ces anciennes versions de gcc prennent-elles en charge la compilation en PIC / PIE pour ASLR? Supportent-ils les canaris de pile? W ^ X (NX)? Sinon, le manque d'atténuation des vulnérabilités est une bonne raison de mettre à niveau.
EOF
12
Le simple fait de regarder les avertissements de gcc 4.x peut immédiatement révéler toute une série de failles de sécurité existantes que vous ne saviez pas que vous aviez.
OrangeDog
7
@OrangeDog: Pourquoi gcc 4.x? gcc6 est la série de versions actuelle, et gcc 5 existe depuis un certain temps. Mais oui, la résolution des problèmes identifiés par -O3 -Wall -Wextra -fsanitize=undefinedavec gcc et clang modernes devrait aider.
Peter Cordes
4
@OrangeDog GCC est passé aux numéros de version marketing. GCC 5 méritait un changement de version majeur, car ils ont changé les standards C et C ++ par défaut et l'ABI libstdc ++. GCC 6 aurait dû s'appeler 5.1.
zwol

Réponses:

102

En fait, je dirais le contraire.

Il existe un certain nombre de cas où le comportement n'est pas défini par le standard C mais où il est évident ce qui se passerait avec un "compilateur stupide" sur une plate-forme donnée. Des cas comme permettre à un entier signé de déborder ou d'accéder à la même mémoire via des variables de deux types différents.

Les versions récentes de gcc (et clang) ont commencé à traiter ces cas comme des opportunités d'optimisation ne se souciant pas de savoir si elles modifient le comportement du binaire dans la condition de «comportement indéfini». C'est très mauvais si votre base de code a été écrite par des personnes qui traitaient C comme un "assembleur portable". Au fil du temps, les optimiseurs ont commencé à regarder des morceaux de code de plus en plus gros lors de ces optimisations, augmentant les chances que le binaire finisse par faire autre chose que "ce qu'un binaire construit par un compilateur stupide" ferait.

Il existe des commutateurs de compilateur pour restaurer le comportement "traditionnel" (-fwrapv et -fno-strict-aliasing pour les deux que j'ai mentionnés ci-dessus), mais vous devez d'abord les connaître.

Alors qu'en principe un bogue du compilateur pourrait transformer un code conforme en une faille de sécurité, je considérerais le risque comme négligeable dans le grand schéma des choses.

plugwash
la source
13
Mais cet argument fonctionne dans les deux sens. Si un compilateur a un "comportement indéfini" prévisible, alors il peut être sans doute plus facile d'en faire un usage malveillant ...
André
18
@Andre Le code compilé a de toute façon un comportement indéfini prévisible. Autrement dit, une fois que le code a été compilé, tout comportement imprévisible est maintenant prévisible, dans cette version compilée particulière.
user253751
6
people who treated C like a "portable assembler"n'est-ce pas ce que C est?
Max
10
@Max Cette réponse met en garde précisément sur le fait que la notion d '«assembleur portable» est au moins dépassée en pratique, en raison des optimiseurs modernes. Et je dirais que cela n'a jamais été conceptuellement correct au départ.
Theodoros Chatzigiannakis
6
Aucune sympathie ici pour ceux qui comptent sur un comportement indéfini et commencent plus tard à récolter ce qu'ils ont semé. Cela ne signifie pas que les nouveaux compilateurs sont intrinsèquement moins sécurisés - cela signifie que le code non conforme était une bombe à retardement. Le blâme doit être réparti en conséquence.
underscore_d
52

Il y a des risques dans les deux modes d'action.


Les compilateurs plus anciens ont l'avantage de la maturité, et tout ce qui y était cassé a probablement (mais il n'y a aucune garantie) été contourné avec succès.

Dans ce cas, un nouveau compilateur est une source potentielle de nouveaux bogues.


D'autre part, les nouveaux compilateurs sont livrés avec des outils supplémentaires :

  • GCC et Clang disposent désormais tous deux de désinfectants qui peuvent instrumenter l'exécution pour détecter des comportements indéfinis de différentes sortes (Chandler Carruth, de l'équipe Google Compiler, a affirmé l'année dernière qu'il s'attend à ce qu'ils aient atteint une couverture complète)
  • Clang, au moins, dispose d'un durcissement , par exemple Control Flow Integrity consiste à détecter les hi-jacks du flux de contrôle, il existe également des outils de durcissement pour se protéger contre les attaques de destruction de pile (en séparant la partie flux de contrôle de la pile de la partie données) ; les fonctionnalités de renforcement sont généralement faibles (<1% de surcharge du processeur)
  • Clang / LLVM travaille également sur libFuzzer , un outil pour créer des tests unitaires de fuzzing instrumentés qui explorent intelligemment l'espace d'entrée de la fonction testée (en ajustant l'entrée pour prendre des chemins d'exécution pas encore explorés)

L'instrumentation de votre binaire avec les désinfectants (Address Sanitizer, Memory Sanitizer ou Undefined Behavior Sanitizer) puis le fuzzing (en utilisant American Fuzzy Lop par exemple) a découvert des vulnérabilités dans un certain nombre de logiciels de haut niveau, voir par exemple cet article de LWN.net .

Ces nouveaux outils, et tous les futurs outils, vous sont inaccessibles à moins que vous ne mettiez à niveau votre compilateur.

En restant sur un compilateur sous-alimenté, vous mettez la tête dans le sable et croisez les doigts qu'aucune vulnérabilité n'est trouvée. Si votre produit est une cible de grande valeur, je vous exhorte à reconsidérer.


Remarque: même si vous ne mettez PAS à niveau le compilateur de production, vous voudrez peut-être utiliser un nouveau compilateur pour vérifier la vulnérabilité de toute façon; sachez que, comme ce sont des compilateurs différents, les garanties sont cependant amoindries.

Matthieu M.
la source
1
+1 pour avoir pris la peine de mentionner les cas dans lesquels les nouveaux compilateurs peuvent être plus sûrs, plutôt que d'empiler sur le train en marche «b-mais mon bon vieux UB» des autres réponses. cela s'ajoute aux nombreuses autres améliorations qu'ils offrent qui ne sont pas directement liées à la sécurité, mais qui donnent encore plus d'élan pour être raisonnablement moderne.
underscore_d
Bien que cela ressemble à prôner «la sécurité par l'obscurité»; les bogues qui affectent les anciens compilateurs sont connus et publics. Bien que je sois d'accord que les nouveaux compilateurs introduiront des bogues, ces bogues ne sont pas encore publics comme ceux des versions précédentes, ce qui est une sécurité si vous mettez souvent à jour l'application.
The6P4C
Chandler Carruth est si mignon et parle de choses merveilleuses. Je l'épouserais si je le pouvais.
Daniel Kamil Kozar
46

Votre code compilé contient des bogues qui pourraient être exploités. Les bogues proviennent de trois sources: des bogues dans votre code source, des bogues dans le compilateur et les bibliothèques, et un comportement indéfini dans votre code source que le compilateur transforme en bogue. (Un comportement non défini est un bogue, mais pas encore un bogue dans le code compilé. Par exemple, i = i ++; en C ou C ++ est un bogue, mais dans votre code compilé, il peut augmenter i de 1 et être OK, ou définir i à des ordures et être un bogue).

Le taux de bogues dans votre code compilé est probablement faible en raison des tests et de la correction de bogues dus aux rapports de bogues des clients. Il peut donc y avoir eu un grand nombre de bogues au départ, mais cela a disparu.

Si vous mettez à niveau vers un compilateur plus récent, vous risquez de perdre les bogues introduits par les bogues du compilateur. Mais ces bogues seraient tous des bogues que, à votre connaissance, personne n'a trouvé et personne n'a exploité. Mais le nouveau compilateur peut avoir des bogues en lui-même, et surtout les nouveaux compilateurs ont une tendance plus forte à transformer un comportement non défini en bogues dans le code compilé.

Vous aurez donc beaucoup de nouveaux bogues dans votre code compilé; tous les bogues que les pirates pourraient trouver et exploiter. Et à moins que vous ne fassiez beaucoup de tests et que vous ne laissiez votre code aux clients pour trouver des bogues pendant longtemps, il sera moins sécurisé.

gnasher729
la source
6
Donc en d'autres termes ... il n'y a pas de moyen facile de dire quels problèmes le compilateur introduit, et en changeant tout ce que vous faites est d'obtenir un ensemble différent de problèmes inconnus?
Jeremy Kato
1
@JeremyKato: eh bien, il y a des cas où vous rencontrez également un ensemble différent de problèmes connus. Je ne sais pas quelles sont les failles de sécurité connues dans le compilateur lui-même, mais pour un exemple concret, supposons que la mise à jour vers un nouveau compilateur signifie également pouvoir prendre la dernière libc (tout en utilisant l'ancienne signifie ne pas pouvoir pour ce faire), alors vous sauriez que vous corrigez cette faille dans getaddrinfo(): access.redhat.com/articles/2161461 . Cet exemple n'est pas en fait une faille de sécurité du compilateur, mais sur plus de 10 ans, il y aura forcément des failles corrigées connues.
Steve Jessop
2
Hé, en fait, cette faille n'a été introduite qu'en 2008, donc le questionneur pourrait en être à l'abri. Mais mon point ne concerne pas cet exemple particulier, c'est qu'il existe des bogues connus qu'une ancienne chaîne d'outils mettra dans votre code. Ainsi, lorsque vous mettez à jour, il est vrai que vous introduisez un nouvel ensemble d'inconnues, mais ce n'est pas tout ce que vous faites . Vous devez simplement deviner si vous êtes "plus sûr" en laissant dans la faille critique connue que la dernière chaîne d'outils corrige, ou en prenant les conséquences inconnues de relancer les dés sur tout le comportement non défini dans votre propre code.
Steve Jessop
19

Si ce n'est pas cassé, ne le répare pas

Votre patron a raison de dire cela, cependant, le facteur le plus important est la protection des entrées, des sorties et des débordements de tampon. L'absence de ceux-ci est invariablement le maillon le plus faible de la chaîne de ce point de vue quel que soit le compilateur utilisé.

Cependant, si la base de code est ancienne et que des travaux ont été mis en place pour atténuer les faiblesses du K&R C utilisé, telles que le manque de sécurité de type, les fgets non sécurisés, etc., posez la question "La mise à niveau du compilateur vers un C99 plus moderne / Les normes C11 cassent tout? "

À condition qu'il existe un chemin clair pour migrer vers les nouvelles normes C, ce qui pourrait induire des effets secondaires, il serait peut-être préférable d'essayer un fork de l'ancienne base de code, de l'évaluer et de mettre en place des vérifications de type supplémentaires, des vérifications de cohérence et de déterminer si la mise à niveau vers le nouveau compilateur a un effet sur les ensembles de données d'entrée / sortie.

Ensuite, vous pouvez le montrer à votre patron, " Voici la base de code mise à jour, refactorisée, plus conforme aux normes C99 / C11 acceptées par l'industrie ... ".

C'est le pari sur lequel il faudrait peser, très soigneusement , la résistance au changement pourrait apparaître dans cet environnement et refuser de toucher aux nouveautés.

ÉDITER

Je me suis assis pendant quelques minutes, j'ai réalisé cela, le code généré par K&R pourrait fonctionner sur une plate-forme 16 bits, il y a de fortes chances que la mise à niveau vers un compilateur plus moderne puisse en fait casser la base de code, je pense en termes d'architecture, du code 32 bits serait généré , cela pourrait avoir des effets secondaires amusants sur les structures utilisées pour les ensembles de données d'entrée / sortie, c'est un autre facteur énorme à peser avec soin.

De plus, comme OP a mentionné l'utilisation de Visual Studio 2008 pour créer la base de code, l'utilisation de gcc pourrait induire l'introduction dans l'environnement de MinGW ou de Cygwin, ce qui pourrait avoir un impact sur l'environnement, à moins que la cible soit pour Linux, alors ce serait vaut le détour, peut devoir inclure des commutateurs supplémentaires dans le compilateur pour minimiser le bruit sur l'ancienne base de code K&R, l'autre chose importante est d'effectuer de nombreux tests pour s'assurer qu'aucune fonctionnalité n'est interrompue, cela peut s'avérer un exercice douloureux.

t0mm13b
la source
Le même code est construit par Visual Studio 2008 pour les cibles Windows, et MSVC ne prend pas encore en charge C99 ou C11 (je ne sais pas si le plus récent MSVC le fait), et je peux le construire sur ma machine Linux en utilisant le dernier GCC. Donc, si nous abandonnions simplement un GCC plus récent, il serait probablement aussi bien construit qu'avant.
Calmarius
@Calmarius merci pour la mise en garde, il serait peut-être préférable de modifier votre question pour inclure le commentaire, c'est important :) Et aurait dû être là; D
t0mm13b
@Calmarius a édité ma réponse, qui est ma réflexion sur la question récemment mise à jour.
t0mm13b
2
"Pourrait fonctionner sur une plate-forme 16 bits, il y a de fortes chances que la mise à niveau vers un compilateur plus moderne puisse en fait casser la base de code, je pense en termes d'architecture, de code 32 bits" Je ne pense pas que la question soit de porter le code vers une nouvelle implémentation définie paramètres.
Pascal Cuoq
D'accord. Il est possible qu'une vulnérabilité d'exécution puisse être créée par un bogue du compilateur. Mais il est beaucoup plus probable que le code contienne des vulnérabilités d'exécution dues à des éléments tels que les dépassements de tampon et de pile. Ainsi, lorsque vous investissez du temps dans la sécurisation de cette base de code, vous devez l'investir dans des actions telles que vérifier la longueur des chaînes d'entrée pour vous assurer qu'elles ne dépassent pas les limites de votre programme. Obtenir un compilateur plus récent n'aidera pas beaucoup. Réécrire le code à partir de zéro dans un langage avec des objets de chaîne et de tableau natifs aidera beaucoup. Mais votre patron ne paiera pas pour ça.
O. Jones
9

L'utilisation d'un ancien compilateur C peut-elle compromettre la sécurité du programme compilé?

Bien sûr, cela peut, si l'ancien compilateur contient des bogues connus qui, selon vous, affecteront votre programme.

La question est, n'est-ce pas? Pour être sûr, vous devrez lire l'intégralité du journal des modifications de votre version à la date actuelle et vérifier chaque bogue corrigé au fil des ans.

Si vous ne trouvez aucune preuve de bogues du compilateur qui pourraient affecter votre programme, mettre à jour GCC juste pour le plaisir semble un peu paranoïaque. Vous devez garder à l'esprit que les versions plus récentes peuvent contenir de nouveaux bogues, qui ne sont pas encore découverts. De nombreux changements ont été apportés récemment avec le support de GCC 5 et C11.

Cela étant dit, le code écrit dans les années 80 est probablement déjà rempli à ras bord de failles de sécurité et de dépendance à un comportement mal défini, quel que soit le compilateur. Nous parlons ici de C pré-standard.

Lundin
la source
6
Je ne pense pas que ce soit de la paranoïa; Je pense que l'OP essaie d'inventer des raisons pour convaincre son patron. L'OP veut probablement en fait un nouveau compilateur car ils font de meilleurs asm (y compris l'optimisation entre fichiers avec LTO), ont des diagnostics / avertissements plus utiles et permettent des fonctionnalités et une syntaxe en langage moderne. (par exemple C11 stdatomic).
Peter Cordes
9

Il existe un risque de sécurité où un développeur malveillant peut se faufiler en dérobant un bogue du compilateur. En fonction de la quantité de bogues connus dans le compilateur utilisé, la porte dérobée peut paraître plus ou moins discrète (dans tous les cas, le fait est que le code est correct, même s'il est alambiqué, au niveau de la source. Revues et tests du code source à l'aide de un compilateur non bogué ne trouvera pas la porte dérobée, car la porte dérobée n'existe pas dans ces conditions). Pour des points de déni supplémentaire, le développeur malveillant peut également rechercher lui-même des bogues de compilateur auparavant inconnus. Encore une fois, la qualité du camouflage dépendra du choix des bogues du compilateur trouvés.

Cette attaque est illustrée sur le programme sudo dans cet article . bcrypt a écrit un excellent suivi pour les minificateurs Javascript .

En dehors de cette préoccupation, l'évolution des compilateurs C a été d'exploiter un comportement non défini plus et plus et plus agressive, si vieux code C qui a été écrit de bonne foi serait effectivement plus sûr compilé avec un compilateur C à partir du moment ou compilé à -O0 (mais de nouvelles optimisations exploitant UB révolutionnaires sont introduites dans les nouvelles versions des compilateurs même à -O0 ).

Pascal Cuoq
la source
7

Les compilateurs plus anciens peuvent ne pas avoir de protection contre les attaques de piratage connues. La protection contre le bris de pile, par exemple, n'a pas été introduite avant GCC 4.1 . Donc oui, le code compilé avec des compilateurs plus anciens peut être vulnérable d'une manière contre laquelle les nouveaux compilateurs se protègent.

DrMcCleod
la source
6

Un autre aspect à craindre est le développement d'un nouveau code .

Les compilateurs plus anciens peuvent avoir un comportement différent pour certaines fonctionnalités du langage que ce qui est normalisé et attendu par le programmeur. Cette discordance peut ralentir le développement et introduire des bogues subtils qui peuvent être exploités.

Les compilateurs plus anciens offrent moins de fonctionnalités (y compris les fonctionnalités de langage!) Et n'optimisent pas aussi. Les programmeurs contourneront ces lacunes - par exemple en réimplémentant des fonctionnalités manquantes, ou en écrivant du code intelligent qui est obscur mais s'exécute plus rapidement - créant de nouvelles opportunités pour la création de bogues subtils.


la source
5

Nan

La raison est simple, l'ancien compilateur peut avoir d'anciens bogues et exploits, mais le nouveau compilateur aura de nouveaux bogues et exploits.

Vous ne "corrigez" aucun bogue en passant à un nouveau compilateur. Votre changement d'anciens bogues et exploits pour de nouveaux bogues et exploits.

coteyr
la source
3
Celles-ci semblent très simplistes: le nouveau compilateur a peut-être ses faiblesses, mais je m'attendrais à ce qu'elles soient moins importantes que dans l'ancien compilateur, et il est susceptible de détecter plusieurs vulnérabilités de code qui sont devenues connues depuis.
PJTraill
Mais le nouveau compilateur peut avoir de nouvelles faiblesses inconnues. Le compilateur seul n'est pas un risque de sécurité nécessitant une mise à jour. Vous ne réduisez pas votre surface. Vous échangez un ensemble de problèmes connus pour un ensemble inconnu.
coteyr
Les outils pour aider à trouver des bogues se sont considérablement améliorés depuis les premiers jours de GCC, et ces outils (analyse statique, analyse dynamique de code instrumenté / désinfectants, fuzzers, etc.) ont également été appliqués au code du compilateur, pour aider à améliorer la qualité. Il était beaucoup plus difficile de trouver toutes les classes de bogues à l'époque de GCC 2. Comparaison des bogues du compilateur sur les versions - voir page 7: cs.utah.edu/~regehr/papers/pldi11-preprint.pdf GCC 4.5 et LLVM 2.8 (dernier à la publication) ont le moins de bogues dus au fuzzing.
Jetski S-type
2

Eh bien, il y a une probabilité plus élevée que les bogues de l'ancien compilateur soient bien connus et documentés par opposition à l'utilisation d'un nouveau compilateur, donc des actions peuvent être prises pour éviter ces bogues en codant autour d'eux. Donc, d'une manière qui n'est pas suffisante comme argument pour la mise à niveau. Nous avons les mêmes discussions là où je travaille, nous utilisons GCC 4.6.1 sur une base de code pour les logiciels embarqués et il y a une grande réticence (parmi la direction) à mettre à niveau vers le dernier compilateur par crainte de nouveaux bogues non documentés.

AndersK
la source
0

Votre question se divise en deux parties:

  • Explicite: "Y a-t-il un plus grand risque à utiliser l'ancien compilateur" (plus ou moins comme dans votre titre)
  • Implicite: "Comment persuader la direction de procéder à une mise à niveau"

Peut-être pouvez-vous répondre aux deux en trouvant une faille exploitable dans votre base de code existante et en montrant qu'un compilateur plus récent l'aurait détectée. Bien sûr, votre direction peut dire «vous avez trouvé cela avec l'ancien compilateur», mais vous pouvez souligner que cela a coûté des efforts considérables. Ou vous l'exécutez via le nouveau compilateur pour trouver la vulnérabilité, puis l'exploitez, si vous êtes en mesure / autorisé à compiler le code avec le nouveau compilateur. Vous pouvez avoir besoin de l'aide d'un pirate informatique amical, mais cela dépend de leur confiance et de la capacité / l'autorisation de leur montrer le code (et d'utiliser le nouveau compilateur).

Mais si votre système n'est pas exposé aux pirates, vous devriez peut-être être plus intéressé par la question de savoir si une mise à niveau du compilateur augmenterait votre efficacité: l'analyse de code MSVS 2013 trouve assez souvent des bogues potentiels beaucoup plus tôt que MSVS 2010, et prend plus ou moins en charge C99 / C11 - je ne sais pas si c'est officiellement le cas, mais les déclarations peuvent suivre des instructions et vous pouvez déclarer des variables dans for-loops.

PJTraill
la source