Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

optimisation vs securite

18 réponses
Avatar
Thierry B.
Bonjour les codeurz...

En me promenant dans le grand ouèbe mondial, je suis tombé sur
cet article: http://lwn.net/Articles/278137/ qui évoque des
problèmes de potentiels buffer-overflow quand Gcc (et d'autres)
optimisent un peu trop sur certaines condition/tests.

Certains commentaires sont assez bons :)

tTh.


--
Une cinquième astuce pour pas passer pour une imbécile sur usenet : Ne
jamais hésiter à donner dans l'auto-dérision.
--{ SC, in fr.misc.divers }--

8 réponses

1 2
Avatar
Vincent Lefevre
Dans l'article <g005i1$1jgj$,
Marc Espie écrit:

In article <20080508232434$,
Vincent Lefevre <vincent+ wrote:

En fait, je ne vois pas le problème dont parle Marc concernant la mise
à 0 de la mémoire: il suffit de mettre le code dans une fonction avec
le pointeur passé à cette fonction, et de compiler cette fonction
séparément du reste: le compilateur ne peut pas deviner ce que va faire
le reste du programme et ne pourra donc pas optimiser (i.e. il sera
forcé de mettre à 0 la mémoire comme voulu).


Effectivement, tu passes completement a cote du vrai probleme.

Dans le temps, gcc n'optimisait pas le memset en question.

Qui te dit que bientot, il n'optimisera pas ce qui se passe entre
unites de compilation ? certains compilos le font bien...


C'est un bug. Un compilateur ne doit pas supposer plus de choses qu'on
lui fournit (à moins que ce soit documenté), et une telle optimisation
ne pourrait intervenir qu'au niveau du linkeur, et il faudrait que les
fichiers objets contiennent suffisamment d'information pour indiquer
que l'optimisation est valide (en effet, actuellement les fichiers
objets ne dépendent pas du langage et une optimisation valide en C ne
l'est pas forcément dans un autre langage).

Mais de toute façon, là, on sort complètement de la norme C, et le
programmeur doit un minimum se baser sur la doc de son compilateur
quand celui-ci outrepasse ce qu'il est censé faire (qui est de
compiler une unité en un fichier objet), et plus généralement de
son environnement (OS, etc.); les normes liées aux langages n'y
changeront rien. Un des problèmes connus concernant la sécurité est
d'ailleurs le swap, d'où la nécessité probablement d'utiliser mlock
sous système POSIX...

Un SIGSTOP qui intervient juste avant la mise à zéro de la mémoire
peut aussi être un problème...

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)


Avatar
espie
In article <20080513110728$,
Vincent Lefevre <vincent+ wrote:
Mais de toute façon, là, on sort complètement de la norme C, et le
programmeur doit un minimum se baser sur la doc de son compilateur
quand celui-ci outrepasse ce qu'il est censé faire (qui est de
compiler une unité en un fichier objet), et plus généralement de
son environnement (OS, etc.);


C'est bien ce que je reproche a gcc, de s'arreter *avant* ce niveau
de detail, et de se retrancher derriere la norme pour changer des
comportements qui ont une importance pratique.

les normes liées aux langages n'y
changeront rien. Un des problèmes connus concernant la sécurité est
d'ailleurs le swap, d'où la nécessité probablement d'utiliser mlock
sous système POSIX...


Une autre solution, preferable, consiste a chiffrer le swap. C'est plutot
facile, et ca n'a pas d'incidence reelle sur les perfs de la machine sur
une architecture moderne. On avait propose ca en option sur OpenBSD, et
puis c'est devenu systematique une fois qu'on a fait les mesures et qu'on
a constate que ca ne coutait vraiment rien...

Avatar
Vincent Lefevre
Dans l'article <g0c3ms$ufd$,
Marc Espie écrit:

