Blog Haypo

Aller au contenu | Aller au menu | Aller à la recherche

samedi 27 octobre 2007

Analyse statique de code et audit de sécurité

Alors que je cherchais un outil d'analyse statique pour PHP, j'étais tombé sur la page Tool Survey du projet Software Assurance Metrics And Tool Evaluation. Ce projet, mené par le NIST et financé par le DHS, m'intéresse car il vise à tester et classer divers outils servant pour un audit de sécurité. Plus particulièrement, la page Source Code Security Analyzers dresse une longue liste d'outils d'analyse statique de code.

Rien ne vaut une relecture manuelle et attentive

Je garde un souvenir amer de l'analyse statique. J'avais testé SPlint, FlawFinder et RATS pour m'aider à relire le code source du pare-feu NuFW écrit en C. Ces outils sont peu efficaces car on est rapidement noyés sous une tonne de faux positifs. On perd finalement plus de temps à affiner leur configuration qu'à trouver des bugs. Je préfère encore une relecture manuelle et attentive du code.

Quand les ressources humaines sont insuffisantes pour relire toute la base de code, il faut choisir efficacement les portions de code à auditer. Il vaut mieux commencer par celles qui traitent les données venant de l'utilisateur. Une autre approche est de rechercher des erreurs classiques comme les dépassements de tampon, erreur de formatage printf, injection SQL, etc. Je vous conseille d'ailleurs d'aller lire les recommandations du CERT : Secure Coding Standards.

Trouver une erreur memset avec Google

On peut s'amuser à exploiter les moteurs de recherche de code pour trouver des failles connues : koders.com, krugle.com et Google codesearch. Seul Google codesearch supporte les expressions régulières, sans référence arrière malheureusement. On peut également trouver des mots de passe codés en dur et autres indiscrétions. C'est le moment de redécouvrir la Google Hacking Database pour vous donner des idées. Recherchons par exemple une utilisation incorrecte de la fonction memset() par l'inversion du 2e et 3e argument. Utilisez le motif suivant avec Google codesearch :

memset\([^,]+,[^,]+,\ *0\)

On trouve cette erreur dans divers projets dont certains très connus : OpenSSL, GnuPG, Prelude, Linux, Mozilla, Python, Parrot, Kaffe, aMule, µClibc, libgphoto2, ATI gatos, WINE, Blender, etc. Après vérification dans quelques projets (en particulier OpenSSL, GnuPG et Prelude !), l'erreur est corrigée dans la dernière version. Le fait qu'elle ait existé un temps prouve que de meilleurs outils d'analyse statique seraient utiles !

Autre exemple : erreur memcpy

L'instruction « memcpy(dest, source, sizeof(dest)); » est incorrecte quand dest est un pointeur. La taille copiée est celle du pointeur et non pas celles des données pointées ! Voici donc deux commandes pour rechercher des utilisations incorrectes des fonctions memcpy(), memmove(), strncpy(), g_memdup(), memset() et wmemset() :

find DOSSIER -name "*.c"|xargs egrep -H '(memcpy|memmove|strncpy|memset|g_memdup)\(([^,]+), .*sizeof\(\2\)\)'
find DOSSIER -name "*.c"|xargs egrep -H 'w?memset([^,]+,[^,]+, *0)'

Sauf que strncpy() (et ses voisines) peuvent fonctionner pour « strncpy(dest, source, sizeof(dest)); » quand dest n'est pas pas un pointeur mais un tableau de taille fixe comme « char buffer[256]; »...

Complexité McCabe d'une fonction

Pour finir, un ami (misc) m'a fait découvrir aujourd'hui l'outil pmccabe par le billet The Cyclomatic Horror From Outer Space. Ce programme sert à estimer la complexité d'une fonction sachant qu'une note supérieure à 50 indique une fonction « non testable (risque très élevé) ». Je me suis amusé à lancer pmccabe sur la GNU libc avec la commande :

rm /tmp/out; find . -name "*.c"|xargs pmccabe >>/tmp/out; sort -nr /tmp/out|head

Voici les pires fonctions :

  • 494 : scanf()
  • 230 : fnmatch()
  • 222 : strtod()
  • 200 : collate_read()
  • 197 : dl_main()
  • 175 : wordexp()

Dans les commentaires du blog, on apprend que gcc explose le record avec une fonction à plus de 1000. Une scéance de refactoring cuisse-abdo-fessier ne ferait pas de mal à la libc et à gcc...

mercredi 24 octobre 2007

Dasher, Chaîne de Markov, N-gramme, Google et Pangramme

