• Aucun résultat trouvé

Modélisation d’un appel à free

Dans le document Analyse des pointeurs pour le langage C (Page 120-123)

5.2 Modélisation de l’allocation dynamique

5.2.3 Modélisation d’un appel à free

Après la modélisation du tas et des appels à malloc, nous nous intéressons naturellement à l’intrinsèque free. Cette fonction permet de libérer l’espace mémoire alloué précédemment par la fonction malloc. Elle prend comme argument un pointeur qui doit obligatoirement pointer vers une zone allouée via malloc. Prenons le programme 5.1 comme exemple.

int main() {

int *pi, *qi;

pi = (int *) malloc(sizeof(int)); qi = pi;

free(pi); // S

return 0; }

Prog 5.1 – Exemple de libération de mémoire

FIGURE5.1 – Graphe des arcs points-to avant

l’appel à free

4. Pour être strict, cette valeur de retour doit être implantée, mais elle pourrait être contrôlée par une propriété de PIPS.

5.2. Modélisation de l’allocation dynamique 95

En termes de graphe points-to, au niveau du point programme S avant l’appel à free, les arcs entre pointeurs sont représentés par le graphe 5.1.

L’effet de la fonction free sur les arcs points-to est de modifier la valeur du pointeur passé en argument sans l’écrire. La norme C spécifie que le pointeur ne pointe plus vers une zone mémoire valide et, par conséquent, une destination indeterminate doit lui être affectée implicitement. Ceci implique aussi qu’il ne peut plus être déréférencé.

Une autre conséquence à prendre en compte concerne les pointeurs en alias avec l’argument de free. Une fois la zone mémoire libérée, ces alias sont eux aussi réinitialisés et ne peuvent plus être déréférencés. Réinitialiser veut dire ici que les pointeurs se retrouvent assimilés à des poin- teurs déclarés pour la première fois ; il faut les faire pointer vers l’élément du treillis représentant la valeur indeterminate de la norme, undefined.

Le résultat de l’appel à free sur l’état des arcs points-to est montré sur le programme 5.2.

5.2.3.1 Les équations de calcul d’arcs points-to après un free

Pour pouvoir supprimer les arcs vers la zone libérée après l’appel à free, nous devons établir des équations spécifiques pour ce cas particulier. Prenons en compte les équations précédem- ment définies qui permettent d’évaluer les adresses correspondantes à une expression e dans un contexte points-to In.

(L,In0) = eta(e,In) (5.1)

(R2,In0) = etv(e,In) (5.2)

L’ensemble R2doit être restreint parce que tous les chemins constants ne peuvent pas faire l’objet

d’un free : seules certaines adresses du tas et la valeur NULL sont autorisées par la norme. On calcule donc un nouvel ensemble R, où ne peuvent apparaître que NULL, les éléments du tas, les éléments du contexte formel puisqu’on n’en connaît pas l’allocation, et toutes les valeurs abstraites qui contiennent des éléments du tas.

R1= {cp ∈ R2|cp ∩ (HEAP ∪ FORMAL ∪ {anywhere}) 6= ∅} (5.3)

Il faut noter que les éléments de R, sont eux-mêmes des ensembles, puisqu’il s’agit d’adresses abstraites et non d’adresses concrètes.

Si R1 ne contient qu’un élément et que cet élément est NULL, la norme spécifie que l’appel à

free n’a aucun effet sur l’exécution et que l’analyse est terminée. Sinon, il est possible de retirer l’élément NULLpour obtenir R final :

R = R1− {NULL} (5.4)

Si R est vide, une erreur d’exécution doit survenir. C’est par exemple le cas avec les séquences {int i, *p = &i; free(p);} et {int *p; free(p);}. Et l’analyse est terminée. Il reste donc à traiter les cas où les ensembles L et R sont non vides.

Comme l’appel à free(e) génère de nouveaux arcs points-to et en supprime d’autres, il est évident qu’il faut déterminer de nouvelles équations pour calculer les ensembles Gen1, Gen2,

Kill1 et Kill2 correspondants. Maintenant, toutes les sources qui appartiennent à l’ensemble L

doivent ou peuvent pointer vers l’emplacement abstrait undefined, d’où l’équation (5.5).

Gen1(L) = {(l, undef ined)|l ∈ L} (5.5)

