• Aucun résultat trouvé

2.2 Techniques d’injection de fautes pour cibler l’intergiciel

2.2.3 Techniques de corruption à l’API

La robustesse d’un système est une mesure de sa capacité à délivrer un service correct en présence d’entrées invalides et de conditions environnementales défavorables, ou « stressantes ». Le test de robustesse consiste à appliquer des données invalides à l’interface externe du système, puis à observer son comportement. En appliquant des tests de robustesse, on considère chaque interface d’un sous-système comme étant un canal de propagation d’erreur potentiel qui a été mis en place par les développeurs du système, et on cherche à évaluer le degré d’étanchéité de ces interfaces vis-à-vis des fautes qui peuvent se propager depuis d’autres sous-systèmes.

Le test de robustesse nécessite, pour sa mise en œuvre, que le système cible possède une interface de service explicite, qui peut alors être ciblée par l’injecteur de fautes. Ceci est difficile dans le cas d’un courtier CORBA, dans la mesure où, comme nous l’avons vu au §1.6.1, une grande partie de la fonctionnalité fournie par un courtier est implicite. En effet, la fonctionnalité qui est disponible via une interface explicite se résume aux points suivants :

• l’initialisation du courtier : certaines opérations permettent la prise en compte d’informations provenant de l’environnement d’exécution afin d’obtenir des ré- férences initiales vers certains services (processus d’amorce ou de bootstrap). C’est le cas en particulier de l’opération resolve_initial_references, qui peut par exemple analyser les arguments fournis sur la ligne de commande lors du lancement de l’application.

• gestion de l’adaptateur d’objets (POA), pour un objet qui joue un rôle de ser- veur. Les opérations explicites de l’interface du POA permettent à un servant de s’inscrire auprès de son adaptateur d’objets, et de contrôler son cycle de vie. • gestion de politiques : certaines opérations permettent le contrôle de change-

ments dynamiques dans les politiques utilisées par le courtier, telles que le modèle de concurrence, la politique de contrôle d’accès.

• la conversion de références objet vers une représentation textuelle, et l’opération inverse d’extraction d’une référence à partir d’une chaîne de caractères.

• des opérations utilitaires, permettant la création de certains types de données.

Les travaux rapportés dans [Pan et al. 2001] sur le test de robustesse de courtiers

CORBA ont ciblé environ 20 opérations dans cette interface publique. Ces opérations constituent une proportion relativement réduite de la fonctionnalité fournie par un courtier, et elles sont pour la plupart utilisées uniquement lors de l’initialisation du courtier. En effet, une grande partie de la fonctionnalité implémentée par un courtier est fournie de manière implicite, et ne résulte pas d’un appel explicite sur une interface publique. Considérons par exemple une invocation CORBAdans un programme écrit

en langage C++ :

result = theObject->theMethod("foo", 42);

La variable theObject est une instance d’une classe qui hérite de classes fournies par le courtier. Au cours de l’exécution de cette méthode, le courtier identifie le nœud sur lequel s’exécute l’objet theObject, et établit (ou réutilise lorsqu’elle existe déjà) une connexion réseau vers ce nœud. Il emballe les paramètres de l’appel dans un format standard (le CDR), et envoie les données sur le réseau. Il attend la réponse du serveur, et déballe la réponse dans la variable result. Si l’invocation a généré une exception

CORBA, le courtier lève une exception C++ correspondante.

Toute cette activité est transparente pour le programmeur d’application (ou opaque – il ne voit pas les détails), et l’appel est identique d’un point de vue syntaxique à une invocation normale de méthode sur un objet local, nonCORBA. La force deCORBA

provient justement de ce que tous les mécanismes sous-jacents ne soient plus gérés par le programmeur applicatif, mais par le courtier.

Cependant, cette transparence implique que la fonctionnalité fournie par le courtier n’est pas présentée aux clients par l’intermédiaire d’une interface explicite. On ne peut pas, par conséquent, appliquer la technique de test de robustesse à ces fonctionnalités de transparence.

Ces fonctionnalités implicites fournies par le courtier peuvent être catégorisées comme suit :

• l’interaction avec le langage de programmation du niveau applicatif : il faut exposer les interfaces spécifiées par la normeCORBAde manière compatible avec la projection langage. Il faut également fournir des méthodes d’emballage et de déballage pour tous les types CORBA, gérer la création et la destruction des objets, et implémenter les exceptions de manière compatible avec la projection langage.

• fonctionnalités orientées réseau : résolution des adresses des nœuds, établisse- ment de connexions réseau, envoi et réception d’informations depuis des ma- chines distantes ;

• gérer la concurrence en fonction de la politique demandée par l’application, en s’appuyant sur les primitives fournies par le système d’exploitation ;

• gestion de ressources : allouer et libérer des tampons mémoire, des pools de processus légers.

Déroulement d’une campagne

Une expérience d’injection de fautes utilisant cette approche de test de robustesse consiste à exécuter les étapes suivantes :

1. Générer une interface avec une combinaison de définitions de types et des signatures d’opération.

2. Générer une implantation correspondante pour le service, la charge applicative, et les composants d’observation.

3. Déclencher l’injecteur de fautes, qui invoque alors le service avec des paramètres corrompus.

4. Observer la réaction du système face à cette situation « stressante ».

Il convient de sélectionner les paramètres employés pour l’invocation du service afin de maximiser la couverture des chemins dans le code ciblé, c’est-à-dire dans le code du courtier, des souches et des squelettes. Cette même problématique de génération de paramètres afin de maximiser des critères de couverture se retrouve dans le domaine du test fonctionnel. Plusieurs techniques sont connues, parmi elles la génération sta- tistique d’entrées de test [Thévenod-Fosse et al. 1995].

En l’absence d’informations sémantiques sur les domaines d’entrée admis par les opérations dans l’interface, les types de corruption qui peuvent être appliqués aux paramètres dépendent uniquement de leur type IDL, ainsi que de la projection lan- gage. Il faut noter que la majorité des types OMG IDL sont « incorruptibles », dans le sens où toutes les formes binaires qui peuvent être stockées en mémoire sont une représentation valide d’un élément du type. D’autres types, par contre, ont un domaine d’entrée plus restreint, et peuvent donc faire l’objet de corruptions. C’est le cas par exemple des références objets.