Comme j'ai la mémoire courte, j'oublie souvent le nom d'articles qui m'intéressent pourtant beaucoup. Ce billet reprend donc des idées (concepts, techniques, etc.) qui me passionnent mais que j'oublie à répétition.

Logiciel dasher

Aujourd'hui, je parlais de dasher à un ami. Ce logiciel sert à écrire du texte avec un dispositif de pointage ayant uniquement 2 axes (X et Y) tel qu'une souris, une manette de jeu ou les yeux (avec une caméra). L'animation ci-dessous en présente le fonctionnement :

Le point qui m'intriguait était de savoir comment dasher décide quelles lettres proposer à l'utilisateur pendant qu'il écrit son texte. Effectivement, dasher ne propose pas simplement l'alphabet, les chiffres et la ponctuation à chaque fois : ce serait trop pénible.

Chaînes de Markov

J'ai alors pensé aux chaînes de Markov : un modèle qui permet justement de répondre à ce besoin. Je maîtrise mal le sujet et je vais donc expliquer avec mes mots ce que j'en ai compris. Les chaînes de Markov servent à prédire quelles lettres sont les plus probables selon celles précédemment saisies. On utilise pour cela des « N-grammes » où N est le nombre de lettres, exemple de bigrammes : « de », « la », etc. En partant d'un corpus (grosse base de textes), on va construire nos chaînes de Markov donnant la probabilité d'apparition des N-grammes dans la langue du corpus. À partir de ces résultats, il y a une autre application rigolote : la génération de texte aléatoire. On peut effectivement crée un texte purement artificiel qui a l'air vrai mais est en fait le résultat d'un ordinateur ! Articles Wikipédia à lire pour en savoir plus : algorithme de Viterbi et modèle de Markov caché.

N-grammes de Google

La société Google a annoncé en août 2006 qu'elle allait distribuer ses N-grammes. Ce qui a été fait en septembre 2006 par l'intermédiaire de l'Institut des données linguistiques qui vend ces données sous forme de 6 DVD. L'ensemble est proposé au prix prix de US$150 plus US$50 pour les non-membres, ce qui fait un total de 146 €. Par contre, interdiction de rediffuser ces données ou de les utiliser à des fins commerciales ! Dommage car c'est, apparemment, la plus grosse base de données mondiale de N-grammes : 24 Go compressés par gzip en UTF-8 comprenant 1.024.908.267.229 lexèmes et 95.119.665.584 phrases. Il faut savoir que Google a utilisé Internet comme corpus. On peut donc se demander si c'est normal qu'il revende ses résultats, car très peu de sites Internet sont libres de droit !

Pangramme

Pour finir, un article qui n'a rien à voir mise à part la sonorité avec N-gramme : Pangramme. Cet article a résolu une énigme que je me posais depuis de nombreuses années : pourquoi Windows affiche « Portez ce vieux whisky au juge blond qui fume », une phrase qui sonne faux, pour la prévisualisation d'une police de caractères ? C'est simple, c'est une des phrases les plus courtes comportant les 26 lettres de l'alphabet français !

mardi 16 octobre 2007

Information, énergie et ordinateur réversible

En lisant le journal « Une équivalence entre l'énergie et l'information ? », je me suis mis à rechercher des articles sur les questions soulevées pour tenter d'y répondre. J'ai croisé les informations avec les articles que j'avais déjà collectés sur les ordinateurs réversibles. Le billet qui suit est un petit état de l'art en vrac sur le lien entre l'énergie et l'information et ses diverses applications actuelles et futures.

Lire la suite

lundi 15 octobre 2007

Linus corrige un bug dans gcc

J'avais déjà lu que le noyau Linux est un bon test pour une version de gcc. J'en ai maintenant la preuve avec une nouvelle démonstration de Linus Torvald. L'histoire commence avec un courriel du développeur noyau suractif Ingo Molnar le 2 octobre vers minuit. Six heures plus tard, Linus répond que c'est un bogue du compilateur gcc.

Il montre que le code en langage machine cité dans le Oops noyau recopié dans le courriel d'Ingo ne correspond pas au code source C du noyau :

Code: 89 45 f0 76 77 eb 7a 8b 55 ec 8b 4d ec 
89 f7 8b 02 89 c2 03 51 0c 29 c7 89 f0 89 79 
0c 29 d0 eb 6c 89 f8 88 06 46 eb 54 8b 55 f0 
<8b> 3a 42 89 55 f0 89 f9 84 c9 74 d0 8b 45 
08 0f be d9 89 da e8

Il écrit :

Lookie here:
- the bug happens on this:
       char c = *p++;
- which has been compiled into
       8b 3a           mov    (%edx),%e
which is a *word* access.