Dans le cas où le cardinal de L vaut 1 et où son unique élément l est un atome du treillis, i.e. un singleton, l’unique arc généré est un arc exact qui traduit le fait que selon la norme la valeur du pointeur l, M [l], est indeterminate.

96 Chapitre 5. L’analyse intraprocédurale étendue

int main() {

int *pi, *qi;

// Points To:

// pi -> undefined , EXACT // qi -> undefined , EXACT

pi = (int *) malloc(sizeof(int));

// Points To: // pi -> *HEAP*_l_10 , MAY // qi -> undefined , MAY qi = pi; // Points To: // pi -> *HEAP*_l_10 , MAY // qi -> *HEAP*_l_10 , MAY free(pi); // Points To: // pi -> undefined , EXACT // qi -> undefined , MAY // qi -> *HEAP*_l_10 , MAY return 0; }

Prog 5.2 – Le résultat de l’analyse après l’appel à free

FIGURE5.2 – Graphe des arcs points-to après

l’appel à free

Comme l’appel à free a des effets sur les pointeurs en alias avec son argument, il est évident qu’il faut définir deux ensembles Gen1et Gen2. L’équation de Gen2est la suivante :

Gen2(R,In) = {(source, undef ined)|∃r ∈ R ∧ ∃(source, r) ∈ In} (5.6)

Elle définit les nouveaux arcs impliquant les pointeurs qui pointaient vers la même zone au niveau du tas. Les sources sont récupérées et elles pointent maintenant vers undefined. Dans le cas où R est un singleton et où son unique élément est atomique, les arcs générés sont exacts. Mais comme les emplacements alloués au niveau du tas ne sont pas atomiques leur approximations vaut MAY. En effet, une allocation au niveau du tas représente un ou plusieurs emplacements abstraits. Et Comme la modélisation ne comporte que des chemins d’accès constants abstraits, ceci n’est possible que lorsque l’objet de l’appel à free est un élément du contexte formel.

Comme nous avons défini les deux ensembles Gen, nous définissons maintenant les en- sembles Kill, c’est-à-dire les arcs points-to qui ne sont plus valables et dont les sources sont l’argument de la fonction free, ainsi que les pointeurs en alias avec cet argument. Le premier ensemble Kill correspond aux pointeurs définis par l’expression e. Il est défini par l’équation sui- vante :

Kill1(L,In) = {(l, r) ∈ In|l ∈ L ∧ |L| = 1 ∧ atomic(l)} (5.7)

où atomic est un prédicat sur CP qui est précisé sous-section 5.3.6 et qui signifie que l’adresse mémoire concrète est unique.

Quant aux alias de ces pointeurs, leur ensemble Kill est défini par l’équation (5.8).

5.2. Modélisation de l’allocation dynamique 97

Pour garantir la correction de l’analyse, tous les arcs pointant vers cette zone doivent être sup- primés.

L’équation finale qui nous permet de calculer l’ensemble Out après un appel à la fonction free est :

Out = (In − Kill1− Kill2) ∪Gen1∪ Gen2 (5.9)

La libération d’une zone peut générer des pointeurs de valeur indeterminate, appelés dangling pointers, et des fuites mémoire.

5.2.3.2 L’algorithme de traitement de l’appel à free

Après avoir défini les équations liées au traitement de l’appel à free, nous présentons ci- dessous l’algorithme qui permet de traiter cette instruction de libération de mémoire.

Fonction 15 : points_to_f ree

points_to_f ree : E × PT → PT points_to_f ree(e : E, In : PT ) (L,In0) = eta(e,In)

(R2,In0) = etv(e,In)

R1= {cp ∈ R2|cp ∩ (HEAP ∪ FORMAL ∪ {anywhere}) 6= ∅}

R = R1− {NULL}

Gen1(L) = {(l, undef ined)|l ∈ L}

Gen2(R,In) = {(source, undef ined)|∃r ∈ R ∧ ∃(source, r) ∈ In}

Kill1(L,In) = {(l, r) ∈ In|l ∈ L ∧ |L| = 1 ∧ atomic(l)}

Kill2(R,In) = {(l, r) ∈ In|r ∈ R ∧ |R| = 1 ∧ atomic(r)}

Out = (In − Kill1− Kill2) ∪Gen1∪ Gen2

Dans le document Analyse des pointeurs pour le langage C (Page 120-123)