Sous-sections
Nous présentons d'abord des résultats généraux sur
l'analyse de programmes (module 6.2.1) avant de détailler
un certain nombre de recherches portant sur des analyses
particulières : la vérification de politiques de
sécurité (module 6.2.3), la preuve de
protocoles cryptographiques à l'aide de l'interprétation
abstraite (module 6.2.2) et
l'analyse de programmes
Prolog (module 6.2.4). Nous décrivons
ensuite nos travaux récents sur le débogage dynamique (module
6.2.5), la
sémantique et la génération de traces (modules 6.2.6 et 6.2.7) et leur application
au problème de la détection d'intrusion. Nous concluons avec
la présentation de nos activités en matière de génération de
jeux de test (module 6.2.9).
Construction systématique d'analyses statiques
Participants : Thomas Jensen,
Florimond Ployette, Frédéric Besson.
Mots clés : analyse de programme,
sémantique, interprétation abstraite, calcul de point fixe
.
Résumé :
Une analyse statique se divise souvent en deux phases.
La première phase produit à partir d'un programme un
système de contraintes dont la solution représente
l'information recherchée. Une deuxième phase calcule la
solution. La phase de résolution est indépendante du
programme analysé. Une méthode de résolution de systèmes
d'équations peut donc s'appliquer à des systèmes provenant
de différentes analyses ce qui suggère la possibilité de
réaliser un « moteur d'analyse ».
De nombreux travaux ont été effectués sur les fondements
de l'analyse sémantique, la correction et la précision des
analyseurs. On peut regretter cependant le peu d'attention
accordé jusqu'à présent à la conception d'outils d'analyse
génériques. Nous travaillons à la mise au point d'un solveur
de point fixe générique qui peut servir de base pour le
développement de nouveaux analyseurs (cf module 5). Une analyse produit
souvent comme résultat un système d'(in)équations sur un
domaine de propriétés dont la solution représente
l'information recherchée. La phase de résolution est
indépendante du programme analysé ; une méthode de résolution
de systèmes d'équations peut donc s'appliquer à des systèmes
provenant de différentes analyses. Cette observation suggère
la possibilité de réaliser un « moteur d'analyse », c'est à
dire un outil générique de résolution de systèmes
d'(in)équations qui peut servir de base pour implanter des
analyseurs. La généricité évoquée ici est relative aux
propriétés et aux domaines abstraits considérés. La solution
d'un tel système peut être calculée de faÇon itérative comme
le plus petit point fixe d'un opérateur défini par le système
d'équations à résoudre.
Nous avons appliqué cette technique à la vérification de
propriétés de sûreté de programmes synchrones écrits dans le
langage SIGNAL. En collaboration avec
J.-P. Talpin du projet EPATR, nous avons
conÇu une analyse de flot de données de programmes Signal
pour déterminer des relations entre les variables d'un
programme [BJT99]. L'analyse prend en entrée un programme
SIGNAL et rend un système de contraintes dont
les solutions décrivent l'exécution du programme. La
résolution de ce système de contraintes se fait par itération
de point fixe dans le domaine des polyèdres convexes. Ce
domaine contenant des chaînes infinies, nous avons été amenés
à définir des opérateurs d'élargissement pour garantir la
convergence du calcul de point fixe. Ces opérateurs se basent
sur une représentation de polyèdres différente de celle
utilisée dans la définition des opérateurs existants et
permettent de faire des approximations plus fines dans
certains cas ; ils complètent donc la collection
d'élargissements dont on dispose pour faire converger un
calcul de point fixe. Pour étudier la faisabilité de ce
modèle biphasé de la conception d'analyseurs, nous avons
réalisé l'analyseur SIGNAL en utilisant
l'environnement de programmation SmartTool (cf module
8.1)
pour générer le système de contraintes et le solveur Reqs (cf
module 5) pour le
résoudre.
Preuve et interprétation abstraite pour la vérification de
protocoles cryptographiques
Participants : Valérie Viet Triem
Tong, Thomas Genet.
Mots clés : preuve assistée,
interprétation abstraite, vérification, protocoles
cryptographiques .
Résumé :
Nous nous intéressons à l'utilisation des
interprétations abstraites pour la simplification de
certaines preuves réalisées à l'aide de démonstrateurs ou
d'assistants de preuve. En simplifiant ces preuves, nous
souhaitons améliorer l'automatisation de ces outils et
ainsi faciliter leur utilisation pour la vérification de
systèmes ou de logiciels ayant un niveau de complexité
intermédiaire. Parmi les cibles privilégiées de ce type de
vérification on trouve les protocoles cryptographiques,
dont les propriétés essentielles peuvent être partiellement
vérifiées par interprétation abstraite, mais dont la
vérification complète nécessite des mécanismes de preuves
plus puissants comme, par exemple, l'induction.
Qu'il s'agisse de connexion à une machine distante,
d'envoi d'informations confidentielles ou de transaction
bancaire, toutes ces situations nécessitent l'utilisation de
protocoles cryptographiques. Par des techniques de
cryptage, ces protocoles sont chargés d'assurer la sécurité
des informations échangées : essentiellement la
confidentialité et l'authenticité.
Il existe de nombreux protocoles cryptographiques mais la
fiabilité de beaucoup d'entre-eux a été invalidée par la
découverte de failles de sécurité importantes. De telles
failles peuvent être exploitées par un acteur malveillant
pour attaquer le protocole et, par exemple, obtenir
une information qui ne lui était pas destinée (annule la
confidentialité) ou se faire passer pour l'auteur d'un
message qu'il n'a pas écrit (annule l'authenticité).
L'attaque consiste, en général, en une séquence de messages
contrefaits ou réutilisant des messages capturés.
La vérification des protocoles cryptographiques nécessite
des techniques d'analyse particulièrement fines et
sophistiquées. En effet, à cause des enjeux notamment dans le
secteur bancaire, la sécurité de ces protocoles doit être
garantie contre tout type d'attaque et ceci dans un contexte
d'utilisation très général : pas de limites sur le nombre de
sessions ou sur le nombre d'acteurs exécutant le protocole en
même temps. Actuellement, la principale technique de
vérification des protocoles cryptographiques - le
model-checking - permet de découvrir certaines attaques, mais
ne permet pas de garantir un protocole contre leur existence.
D'autres techniques, faisant appel à la démonstration
automatique, donnent une garantie beaucoup plus forte en
démontrant formellement les propriétés de sécurité d'un
protocole dans un contexte d'utilisation général. Cependant,
les preuves demeurent fastidieuses et deviennent
impraticables sur des protocoles complexes.
Nous nous proposons de concilier la démonstration
automatique, qui permet une vérification profonde et précise
des protocoles cryptographiques, avec des techniques d'
interprétation abstraite afin de simplifier
certaines preuves. En effet, alors que certaines propriétés
nécessitent une technique de démonstration puissante,
d'autres peuvent être démontrées en approchant, par
interprétation abstraite, le problème et la propriété à
prouver. En ne considérant qu'une approximation du problème
et de la propriété, la démonstration peut, bien souvent, être
effectuée à l'aide de mécanismes de déduction basiques. De
plus, il est important d'intégrer la technique
d'interprétation abstraite au démonstrateur car il doit être
en mesure de s'appuyer sur les approximations pour mener à
bien ses preuves. En particulier, dans une même preuve, le
démonstrateur doit pouvoir combiner un raisonnement par
approximation avec un raisonnement logique ou inductif.
En utilisant des techniques de réécriture et d'automates
d'arbres, nous avons proposé une méthode de vérification de
protocoles cryptographiques [26]
utilisant une forme d'interprétation abstraite et un
mécanisme de déduction simple sur des contraintes
ensemblistes. A partir d'une description d'un protocole sous
la forme d'un système de réécriture et d'un ensemble de
requêtes possibles décrit par un automate d'arbre, nous
calculons automatiquement une sur-approximation du
comportement du protocole sur les requêtes possibles. Cette
approximation est obtenue sous la forme d'un automate d'arbre
reconnaissant un sur-ensemble des messages pouvant être
échangés entre un nombre quelconque d'acteurs, pendant un
nombre quelconque de sessions. A partir de ce sur-ensemble,
pour montrer que le protocole a les propriétés souhaitées, il
est suffisant de montrer que l'intersection entre l'ensemble
approximation et un ensemble contenant tous les cas d'erreurs
est vide. Ce travail a été réalisé en collaboration avec
Francis Klay de France Telecom R&D.
Actuellement, nous étudions la combinaison de certaines
formes d'interprétation abstraite avec des mécanismes de
déduction plus sophistiqués afin de les intégrer dans des
démonstrateurs à base de réécriture comme CiME ou dans des
assistants de preuve comme Coq, Isabelle/HOL ou PVS. A terme,
ceci doit permettre de vérifier des propriétés plus fines sur
des protocoles cryptographiques plus complexes comme le
protocole SET de VISA et MASTERCARD.
Analyse de propriétés de sécurité
Participants : Frédéric Besson,
Marc Eluard, Thomas Jensen, Fausto Spoto.
Mots clés : téléchargement, sécurité,
chargement dynamique, Java .
Résumé :
Nous avons proposé un cadre de définition de politiques
de sécurité reposant sur une logique temporelle linéaire à
deux niveaux. Nous avons montré que ce modèle est
suffisamment expressif pour décrire une variété de
politiques de sécurité communes et nous avons proposé une
méthode de « model checking » automatique pour la
vérification de propriétés de cette logique. Ensuite nous
avons montré comment la politique de sécurité du dernier
environnement de développement de Java (JDK
1.2) peut s'exprimer dans notre logique.
La sécurité d'un système informatique dépend en général
d'un ensemble de contrÔles de nature très variée
(vérification formelle, analyse statique, analyse dynamique,
gestionnaire de sécurité, protocole d'authentification,
contrÔles d'accès, etc.). Une difficulté majeure dans ce
domaine est de pouvoir déterminer la politique globale de
sécurité qui est assurée par cette association de contrÔles
spécifiques. L'évolution récente vers des langages de
programmation qui offrent des fonctions de sécurité propres
(comme Java ou Telescript) permet d'espérer des progrès en
matière de vérification formelle de politiques de sécurité.
Beaucoup reste cependant à accomplir comme le montrent les
discussions récurrentes sur la sécurité des différentes
versions d'environnements de Java. Dans ce contexte, on peut
décomposer le problème en trois tâches complémentaires :
- La définition formelle de la sémantique du langage de
programmation
(avec ses fonctions de
sécurité).
- La spécification formelle de la politique de sécurité
qui doit être assurée.
- La vérification que la politique
est effectivement assurée par
un système donné (décrit dans le langage
).
Nous avons étudié ces trois aspects en fournissant un
cadre général de spécification de politiques de sécurité et
en décrivant son instanciation au langage Java et à son
environnement de développement JDK 1.2
[7,14].
Notre cadre de définition de politiques de sécurité repose
sur un modèle abstrait de programmes comportant des
opérations génériques de contrÔle de sécurité. Leur
sémantique opérationnelle est définie comme un système de
transitions sur des états constitués de piles de contrÔle.
Les politiques de sécurité sont exprimées dans une logique
temporelle linéaire à deux niveaux (les objets étant définis
par des suites de piles). Nous avons montré que ce formalisme
est suffisamment expressif pour décrire une variété de
politiques de sécurité communes (séparation des devoirs ou «
segregation of duty », protection de ressources, bac à sable)
avec une attention particulière à la politique de sécurité du
dernier environnement de développement de Java
(JDK 1.2) et des propriétés de sécurité
d'applications pour les cartes à puce multi-applicatives
programmées en Java Card.
Dans une communication précédente, nous avons proposé une
méthode automatique (et complète) de vérification de
propriétés pour cette logique [7].
Cette méthode de vérification était soumise à certaines
restrictions (concernant notamment la récursivité mutuelle).
Ces restrictions ont pu être levées grâce à une nouvelle
technique de « model checking » basée sur une représentation
de formules temporelles par des automates finis. Nous
étudions maintenant comment modulariser cette méthode de
vérification afin de mieux analyser des applications
utilisant le chargement dynamique de classes en Java.
Le langage Java Card définit un modèle de sécurité
légèrement différent du modèle de Java. Par défaut, les
applettes installées sur une carte Java Card sont séparées
par un pare-feu et ne peuvent communiquer entre elles. La
communication entre applettes se fait par la création et les
échanges des objets partagés. Nous nous sommes attaqués au
problème de vérifier que les accès aux données rendus
possibles par des objets partagés ne divulguent pas des
informations confidentielles. Pour calculer une approximation
sûre des accès effectués pendant l'exécution, nous nous
appuyons sur des analyses statiques de flot de données et de
contrÔle. Ces analyses sont des analyses de byte code Java,
adaptées pour prendre en compte les spécificités du pare-feu
Java Card. Ces travaux se sont déroulés dans le cadre de
l'action VIP du GIE Bull-Inria Dyade et vont
continuer dans le projet européen IST SECSAFE
(cf module 7.4).
Participant : Olivier
Ridoux.
Mots clés : analyse statique,
lambda-Prolog, type .
Résumé :
Nous étudions l'analyse statique de
Prolog par la
technique de la compilation abstraite où un
programme source est traduit en un autre programme dont les
résultats sont interprétés comme le résultat de l'analyse
du programme source. L'extension à
Prolog des
techniques éprouvées dans le cas de Prolog nécessite entre
autres le traitement des
-termes simplement typés.
Nous avons choisi pour l'analyse statique de
Prolog la technique
de la compilation abstraite où un programme source
est traduit en un autre programme dont les résultats sont
interprétés comme le résultat de l'analyse du programme
source. Cette méthode a déjà été employée pour Prolog et
l'appliquer à
Prolog nécessite des extensions dans
trois directions :
- La prise en compte de la structure des formules de
Prolog (formules de Harrop au lieu de
formules de Horn).
- Le traitement de la structure des termes de
Prolog (
-termes simplement
typés au lieu de termes de premier ordre).
- L'application à l'analyse de propriétés nouvelles dont
l'utilisation permettrait d'optimiser l'implantation du
langage (état de
-normalisation, combinateurs,
etc.).
Le domaine de calcul choisi pour les programmes abstraits
est celui des fonctions booléennes. Dans ce cadre, nous avons
développé une généralisation du domaine des fonctions
booléennes positives à des fonctions sur les types,
elles-mêmes positives en un sens qu'il a fallu redéfinir. Ce
domaine a la caractéristique d'être infini même quand on fixe
le nombre de variables (au contraire du domaine des fonctions
booléennes positives). L'argument de terminaison de l'analyse
est donc plus subtil que pour l'abstraction par les fonctions
booléennes. Il repose sur l'hypothèse que le programme
analysé est bien typé [18]. Ainsi, si la propriété analysée est
l'absence de variable existentielle libre (la clÔture, ou
groundness en anglais), l'expression (list (pair
vrai faux)) sera vraie d'une liste dont le squelette ne
comporte pas de variable, dont aucun élément n'est une
variable, et dont tous les éléments comportent un «champ
gauche» sans variable libre et un «champ droit» qui peut en
comporter.
Ce travail a été réalisé en collaboration avec Patrice
Boizumault de l'École des Mines de Nantes.
Participants : Mireille Ducassé,
Erwan Jahier.
Mots clés : débogage, analyse
dynamique, traceur abstrait, Prolog, Mercury, langage C
.
Résumé :
Nous avons développé des outils qui analysent les traces
d'exécution de programmes générées par un traceur bas
niveau. Les programmeurs peuvent spécifier de manière
précise ce qu'ils veulent observer du comportement du
programme à l'aide de requêtes exprimées en Prolog. À
partir du langage de requêtes, nous avons bâti des analyses
qui fournissent des vues abstraites des exécutions selon
certains critères. Il a également été possible de mettre en
oeuvre à faible coût des traceurs abstraits pour des
langages de haut niveau. Les techniques de base exploitent
une trace dont le seul pré-requis est d'être séquentielle.
Nous avons montré cette généralité en appliquant ces
principes aux langages Prolog, Mercury et C.
Nous avons développé des outils (cf. module 5) qui analysent les traces
d'exécution de programmes générées par un traceur bas niveau
(cf. module 3.3).
Le programmeur peut poser des questions sur les exécutions à
l'aide de Prolog et de quelques primitives dans une forme
concise en s'appuyant sur la logique et les mécanismes de
recherche du langage. Les programmeurs peuvent donc, à l'aide
de requêtes de haut niveau, spécifier de manière précise ce
qu'ils veulent observer du comportement du programme.
Le prototype initial, Opium [5],
était essentiellement dédié au débogage. Nous avons montré
avec Morphine comment il est possible d'utiliser un traceur
doté d'un module d'analyse de traces pour faire davantage que
du débogage. Par exemple, nous pouvons calculer un taux de
couverture de jeu de test afin d'en évaluer la qualité. Des
«moniteurs» qui surveillent le comportement des programmes
peuvent également être facilement mis en oeuvre. Ainsi, au
lieu de fabriquer des instrumentations ad hoc comme
c'est le cas actuellement pour de tels outils, on peut
utiliser un environnement uniforme, ce qui permet une
synergie entre les outils. De plus, les instrumentations
ad hoc nécessitent de bien connaître le système à
instrumenter, que l'instrumentation se fasse au niveau bas ou
par transformation de source à source. Même si elle n'est pas
techniquement très difficile, cette tâche demande un effort
de programmation non négligeable. Dans notre cas,
l'instrumentation étant générique, elle est faite une fois
pour toutes et les analyses spécifiques peuvent être
relativement simples [13].
À partir du langage de requêtes, nous avons également bâti
des analyses qui fournissent des vues abstraites graphiques
d'exécutions Mercury (par exemple, arbres de preuves, graphes
de flot de contrÔle). Grâce à la flexibilité de nos
mécanismes, chacune de ces vues ne nécessite que quelques
lignes de Mercury. Les temps d'exécution sont
acceptables [29].
Nous démarrons un projet RNTL sur l'analyse de trace
d'exécutions de programmes sous contraintes (cf.
module 7.1).
Participants : Mireille Ducassé,
Erwan Jahier, Olivier Ridoux.
Mots clés : débogage, traceur, modèle
de traces, sémantique, continuation, Prolog .
Résumé :
Nous proposons une architecture formelle pour spécifier,
prototyper et valider des modèles de traces d'exécutions
Prolog. Cette architecture est basée sur une sémantique
opérationnelle par continuations. Les spécifications
formelles peuvent être exécutées par une transcription
directe en
Prolog. Cela permet d'expérimenter
différents modèles de traces et de les valider.
Le modèle de base pour tracer des exécutions Prolog est le
modèle de Byrd [Byr80]. De nombreux systèmes
l'utilisent, mais avec différentes interprétations ce qui
fait qu'ils génèrent des traces différentes. Ces divergences
ne sont certainement pas toutes intentionnelles, certaines
étant dues au fait que la spécification originale est
informelle.
Pour remédier à ce problème, nous proposons une
architecture formelle permettant de spécifier, de prototyper
et de valider des modèles de traces d'exécutions de Prolog.
Cette architecture est basée sur une sémantique
opérationnelle par continuation. Nous donnons une
spécification formelle du modèle de traces de Byrd et nous
montrons comment cette spécification peut être étendue pour
spécifier des modèles de traces plus riches. Nous montrons
également comment ces spécifications peuvent être exécutées
par une transcription directe en
Prolog ; nous
obtenons ainsi un méta-interprète Prolog qui calcule des
traces d'exécutions. Ce méta-interprète peut être utilisé
pour expérimenter différents modèles de traces et pour les
valider [27,28].
Participante : Mireille
Ducassé.
Mots clés : débogage, traceur,
transformation de programme, mesure de performance, Prolog
.
Résumé :
Les traces d'exécution ne sont pas disponibles
directement. Il faut des outils pour les générer, que l'on
appelle des traceurs. Les traceurs actuels modifient en
général en profondeur le compilateur, ce qui rend leur
portage problématique. La génération de traces d'exécution
par transformation source à source de programmes permet
d'éviter cet écueil. Un prototype a été réalisé pour
Prolog. Des mesures montrent que cette technique, bien que
donnant des performances moindres que des techniques de
plus bas niveau, peut être acceptable pour construire des
traceurs.
Les traces d'exécution ne sont pas disponibles
directement. Il faut des outils pour les générer, que l'on
appelle des traceurs. Les techniques d'implémentation de ces
traceurs sont dans l'ensemble mal étudiées. Cela nuit, bien
sûr, à leur implémentation, mais aussi à leur portage et à
leur maintenance.
Typiquement, les traceurs modifient en profondeur le
compilateur, les structures de données et les schémas
d'exécution. Les inconvénients de ce type de traceurs sont de
plusieurs ordres : tout d'abord, ils ne permettent
généralement pas de conserver les optimisations qui donnent
tout leur intérêt aux compilateurs ; ensuite,
l'information fournie à l'utilisateur est de trop bas niveau,
elle peut même se trouver significativement dégradée ;
enfin, ces traceurs ne sont pas portables d'un compilateur à
l'autre pour un même langage.
Nous étudions actuellement la génération de traces
d'exécution pour Prolog par transformation source à source de
programmes. Cette technique permet d'éviter les inconvénients
mentionnés ci-dessus mais il faut établir son efficacité.
Nous avons implémenté un prototype à cet effet. Les mesures
montrent que les performances des programmes tracés par
transformation de programmes sont au pire deux fois moins
bonnes que celles des programmes tracés par deux traceurs
opérationnels « bas niveau » [16]. Cette réduction de performance est
acceptable pour un traceur « standard » surtout lorsque l'on
considère le gain significatif en termes de portabilité.
Ce travail est mené en collaboration avec Jacques Noyé, de
l'École des Mines de Nantes.
Participants : Mireille Ducassé,
Jean-Philippe Pouzol.
Mots clés : sécurité, analyse d'audit,
intrusion, attaque .
Résumé :
Dans la continuité des travaux sur l'analyse de traces,
Lande démarre une activité autour de l'analyse d'audits
dans le cadre de la détection d'intrusion. La détection
d'intrusion est une analyse de l'activité d'un système
visant à rapporter à l'officier de sécurité toute
utilisation suspecte. D'une part, nous mettons en place une
plate-forme de mesure de performances d'un système de
détection d'intrusion existant, afin d'évaluer les
problèmes techniques et humains. D'autre part, nous
cherchons à concevoir un langage de haut niveau de
spécification de signatures d'attaques.
Dans la continuité des travaux sur l'analyse de traces,
Lande démarre une activité autour de l'analyse d'audits dans
le cadre de la détection d'intrusion.
Une stratégie classique pour sécuriser un système
informatique consiste à construire un bouclier protecteur
autour de lui. Les utilisateurs désirant accéder aux données
ou ressources du système doivent franchir des barrières de
sécurité que constituent, par exemple, des mécanismes
d'identification ou de cryptographie. Toutefois, le
développement des systèmes ouverts, ainsi que la
prolifération de machines hétérogènes connectées à des
réseaux, rendent complexe et peu praticable la mise en oeuvre
de mécanismes de sécurité totalement fiables.
Une défense possible face aux utilisations abusives d'un
système est la détection d'intrusion. La détection
d'intrusion est une analyse de l'activité du système visant à
rapporter à l'officier de sécurité toute utilisation
suspecte. Afin de procéder à cette analyse il convient
d'effectuer un audit, dit de sécurité, qui alimentera le
processus d'analyse. Cet audit fournit une séquence
d'événements qui s'apparente à une trace d'exécution de
programme.
Comme dans le débogage, les performances des analyses
effectuées par un IDS sont cruciales. Nous mettons en place
une plate-forme de mesure de performances, en utilisation
réelle, sur le réseau enseignement du département
informatique de l'INSA de Rennes. Nous utilisons un IDS qui
nous semble relativement efficace, ASAX [HCMM93], développé à l'université de Namur.
Les résultats préliminaires sont encourageants, la
performance moyenne de chaque machine n'est pas trop affectée
[19]. Ce travail se fait en collaboration avec
Véronique Abily, administrateur système du département
Informatique de l'INSA.
Nous cherchons par ailleurs à concevoir un langage de haut
niveau de spécification de signatures d'attaques. Les
signatures d'attaques sont les manifestations des attaques
dans les systèmes attaqués. S'il existe quelques bases
informelles d'attaques, nous n'avons pas connaissance d'un
corpus formel de signatures. Un prototype préliminaire est en
cours de réalisation [31].
Participants : Thomas Jensen,
Valérie-Anne Nicolas, Olivier Ridoux.
Mots clés : test en boîte noire, test
en boîte blanche, test structurel, contrainte, Casting, jeu
de test, suite de test .
Résumé :
Nous avons proposé une méthode de génération de suites
de test qui forme le noyau de l'outil Casting développé en
collaboration avec la société AQL. La
méthode est indépendante du format d'entrée, ce qui la rend
utilisable aussi bien dans le cas du test structurel que
fonctionnel. Les suites de test engendrées dépendent de
stratégies spécifiées par l'utilisateur, permettant ainsi
d'atteindre la souplesse d'utilisation exigée pour un usage
industriel.
Nous avons abordé le problème de la systématisation de la
génération de jeux de test en tentant d'abolir la dichotomie
« boîte noire/boîte blanche » (cf. module 3.4). Pour ce faire,
nous décomposons le processus de production des données de
test en trois étapes :
- L'acquisition des critères de test et la production des
hypothèses de test associées, à partir de différents
supports d'entrée.
- La décomposition des opérations en classes d'opérations
et la génération d'un graphe d'accessibilité
symbolique.
- La génération des jeux de test par parcours du graphe
d'accessibilité en assurant un critère de couverture
donné.
Les supports d'entrée peuvent être constitués de
spécifications formelles ou informelles, de programmes
sources ou de propriétés fournies directement par
l'utilisateur. Les opérations peuvent être des machines
abstraites dans le cas du langage B, des
schémas pour le langage Z, des programmes dans
le cas d'un langage de programmation, etc. Dans tous les cas,
les critères de test sont implantés par des stratégies de
test et se traduisent in fine par des hypothèses
d'uniformité et hypothèses de régularité. Ces hypothèses
permettent de préciser le sens (et les limites) des jeux de
test qui seront engendrés (cf. module 3.4). D'un point de vue
pratique, une stratégie de test correspond à un mode
d'extraction de contraintes à partir du texte source. Ces
contraintes caractérisent les jeux de données qui devront
être engendrés pour chaque opération du système. Le graphe
d'accessibilité symbolique indique l'ordre dans lequel les
opérations peuvent être appliquées pour satisfaire toutes les
contraintes. Dans le cas général en effet on ne peut faire
l'hypothèse qu'une opération soit toujours applicable :
selon l'état du système, il peut être nécessaire d'effectuer
plusieurs opérations intermédiaires avant de pouvoir
appliquer une opération donnée. La dernière phase consiste à
explorer ce graphe en résolvant les contraintes associées
pour générer les données de test effectives.
Ces travaux ont conduit au développement de l'outil d'aide
à la génération de jeux de test Casting
(cf. module
5). La version
actuelle de Casting prend en entrée des spécifications dans
la notation AMN de la méthode B
[Abr96] et fait appel à Ilog Solver
pour résoudre les
contraintes engendrées. Diverses démonstrations de ce
prototype ont été réalisées (notamment à la conférence
B). Nous travaillons maintenant à la
transposition de cette technique pour le test d'applications
C et C++ (test structurel) dans
le cadre du projet européen Two et pour le test
d'applications Cobol dans une collaboration industrielle sous
l'égide du programme régional ITR (cf. module
7.2).
Nous nous intéressons conjointement à la manière d'assurer
que des programmes sont conformes à des hypothèses de test.
Nous considérons dans ce but des schémas de programmes qui
peuvent être vus comme une manière de définir des classes de
fautes : identifier un programme de manière non ambiguë
dans une classe revient à assurer que toutes les fautes ayant
pour effet de produire une version de programme dans cette
classe seront détectées. À tout schéma de programme est
associé un jeu de test fini et robuste [30]
(c'est à dire permettant de distinguer deux fonctions
quelconques du schéma). Ce résultat permet de montrer
automatiquement qu'un programme satisfait une propriété
donnée (à condition que l'un et l'autre puissent être situés
dans notre hiérarchie de schémas). La propriété attendue est
définie comme une fonction et la propriété effective est
dérivée du programme par interprétation abstraite. Il s'agit
ensuite de déterminer le plus petit schéma de la hiérarchie
qui les contient toutes les deux. On sait alors que le jeu de
test associé à ce schéma est suffisant pour établir l'égalité
des deux fonctions. Ce jeu de test peut être concrétisé
(opération inverse de l'interprétation abstraite) pour être
soumis au programme [30].