In article <20080513110728$,
Vincent Lefevre <vincent+ wrote:
Mais de toute façon, là, on sort complètement de la norme C, et le
programmeur doit un minimum se baser sur la doc de son compilateur
quand celui-ci outrepasse ce qu'il est censé faire (qui est de
compiler une unité en un fichier objet), et plus généralement de
son environnement (OS, etc.);


C'est bien ce que je reproche a gcc, de s'arreter *avant* ce niveau
de detail, et de se retrancher derriere la norme pour changer des
comportements qui ont une importance pratique.


Il en a tout à fait le droit; il ne peut pas deviner ce que tu veux
comme optimisation. Et si tu ne veux aucune optimisation qui puisse
changer le comportement vis-à-vis de l'extérieur (en quelque sorte,
une généralisation du volatile), les performances seront énormément
dégradées.

Si tu veux qu'une fonction soit forcément exécutée, le meilleur moyen
est de la compiler séparément, de façon à ce que le compilo ne sache
pas si cette fonction a des effets de bord possibles. Là, gcc ne peut
plus se retrancher derrière la norme (faute d'avoir toutes les infos
et de deviner ce que veut l'utilisateur), ni le linkeur (qui est
complètement indépendant des langages, donc de la norme C).

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)


Avatar
espie
In article <20080513234026$,
Vincent Lefevre <vincent+ wrote:
Dans l'article <g0c3ms$ufd$,
Marc Espie écrit:

In article <20080513110728$,
Vincent Lefevre <vincent+ wrote:
Mais de toute façon, là, on sort complètement de la norme C, et le
programmeur doit un minimum se baser sur la doc de son compilateur
quand celui-ci outrepasse ce qu'il est censé faire (qui est de
compiler une unité en un fichier objet), et plus généralement de
son environnement (OS, etc.);


C'est bien ce que je reproche a gcc, de s'arreter *avant* ce niveau
de detail, et de se retrancher derriere la norme pour changer des
comportements qui ont une importance pratique.


Il en a tout à fait le droit; il ne peut pas deviner ce que tu veux
comme optimisation. Et si tu ne veux aucune optimisation qui puisse
changer le comportement vis-à-vis de l'extérieur (en quelque sorte,
une généralisation du volatile), les performances seront énormément
dégradées.


Dialogue de sourds...

Mais *TOUS* les cryptographes ont rale sur la suppression de ce truc,
ont dit que volatile n'etait clairement pas une solution, ont *demande*
un truc aux gens qui pondent gcc, avec juste comme resultat des discussions
de `language lawyer' sur `ah ouais, mais la, ca ca s'interprete pas comme
ca, et la norme ne permet rien'... ce qui n'est pas exactement ce qui
etait voulu comme resultat, hein.

Pour ce qui est de -fwrap, c'est un peu different.

La, le plus gros reproche qu'on peut faire a gcc, c'est que le systeme
de base est devenu beaucoup trop complexe, que personne ne sait vraiment
quelles optimisations servent a autre chose que bouffer du temps de compile,
et surtout que l'enorme majorite des combinaisons d'options de compilation
sont peu testees, et fortement bugguees...



Avatar
Vincent Lefevre
Dans l'article <g0du4c$12kk$,
Marc Espie écrit:

Mais *TOUS* les cryptographes ont rale sur la suppression de ce truc,
ont dit que volatile n'etait clairement pas une solution, ont *demande*
un truc aux gens qui pondent gcc, avec juste comme resultat des discussions
de `language lawyer' sur `ah ouais, mais la, ca ca s'interprete pas comme
ca, et la norme ne permet rien'... ce qui n'est pas exactement ce qui
etait voulu comme resultat, hein.


Est-ce qu'ils ont pondu une spécification claire (qui pourrait alors
être implémentée et activée avec telle ou telle option et serait de
l'implementation-defined)?

Parce qu'il me semble que gcc fait ce genre d'optimisation, à savoir
un stockage un mémoire qui disparaît dans le code généré, depuis très
longtemps. Le cas

