• Aucun résultat trouvé

Même s’il est simple en apparence, le problème de l’inférence de types par l’algorithme W est EXP-complet [Mai90], c’est-à-dire que les algorithmes efficaces ont une complexité expo-nentielle en la taille du programme. Cependant, lorsqu’on borne la « taille » des types, celle-ci devient quasi-linéaire [McA03], ce qui signifie qu’il n’y a pas de problème de performance à attendre en pratique.

Dans notre cas, on utilise une variante de l’algorithme W pour un langage particulière-ment simple. En particulier il n’y a pas de polymorphisme, ni de fonctions imbriquées, et les types des valeurs globales sont écrites par le programmeur. Cela permet de borner leur taille. En pratique, sur les exemples testés (jusqu’à quelques centaines de lignes de code) nous n’avons pas noté de délai d’exécution notable.

En revanche, la compilation de C vers NEWSPEAKpeut être plus coûteuse, notamment lorsque le fichier d’entrée est de taille importante. Le temps de traitement est plus long que celui d’un compilateur commegccouclang.C2NEWSPEAKa toutefois été utilisé pour com-piler des projets de l’ordre du million de lignes de code source prétraité, et son exécution ne prenait pas plus de quelques minutes.

À titre d’illustration, nous avons mesuré les performances deC2NEWSPEAKetptrtypesur l’exemple « Blackfin » du chapitre suivant. Celui consiste en un fichier prétraité de 853 lignes de code C. Exécuter 1000 foisC2NEWSPEAKsur ce fichier prend 36.3 secondes, alors qu’exé-cuter 1000 foisptrtypesur le fichier NEWSPEAKrésultant ne dure que 8.1 secondes (par com-paraison, lancer 1000 fois/bin/true, commande qui ne fait rien, prend 1.6 seconde).

Les structures internes deC2NEWSPEAKont déjà été améliorées, et d’autres optimisations sont certainement possibles, mais la performance n’est pas bloquante pour le moment : une fois que le code est compilé, on peut réutiliser le fichier objet NEWSPEAKpour d’autres ana-lyses. La compilation est donc relativement rare.

7.4. PERFORMANCE 109

Conclusion

Les analyses de typage correspondant aux chapitres 5 et 6 ont été implantées sous forme d’un prototype utilisant le langage NEWSPEAKdéveloppé par EADS. Cela permet de réutiliser les phases de compilation déjà implantées, et d’exprimer les règles de typage sur un langage suffisament simple.

On utilise un algorithme par unification, qui donne une forme simple au programme d’inférence. Pour chaque expression ou instruction à typer, on détermine grâce au lemme 5.1 quelle règle il faut appliquer. Ensuite, on génère les inconnues de type nécessaires pour ap-pliquer cette règle et on indique les contraintes en appelant la fonction d’unification.

Ce prototype comporte environ 1600 lignes de code OCaml. Il est disponible sous license libre sur [☞3]. Il a été pensé pour traiter un type de code particulier, à savoir le noyau Linux. On montre dans le chapitre suivant que cet objectif est atteint, puisqu’il permet de détecter plusieurs bugs.

C

H A P I T R E

8

ÉTUDE DE CAS : LE NOYAU LINUX

Le noyau Linux, abordé dans le chapitre 2, est un noyau de système d’exploitation déve-loppé depuis le début des années 90 et « figure de proue » du mouvement open-source. Au départ écrit par Linus Torvalds sur son ordinateur personnel, il a été porté au fil des années sur de nombreuses architectures et s’est enrichi de nombreux pilotes de périphériques. Dans la version 3.13.1 (2014), son code source comporte 12 millions de lignes de code (en grande majorité du C) dont 58% de pilotes.

Même si le noyau est monolithique (la majeure partie des traitements s’effectue au sein d’un même fichier objet), les sous-systèmes sont indépendants. C’est ce qui permet d’écrire des pilotes de périphériques et des modules.

Ces pilotes manipulent des données provenant de l’utilisateur, notamment par pointeur. Comme on l’a vu, cela peut poser des problèmes de sécurité si on déréférence ces pointeurs sans vérification.

Dans ce chapitre, on met en œuvre sur le noyau Linux le système de types décrit dans le chapitre 6, ou plus précisément l’outilptrtypedu chapitre 7.

Pour montrer que le système de types capture cette propriété et que l’implantation est utilisable, on étudie les cas de deux bugs qui ont touché le noyau Linux. À chaque fois, dans une routine correspondant à un appel système, un pointeur utilisateur est déréférencé direc-tement, pouvant provoquer une fuite d’informations confidentielles dans le noyau.

On commence par décrire les difficultés rencontrées pour analyser le code du noyau Li-nux. On décrit ensuite l’implantation du mécanisme d’appels système dans ce noyau, et en quoi cela peut poser des problèmes. On détaille enfin les bugs étudiés, et comment les adap-ter pour traiadap-ter le code en question.

8.1 Spécificités du code noyau

Linux est écrit dans le langage C, mais pas dans la version qui correspond à la norme. Il utilise le dialecte GNU C qui est celui que supporte GCC. Une première difficulté pour traiter le code du noyau est donc de le compiler.

Pour traduire ce dialecte, il a été nécessaire d’adapterC2NEWSPEAK. La principale particu-larité est la notation__attribute__((...))qui peut décorer les déclarations de fonctions, de variables ou de types.

Par exemple, il est possible de manipuler des étiquettes de première classe : si «lbl:» est présent avant une instruction, on peut capturer l’adresse de celle-ci avecvoid *p = &&lbl

et y sauter indirectement avecgoto *p.

Une autre fonctionnalité est le concept d’instruction-expression :({bloc})est une ex-pression, dont la valeur est celle de la dernière expression évaluée lors debloc.

Les attributs, quant à eux, rentrent dans trois catégories :

• les annotations de compilation ; par exemple,useddésactive l’avertissement « cette va-riable n’est pas utilisée ».

• les optimisations ; par exemple, les objets marquéshotsont groupés de telle manière qu’ils se retrouvent en cache ensemble.

• les annotations de bas niveau ; par exemple,aligned(n)spécifie qu’un objet doit être aligné sur au moinsnbits.

Dans notre cas, toutes ces annotations peuvent être ignorées, mais il faut tout de même adapter l’analyse syntaxique pour les ignorer. En particulier, pour le traitement du noyau Li-nux, il a fallu traiter certaines formes de la constructiontypeofqui n’étaient pas supportées. De plus, pour que le code noyau soit compilable, il est nécessaire de définir certaines macros. En particulier, le système de configuration de Linux utilise des macros nommées

CONFIG_*pour inclure ou non certaines fonctionnalités. Il a donc fallu faire un choix ; nous avons choisi la configuration par défaut. Pour analyser des morceaux plus importants du noyau, il faudrait définir un fichier de configuration plus important.