• Aucun résultat trouvé

Fuites sp´ ecifiques aux programmes concurrents

Le fonctionnement des programmes concurrents a beaucoup d’implications sur la non-interf´e- rence. En particulier, la possibilit´e de partager la m´emoire par plusieurs processus ouvre de nouveaux canaux d’informations. Comme on peut voir avec l’exemple suivant tir´e de [23], certaines fuites d’information sont directement li´ees au s´equencement des instructions du pro- gramme concurrent. Consid´erons les programmes c1 et c2 :

c1 : h := 0; l := h c2 : h := secret

o`u h et secret sont des variables priv´ees et l une variable publique. La commande c1 est

s´ecuritaire parce que la valeur finale de la variable l sera toujours 0. La commande c2 est

s´ecuritaire parce que les variables h et secret partagent le mˆeme niveau de s´ecurit´e. Toutefois, l’ex´ecution parall`ele de c1 et c2 n’est pas n´ecessairement s´ecuritaire. En effet, un ordonnanceur

peut planifier c2 entre l’ex´ecution des deux affectations h := 0 et l := h de c1. Comme r´esultat,

le contenu de secret est copi´e dans la variable l.

Dans le chapitre2, en parlant des canaux cach´es, nous avons d´efini les canaux de chronom´e- trage comme des situations o`u l’information est signal´ee `a travers l’instant exact o`u une action s’est produite au cours de l’ex´ecution du programme. On distingue deux formes diff´erentes de tels canaux, une observable `a l’externe et une autre observable en interne. Consid´erons le programme suivant : if h = 1 then sleep 100. Un attaquant avec un chronom`etre peut obtenir l’information sur la variable h `a partir de la dur´ee des ex´ecutions. C’est un exemple de canal cach´e de chronom´etrage observable `a l’externe. Ils ne sont pas propres `a la programmation

concurrente et nous ne nous attarderons pas sur ces types de canaux. Johan Agat [2] propose une approche par transformation du programme pour fermer les canaux de chronom´etrage externe.

Les canaux de chronom´etrage observables en interne quant `a eux, sont sp´ecifiques aux pro- grammes concurrents. La fuite survient lorsque le comportement au niveau du timing d’un programme peut d´ependre des donn´ees confidentielles. Dans ce cas, les ordonnanceurs peuvent refl´eter cette d´ependance sur les valeurs des variables publiques. Pour comprendre le fonction- nement d’une attaque par chronom´etrage interne, consid´erons la paire de commandes d1 et

d2 suivante :

d1 : if h then sleep(100) else skip; l := 1 d2 : sleep(50); l := 0

o`u les variables l et h sont respectivement publique et priv´ee. Les commandes d1 et d2 sont

intuitivement s´ecuritaires si on les analyse s´epar´ement, car la variable l sera toujours ´egale `a 1 `a la fin de l’ex´ecution de d1, et ´egale `a 0 la fin de l’ex´ecution de d2. Toutefois, lorsque d1 et

d2 sont ex´ecut´es en parall`ele, la s´ecurit´e de l’ensemble n’est plus garantie. En effet, plusieurs

ordonnancements raisonnables des deux commandes sont susceptibles de provoquer la fuite de l’information sur le contenu de h via la variable publique l. Par exemple, en supposant que d1 s’ex´ecute en premier, si h est diff´erent de 0 (ce qui correspond `a vrai), l’instruction

sleep(100) s’ex´ecute en 100 unit´es de temps. La commande d2 si elle est ex´ecut´ee entre-temps,

aura le temps de se terminer. Finalement l’affectation l := 1 dans d1 pourrait ˆetre la toute

derni`ere `a s’ex´ecuter, et la valeur finale de l pourra ˆetre 1. Si au contraire h est ´egal `a 0 (ce qui correspond `a faux), l’instruction sleep(100) ne s’ex´ecute pas. La commande d2 si elle est

ex´ecut´ee entre-temps, n’aura pas le temps de se terminer avant la fin de d1. L’affectation l := 0

pourrait ˆetre la toute derni`ere `a s’ex´ecuter, et la valeur finale de l pourra ˆetre 0. Ce type de fuite est connu dans la litt´erature sous l’appellation fuite par timing observable en interne. Le flot d’information dans ce cas est une combinaison du timing et du flot potentiellement probabilistique (dans le cas o`u l’ordonnanceur fait des choix non d´eterministes). La possibilit´e de cr´eer dynamiquement des processus entraˆıne ´egalement des possibilit´es similaires. Par exemple, une codification directe de l’exemple pr´ec´edent donne ceci :

fork (skip; skip; l := 0); if h ≥ k then

skip; skip else skip; l := 1

o`u la commande fork(c) cr´ee dynamiquement un nouveau processus qui ex´ecute la commande c.

Les fuites par timing interne sont particuli`erement dangereuses. Au contraire du timing ex- terne, l’attaquant n’a pas besoin d’observer les dur´ees d’ex´ecution du programme. De plus, ces fuites peuvent ˆetre grandement amplifi´ees `a l’int´erieur d’une boucle comme on peut voir sur cet autre exemple tir´e de [22] :

p := 0;

while n ≥ 0 do k := 2n − 1;

fork (skip; skip; l := 0); if h ≥ k then skip; skip else skip; l := 1; if l = 1 then h := h − k; p := p + k else skip; n := n − 1

o`u k, l, n, et p sont des variables publiques et h contient une valeur enti`ere secr`ete de longueur n bits. On suppose que ce programme s’ex´ecute en pr´esence d’un ordonnanceur de type Round- robin, c’est-`a-dire qui attribue des tranches de temps `a chaque processus en proportion ´egale, sans accorder de priorit´e aux processus.

On peut voir qu’`a chaque tour de boucle, un nouveau processus est cr´e´e et ex´ecute la com- mande {skip; skip; l := 0}. Par la suite, si la condition (h ≥ k) est vraie, alors le processus nouvellement cr´e´e se termine avant l’ex´ecution de l’instruction l := 1, ce qui force le pro- gramme `a entrer dans la branche du if pour ex´ecuter les deux affectations h := h − k et p := p + k. Par contre, si la condition (h ≥ k) est fausse, l’affectation l := 1 s’ex´ecute avant la fin du nouveau processus. En cons´equence, l vaut 0 au moment du test (if l = 1), et les deux affectations h := h − k et p := p + k n’ont plus lieu. Finalement, `a chaque it´eration de la boucle, un bit de h est r´ev´el´e et la valeur enti`ere de h est transmise `a p au bout de n tours de boucle.

Les deux mod`eles que nous allons pr´esenter proposent entre autres des solutions au probl`eme de fuites par timing observable `a l’interne.