void fct(void)
{
int a;
...
a = 0; /* pour éviter que la valeur précédente de a reste en mémoire */
return;
}

n'est pas très différent du memset, et je pense qu'à peu près tout
le monde ne s'étonne pas qu'un compilo puisse ignorer le "a = 0;".

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)

Avatar
espie
In article <20080515172357$,
Vincent Lefevre <vincent+ wrote:
Dans l'article <g0du4c$12kk$,
Marc Espie écrit:

Mais *TOUS* les cryptographes ont rale sur la suppression de ce truc,
ont dit que volatile n'etait clairement pas une solution, ont *demande*
un truc aux gens qui pondent gcc, avec juste comme resultat des discussions
de `language lawyer' sur `ah ouais, mais la, ca ca s'interprete pas comme
ca, et la norme ne permet rien'... ce qui n'est pas exactement ce qui
etait voulu comme resultat, hein.


Est-ce qu'ils ont pondu une spécification claire (qui pourrait alors
être implémentée et activée avec telle ou telle option et serait de
l'implementation-defined)?


Tu pourrais tres facilement pondre un builtin, style

__builtin_volatile(v);

au point ou le builtin est indique, la valeur de v est observee par un
element exterieur, et donc *doit* coincider avec ce qui est ecrit
dans le code...

Peut-etre reflechir a ce que ca veut dire pour un pointeur/tableau.
Dans le pire des cas, tu prevois un truc pour le scalaire, un truc pour
la valeur referencee par le scalaire...

Voire meme, si c'est un tableau de memset, tu pourrais ecrire:

for (i = 0; i < t; i++)
__builtin_volatile(t[i]);

ce qui explique au compilo que si, si, les valeurs du tableau doivent
bien avoir ete calculees.

Bref, ca me parait pas monstrueux a specifier. A implementer, sans doute
une autre paire de manches... Mais ca serait nettement plus utile que le
`tout ou rien' que constitue le volatile actuel.


Avatar
Vincent Lefevre
Dans l'article <g0i2dp$1kov$,
Marc Espie écrit:

Tu pourrais tres facilement pondre un builtin, style

__builtin_volatile(v);

au point ou le builtin est indique, la valeur de v est observee par
un element exterieur, et donc *doit* coincider avec ce qui est ecrit
dans le code...


OK, ça devient plus clair. À plus long terme, ce serait bien que
ce genre de chose soit dans la norme C, même si le mapping entre la
mémoire de la machine virtuelle et celle(s) de la machine physique
restera de l'implementation-defined (je pense notamment au swap,
mais surtout à un cache write back: une "bonne" implémentation
devra s'assurer que les données du cache sont recopiées en RAM).

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)

Avatar
espie
In article <20080515211954$,
Vincent Lefevre <vincent+ wrote:
Dans l'article <g0i2dp$1kov$,
OK, ça devient plus clair. À plus long terme, ce serait bien que
ce genre de chose soit dans la norme C, même si le mapping entre la
mémoire de la machine virtuelle et celle(s) de la machine physique
restera de l'implementation-defined (je pense notamment au swap,
mais surtout à un cache write back: une "bonne" implémentation
devra s'assurer que les données du cache sont recopiées en RAM).


Le volatile de base n'en fait pas plus. Lorsqu'il est utilise dans du
code de noyau, par exemple, c'est le noyau qui gere les problemes de
cache...

Pour le reste, c'est vrai qu'un defaut du C `recent' c'est que, paradoxalement,
tu es oblige d'ecrire plus de code bas niveau en assembleur... des que tu
veux jouer avec les interruptions a un point precis, par exemple...
Les fonctions/macros de style splx() sont de plus en plus delicates a
ecrire (confere la section 9 de n'importe quel bsd pour la description
de ces trucs).

Dans les versions recentes de gcc, le compilo va meme, dans certaines
conditions, te bouger du volatile asm(), ce qui est quand meme un peu
contraire a ce qu'on voudrait...

1 2