Quarante minutes plus tard, Ingo répond qu'il demeure perplexe « hm, it's 4.0.2. Not the latest & greatest but i've been using it for 2 years and this would be the first time it miscompiles a 32-bit kernel out of tens of thousands of successful kernel bootups. ». Mais les échanges suivants avec d'autres interlocuteurs réussissent à persuader Ingo.

Je pense que le bogue gcc 4.0.2 a été rapporté mais je n'en ai pas trouvé les traces. C'est quand même un bel exploit de l'expertise Linus qui semble habitué aux bogues du compilateur.

mardi 2 octobre 2007

PHP, outil de torture pour développeur

Je travaille actuellement sur le projet Nuface pour INL. Environ cinq personnes travaillent ou ont travaillé sur ce projet écrit en PHP (3/4) et Python (1/4). Le qualité du code source laisse à désirer. Je fais de mon mieux pour simplifier le code tout en le rendant plus simple à comprendre, mais c'est une tâche difficile et pénible. J'ai alors cherché des outils pour m'aider dans ma tâche.

Analyse statique de code

J'ai cherché des outils d'analyse statique de code pour PHP. Malheureusement, je n'en ai trouvé que quatre : Pixy, SWAAT, php-sat et PHP String Analyzer (phpsa). Les deux premiers sont dédiés à la sécurité, ce qui ne m'intéresse pas (injection SQL et XSS). phpsa est un drôle de projet dont je n'ai pas compris l'intérêt, et j'ai donc finalement testé php-sat. L'installation est longue car il n'existe aucun paquet Ubuntu Feisty, ni pour php-sat, ni pour les nombreuses dépendances. Ordre dans lequel il faut les installer (de mémoire) :

  • aterm (2.5)
  • sdf2 (2.4)
  • strategoxt (0.17)
  • php-front (0.1pre401)
  • php-sat (0.1pre344)

Une fois que j'ai terminé de tout compiler et installer, j'obtiens ce joli message :

$ php-sat -i index.php
[ php-sat | error ] invalid box:
        KnownString{SafetyLevel([KnownString])}

Je ne comprend rien à ce message d'erreur cryptique que j'obtiens qu'importe le fichier passé en argument. Ayant passé deux heures à tout installer, je suis plutôt déçu et décide d'abandonner. La bonne pratique serait de contacter Eric Bouwers, l'auteur de php-sat pour lui rapporter mon problème, mais je ne l'ai pas (encore) fait.

Note : L'outil SWAAT a l'air d'être un bon outil pour trouver rapidement des failles de sécurité dans vos applications.

Eclipse et PDT

Un ami — feth — m'a montré les fonctions de refactoring de l'environnement de développement Eclipse. Bluffé par la facilité de renommer une variable ou créer une sous-fonction, je me suis décidé à l'installer pour pouvoir l'utiliser sur du code PHP. Une petite recherche m'a guidé vers PHPEclipse et PDT. Le premier n'est plus développé en faveur du second qui est sponsorisé par Zend, entreprise à l'origine du langage PHP. Zend emploie Yossi Leon pour travailler sur ce projet à plein temps.

J'ai d'abord installé Eclipse via Ubuntu : c'est la version 3.2 qui est disponible. Or quand j'ai voulu ajouter le greffon PDT, j'apprend qu'il faut la version 3.3 d'Eclipse. D'ailleurs, je réalise au passage que PDT est sorti en version 1.0 le 18 septembre dernier, c'est donc tout frais !

J'ai alors installé Eclipse en téléchargeant Eclipse Classic (eclipse-SDK-3.3.1-linux-gtk.tar.gz). Pour importer le projet Nuface, j'ai d'abord installé Subclipse, le greffon Subversion.

Finalement, tout s'est bien emboîté : Nuface s'ouvre à merveille dans Eclipse additionné de Subclipse et PDT. Eclipse affiche directement des avertissements sur la documentation écrite en docbook (format basé sur XML). En corrigeant l'erreur sur le charset incorrect, il arrive ensuite à détecter les erreurs de DTD (docbook). Chapeau !

Enfin, quand je cherche la fonction de refactoring « créer une sous-fonctions » — après deux heures d'installation — je découvre que la fonction n'existe tout simplement pas ! Seule la fonction pour renommer une variable ou une fonction est disponible dans PDT 1.0. Or j'avais installé Eclipse uniquement pour cette fonction... grosse déception.

Plantage PHP

Pour finir en beauté, aujourd'hui j'ai trouvé un bug critique dans l'interpréteur PHP. J'ai mis trois heures à isoler un bug ridicule qui fait planter le processus Apache (child pid 12946 exit signal Segmentation fault (11)). Les détails du plantage sont disponibles dans mon rapport de bug (#42817). Je pense que vu la popularité de PHP, ce bug peut être une faille de sécurité.

PHP est à vomir

Étant un programmeur de longue date, je suis très déçu de PHP. Langage crée en 1994, sa dernière version majeure (PHP5) élimine quelques défauts du modèle objet et comble certaines lacunes (ex: gestion des exceptions). Mais PHP 5.2 ne supporte pas Unicode et est vraiment trop laxiste selon moi. En particulier, accéder à une variable indéfinie ou à une clé inexistante d'un tableau devrait être proscrit. Pour que la programmation en PHP soit supportable, il faut travailler dans un niveau d'avertissement maximum : « error_reporint(E_ALL); ». Je vous conseille de jetter un Å“il au fichier debug.php de Nuface, il contient en particulier un gestionnaire d'erreur écrit PHP (nuface_error_handler).

Bon, moi je vais retourner coder des tests unitaires pour SimpleTest... Pour information, la version CVS offre une méthode expectException(), nécessaire pour pouvoir tester les exceptions.

Deux blogs pour se changer les idées

Mon premier est un junkie

Je suis un peu tombé sur ce blog par hasard : webjunkie.fr. Un chouette habillage graphique sur le ton de la déconnade, j'aime.

Ma sélection d'animations :

Ma sélection de musiques :

Et puis les autres (ce blog est un bouillon, on y trouve de tout) :

Mon second est bien bien bien

Le blog 404 est mort, vive le blog bienbienbien.net !

Ma sélection d'articles :

Compilateurs C libres

Suite à l'annonce de l'intégration de PCC dans NetBSD, je me suis intéressé aux compilateurs libres qui existent. En chatouillant Google et Wikipédia, j'ai obtenu cette petite liste :

  • GCC (GNU Compiler Collection)
    • Licence GNU GPLv2 avec un poil de LGPL
    • Projet lancé en 1985 par Richard Stallman et développé aujourd'hui en partie par RedHat. Fait parti du projet GNU.
    • Intégré dans de nombreux IDE. Exemples : DJGPP (MS-DOS) ; MinGW, Cygwin et Dev-C++ (Windows) ; etc.
  • PCC (Portable C Compiler)
    • Licence BSD
    • Projet lancé dans la fin des années 1970 par Stephen C. Johnson
    • Plus d'informations dans les commentaires de l'annonce linuxfr et des critiques de gcc
  • Watcom
    • Logiciel libéré en 2003 : OpenWatcom (licence)
    • Logiciel propriétaire ayant pour origine un compilateur Fortran écrit en 1965 par des étudiants de l'Université de Waterloo (Canada). Une version optimisée pour le C et pour PC est apparue en 1988 (Watcom C 6.0). Lire l'histoire complète.
    • Compilateurs de tous les jeux MS-Dos de 1993 à 1996 (DOOM, Descent et Duke Nukem 3D, etc.)
  • LLC (Local C Compiler)
    • Développé par Chris Fraser et David Hanson
    • Projet de l'Université de Princeton
    • Logiciel libre ?
    • Intégré dans l'IDE « Lcc-win32 »
  • SDCC (Small Device C Compiler)
    • Licence GNU GPL
    • Projet lancé Sandeep Dutta et ouvert aux contributions (Sourceforge) en 1999
  • TCC (Tiny C Compiler)
  • TenDRA
    • Projet initié par la DERA (agence du département de la Défense du Royaume-Uni) au milieu des années 1990
    • Licence BSD

Cette liste est sûrement incomplète, mais je pense que les compilateurs libres les plus populaires y sont listés. Consultez également la liste des compilateurs C de Wikipédia anglophone. Il existe de nombreux compilateurs commerciaux dont certains sont gratuits sous certaines conditions : Microsoft (Visual Studio), Intel (ICC), Borland (Turbo C, C++ Builder), ...

Pour information, un programme C exécuté dans LLVM 2.0 (Low Level Virtual Machine) est 20% plus rapide que lorsqu'il est exécuté avec GCC 4.2. On peut donc supposer que LLVM a accès à des algorithmes d'optimisation auxquels GCC n'a pas accès (sûrement parce que GCC est statique alors que LLVM est dynamique).

Pour les plus passionnés, un lien utile : Linux Assembly.

Perso, je suis déçu que PyPy n'ai pas réussi à être plus rapide que CPython (l'implémentation de référence en C) car pourtant sa technologie était bien plus évoluée :-( Apparemment, PyPy est plus un laboratoire d'expérimentation qu'une implémentation optimisée de Python.