Failles pro­ces­seurs Meltdown & Spectre : un com­pro­mis qui a mal tour­né

Il fau­dra bien se faire une rai­son : nous de­vrons vivre sans doute quelques an­nées avec les deux failles dé­cou­vertes par dif­fé­rentes équipes de cher­cheurs. Ce n’est qu’à par­tir de 2019 qu’une nou­velle gé­né­ra­tion de pro­ces­seurs ap­por­te­ra un re­mède dé­fi­ni­ti

L'Informaticien - - SOMMAIRE - VINCENT HABCHI

L’in­for­ma­tique mo­derne se construit au­tour de deux pi­liers : la per­for­mance et la sé­cu­ri­té. Mal­heu­reu­se­ment, ces deux ob­jec­tifs sont par­fois contra­dic­toires : la sé­cu­ri­té né­ces­site des contrôles per­ma­nents que la per­for­mance exècre. Par­fois, les in­gé­nieurs réus­sissent à al­lier les deux sans trop de sou­ci ; par­fois, les exi­gences sont si in­com­pa­tibles que des com­pro­mis doivent être trou­vés, au prix d’un cer­tain risque, cons­cient ou non. Meltdown et Spectre, ces deux failles pré­sentes dans toutes les ité­ra­tions ré­centes des pro­ces­seurs In­tel – et, pour Spectre, éga­le­ment AMD et ARM – sont les re­je­tons d’un com­pro­mis qui a mal tour­né. De­puis que la course aux mé­gaHertz s’est ter­mi­née en cul- de- sac, les fon­deurs ont tout mi­sé sur l’exé­cu­tion pa­ral­lèle : les pro­ces­seurs ac­tuels ren­ferment plu­sieurs coeurs qui eux­mêmes exé­cutent plu­sieurs ins­truc­tions si­mul­ta­né­ment ( ar­chi­tec­ture « su­per­sca­laire » ) . Les res­sources d’exé­cu­tion du pro­ces­seur ( uni­té arith­mé­tique, uni­té d’ac­cès mé­moire, etc.) sont mu­tua­li­sées. Chaque ins­truc­tion as­sem­bleur est dé­co­dée, puis dé­com­po­sée en mi­cro- ins­truc­tions ( μOP). Celles- ci s’exé­cutent dès que les res­sources qu’elles né­ces­sitent de­viennent dis­po­nibles, pour­vu qu’elles ne dé­pendent pas d’une autre mi­cro- ins­truc­tion en cours. Cer­taines ins­truc­tions s’exé­cutent donc avant que d’autres, pour­tant an­té­rieures, ne soient ter­mi­nées. Pen­sez à la Poste : si tous les gens at­tendent pour une opé­ra­tion ban­caire et que vous ve­nez pour af­fran­chir une lettre, vous se­rez sor­ti bien avant le der­nier d’entre eux. Lorsque toutes les mi­cro- ins­truc­tions cor­res­pon­dant à une ins­truc­tion ont fi­ni de s’exé­cu­ter, l’ins­truc­tion est mise en at­tente. Elle est en­suite « re­ti­rée » du pi­pe­line, dans l’ordre chro­no­lo­gique d’exé­cu­tion at­ten­du : sa lé­gi­ti­mi­té est vé­ri­fiée et, le cas échéant, ses ré­sul­tats sont en­té­ri­nés. Pour­quoi re­ti­rer dans l’ordre chro­no­lo­gique ? Pre­miè­re­ment parce que l’exé­cu­tion d’un pro­gramme ne doit pas dé­pendre des dé­tails de l’im­plé­men­ta­tion. En­suite, parce que par­fois les choses se passent mal : par exemple une di­vi­sion par zé­ro. Or, quand le pro­ces­seur ren­contre une ins­truc­tion fau­tive, il doit gé­né­rer une ex­cep­tion. Mais si les ins­truc­tions étaient re­ti­rées dans le désordre, le core dump pour­rait in­di­quer un état des re­gistres dif­fé­rent de ce­lui qui a pro­vo­qué l’er­reur. Les pro­ces­seurs pra­tiquent éga­le­ment une autre op­ti­mi­sa­tion ap­pe­lée « bran­che­ment spé­cu­la­tif » . Il s’agit de pré­dire le ré­sul­tat d’un test avant que ce­lui- ci ne soit cal­cu­lé. Pour ce­la, le pro­ces­seur col

lecte le ré­sul­tat

du test à chaque fois qu’il l’exé­cute, en dé­duit une pro­ba­bi­li­té de suc­cès dont il se sert pour com­men­cer à exé­cu­ter « spé­cu­la­ti­ve­ment » les ins­truc­tions de la branche la plus pro­bable. Quitte à tout dé­tri­co­ter si ja­mais le test re­tourne in fine la va­leur in­verse.

Es­pace mé­moire virtuel noyau et uti­li­sa­teur

Tous les sys­tèmes d’ex­ploi­ta­tion mo­dernes, Win­dows ou UNIX­like ( Li­nux, Mac OS X), uti­lisent le concept de « mé­moire vir­tuelle » . Chaque pro­ces­sus s’exé­cute dans un es­pace de mé­moire dit « virtuel » à 64- bit ( conte­nant donc 264 ~ 1020 oc­tets). Un cir­cuit ap­pe­lé MMU ( Me­mo­ry Ma­na­ge­ment Unit) se charge de tra­duire ces adresses vir­tuelles en adresses RAM réelles ( physiques). La MMU dé­coupe les mé­moires vir­tuelle et phy­sique en « pages » ( gé­né­ra­le­ment 4 ko) et gère des tables de trans­la­tion entre adresses vir­tuelles et adresses physiques. Chaque pro­ces­sus pos­sède sa propre table. Une ges­tion cor­recte de la MMU évite ain­si que les pro­ces­sus ne « se marchent » les uns sur les autres. En outre, il existe deux ni­veaux d’exé­cu­tion : « uti­li­sa­teur » et « su­per­vi­seur » – ou « pri­vi­lé­gié » . Toutes les tâches cou­rantes tournent en mode uti­li­sa­teur ; seul le noyau tourne en mode su­per­vi­seur. Ce der­nier dis­pose d’un es­pace mé­moire virtuel pro­té­gé : les don­nées qui s’y trouvent ne peuvent être lues/ écrites que par lui- même ; toute ten­ta­tive d’ac­cès par un pro­ces­sus uti­li­sa­teur se solde par une ex­cep­tion du type Seg­men­ta­tion fault. Et pour cause : on trouve dans cet es­pace des don­nées sen­sibles, par exemple, des clefs cryp­to­gra­phiques pri­vées. Un jeu de dra­peaux propre à une page ou à une zone de pages in­dique le sta­tut de chaque page, pri­vi­lé­giée ou non, lec­ture- seule ou lec­ture/ écri­ture, etc. Les pro­ces­sus uti­li­sa­teurs font plus ou moins fré­quem­ment ap­pel au noyau, qui offre tous les ser­vices d’en­trée- sor­tie et de ges­tion de mé­moire, entre autres. Ces ap­pels au noyau, par l’in­ter­mé­diaire de « trappes » lo­gi­cielles, pro­voquent un chan­ge­ment de ni­veau de pri­vi­lège, mais pas de chan­ge­ment d’es­pace mé­moire virtuel. Lo­gi­que­ment, il fau­drait donc, à chaque « trappe » , que le noyau sau­ve­garde la table de trans­la­tion du pro­ces­sus en mé­moire, charge la sienne propre, exé­cute le ser­vice de­man­dé, puis re­charge la table de trans­la­tion sau­ve­gar­dée avant de rendre la main. Ces opé­ra­tions ont un coût. Les dé­ve­lop­peurs des noyaux UNIX – et Win­dows dans une moindre me­sure – ont donc dé­ci­dé d’in­clure l’es­pace mé­moire virtuel du noyau dans l’es­pace virtuel de chaque pro­ces­sus. Inu­tile alors de chan­ger de table de trans­la­tion lors d’un ap­pel sys­tème, puisque les deux es­paces mé­moire co­existent. Mais, dans ces condi­tions, com­ment em­pê­cher qu’un pro­ces­sus uti­li­sa­teur ac­cède aux don­nées du noyau ? Au­cun sou­ci : pri­vi­lé­giées, ces don­nées sont pro­té­gées de tout ac­cès en mode uti­li­sa­teur. Il n’y a donc au­cune fuite pos­sible. Du moins en théo­rie…

MELTDOWN

Oui, mais pas en pra­tique ! L’at­taque Meltdown [ 1] [ 3] ( CVE- 2017- 5754) ex­ploite une faille, ou­verte par l’exé­cu­tion an­ti­ci­pée d’ins­truc­tions, qui mo­di­fie l’état du pro­ces­seur en fonc­tion de la va­leur d’une don­née pour­tant in­ter­dite en lec­ture. Voyons com­ment. Sup­po­sons que nous ayons al­loué, à l’adresse A, une zone de mé­moire uti­li­sa­teur de 4 096 × 256 = 1 Mo. Consi­dé­rons le pseu­do- code sui­vant : ( 1) Char­ger dans le re­gistre R0 un oc­tet d’une zone pri­vi­lé­giée ( 2) Char­ger dans le re­gistre R1 l’oc­tet à l’adresse A + 4096 * R0 Nous l’avons vu, le contrôle de va­li­di­té de l’ins­truc­tion ( 1) n’in­ter­vient qu’au mo­ment où l’ins­truc­tion est re­ti­rée. En at­ten­dant, l’exé­cu­tion an­ti­ci­pée se pour­suit avec ( 2) dès que la don­née de­man­dée par ( 1) ar­rive. Que fait ( 2) ? ( 2) uti­lise la don­née char­gée par ( 1) comme in­dex pour ac­cé­der à une adresse non- pri­vi­lé­giée. La don­née conte­nue à cette adresse est char­gée dans R1, mais éga­le­ment dans le cache. Pour­quoi la mul­ti­pli­ca­tion par 4 096, c’est- à- dire la taille d’une page ? Parce qu’une ligne du cache ne peut pas sto­cker des don­nées is­sues de pages dif­fé­rentes1. La mul­ti­pli­ca­tion par 4 096 as­sure donc que, se­lon la va­leur de R0, nous al­lons lire dans une page dif­fé­rente, et donc rem­plir une ligne de cache dif­fé­rente. Quand le pro­ces­seur s’aper­çoit que

( 1) viole la pro­tec­tion su­per­vi­seur, il dé­clenche une ex­cep­tion. R0 est ef­fa­cé – ce qui rai­son­nable, au­tre­ment la va­leur illi­ci­te­ment lue se­rait dis­po­nible dans le core dump –, R1 re­prend sa va­leur ini­tiale… mais le cache lui n’est pas ré­ini­tia­li­sé. Ain­si, nous avons main­te­nant dans le cache une don­née qui ne de­vrait pas y être, à une adresse qui dé­pend di­rec­te­ment de la don­née illi­ci­te­ment lue. La belle af­faire ! Et pour­tant. Sup­po­sons que, préa­la­ble­ment à ( 1), nous ayons exé­cu­té une ins­truc­tion vi­dant le cache ( 1)

Une ligne de cache ne contient qu’un seul jeu de dra­peaux de sta­tut de page : im­pos­sible d’y sto­cker des don­nées is­sues d’une page uti­li­sa­teur et d’une page su­per­vi­seur, par exemple.

( cl­flush). Après ( 2), nous sommes sûrs de n’avoir qu’une seule adresse de notre table dans le cache. Pour sa­voir la­quelle, il nous suf­fit, après coup, de lire toutes les don­nées aux adresses A, A + 4096, A + 2 * 4096, …, A + 255 * 4096 et de me­su­rer les temps d’ac­cès : la don­née en cache se­ra lue plus vite que les autres, si vite qu’il se­ra fa­cile de de­vi­ner à quelle adresse elle se trouve. Une fois cette adresse connue, on en dé­dui­ra la va­leur tran­si­toire de R0, bien qu’elle pro­vienne d’un es­pace nor­ma­le­ment in­ac­ces­sible. Bin­go ! Re­com­men­çons ( 1) sur l’en­semble de l’es­pace virtuel du noyau, et voi­là : nous ob­te­nons un dump in­té­gral de la mé­moire ac­ces­sible à ce der­nier. Sa­chant que, pour des rai­sons de per­for­mance, le noyau pos­sède un ac­cès di­rect à toute la mé­moire phy­sique de la ma­chine, nous avons ain­si ac­cès à toute la mé­moire, donc aux codes et don­nées de tous les pro­ces­sus qui s’exé­cutent sur la ma­chine. On pour­ra ob­jec­ter que la faille existe mais qu’elle dé­clenche à chaque lec­ture illi­cite une ex­cep­tion qu’il faut gé­rer, faute de quoi le pro­gramme plante. Lire l’in­té­gra­li­té de la mé­moire si­gni­fie gé­rer plu­sieurs mil­liards de ces ex­cep­tions, soit un temps tel­le­ment long qu’il ne pré­sente au­cun in­té­rêt pra­tique. C’est mal­heu­reu­se­ment faux. Les pro­ces­seurs In­tel dis­posent d’une pseu­do- ins­truc­tion ap­pe­lée TSX qui groupe un bloc d’ins­truc­tions en une « mé­ta- ins­truc­tion » ato­mique – à la ma­nière d’une tran­sac­tion dans une base de don­nées. Si l’une des ins­truc­tions de ce bloc pro­voque une er­reur, toute la tran­sac­tion est an­nu­lée, mais au­cune ex­cep­tion n’est le­vée. Les re­gistres re­viennent à leur état ini­tial, mais le cache, lui, conti­nue à por­ter les traces des ins­truc­tions an­nu­lées. Dans la pra­tique, les pro­grammes d’at­taque uti­lisent quelques raf­fi­ne­ments sup­plé­men­taires ( cf. [ 1] pour plus de pré­ci­sions. Grâce à TSX, un dé­mons­tra­teur par­vient à dum­per la mé­moire à un dé­bit d’en­vi­ron 512 ko/ s avec un taux d’er­reur qua­si- nul. Ce qui si­gni­fie en­vi­ron 30 mi­nutes par gi­ga­oc­tet, ou une jour­née pour une ma­chine avec 48 Go de mé­moire.

Im­pli­ca­tions de Meltdown en termes de sé­cu­ri­té

Meltdown dé­montre que la cloi­son, que l’on pen­sait étanche, entre don­nées uti­li­sa­teur et don­nées su­per­vi­seur, est en réa­li­té po­reuse. N’im­porte quel pro­ces­sus peut lire n’im­porte quelle don­née ac­ces­sible au noyau, pour­vu que l’es­pace virtuel du noyau soit conte­nu dans l’es­pace mé­moire virtuel du pro­ces­sus d’at­taque, ce qui est le cas en pra­tique sur tous les OS cou­rants. L’ori­gi­na­li­té de cette at­taque tient à sa dis­cré­tion : elle se dé­roule à l’in­su de tous les autres uti­li­sa­teurs, noyau com­pris. Meltdown ne laisse au­cune trace dans les logs. Au­cune faille lo­gi­cielle n’est en cause, le code n’est pas tou­ché. Un simple pro­gramme d’at­taque en mode uti­li­sa­teur ( sans root­kit) suf­fit. Ce n’est pas un vi­rus. Ce n’est pas un che­val de Troie. Per­sonne n’est à blâ­mer. Bien en­ten­du, ce sont les ser­veurs qui sont les plus ex­po­sés : les ma­chines mul­ti- uti­li­sa­teurs en gé­né­ral et, par­ti­cu­liè­re­ment, les ser­veurs mu­tua­li­sés « bas de gamme » , où l’hy­per­vi­seur maître par­tage le même noyau entre les dif­fé­rentes ma­chines vir­tuelles. Dans ce cas, la mé­moire vir­tuelle de ce noyau par­ta­gé contient toutes les don­nées pré­sentes sur la ma­chine phy­sique, ce qui si­gni­fie que n’im­porte qui, de­puis une ma­chine vir­tuelle, peut lire les don­nées sto­ckées sur les autres ma­chines vir­tuelles co- hé­ber­gées, voire les don­nées de l’hy­per­vi­seur lui- même. Comme le code d’at­taque se contente d’ac­cé­der à de la mé­moire sans avoir re­cours à au­cun ser­vice du noyau, au­cune me­sure ne peut em­pê­cher son exé­cu­tion.

Pa­rades

Il n’existe, à l’heure ac­tuelle, au­cune pa­rade hard­ware dis­po­nible contre Meltdown. In­tel a confir­mé qu’une mise à jour du mi­cro­code des pro­ces­seurs ne ser­vi­rait à rien. La seule so­lu­tion ef­fi­cace, in­ter­dire l’exé­cu­tion pa­ral­lèle, au­rait de telles consé­quences en termes de per­for­mance qu’elle est in­en­vi­sa­geable. No­tons que les pro­ces­seurs AMD et ARM pa­raissent im­mu­ni­sés contre Meltdown : la faille exis­te­rait bien, mais semble – pour l’ins­tant du moins – in­ex­ploi­table par des pro­grammes simples. On pour­rait croire que la po­li­tique des noyaux Li­nux ré­cents, à par­tir de la ver­sion 4.12, consis­tant à ti­rer au sort la po­si­tion de l’es­pace virtuel du noyau à chaque boot pour­rait

éven­tuel­le­ment pro­té­ger les ma­chines contre Meltdown. En réa­li­té, ce mé­ca­nisme est li­mi­té à 40 bits, et comme l’es­pace mé­moire noyau de­meure conti­gu, il suf­fit de réa­li­ser des lec­tures ré­gu­liè­re­ment es­pa­cées de quelques gi­ga­oc­tets pour trou­ver la bonne gamme d’adresses. Conclu­sion : ce mé­ca­nisme, contour­né en quelques se­condes, n’offre au­cune pro­tec­tion. L’unique voie de contour­ne­ment consiste à réa­li­ser une iso­la­tion to­tale des es­paces mé­moire du noyau et des pro­ces­sus, ce que les dé­ve­lop­peurs Li­nux ont bap­ti­sé KP­TI ( Ker­nel Page Table Iso­la­tion) [ 5]. Dans ce cas, l’es­pace virtuel du noyau n’est plus in­clus dans l’es­pace virtuel des pro­ces­sus. Meltdown ne fonc­tionne plus, car la mé­moire vir­tuelle des pro­ces­sus ne contient plus que les don­nées des pro­ces­sus eux- mêmes. Cette pa­rade est dé­sor­mais en place sur les noyaux Li­nux les plus ré­cents. Win­dows de­vrait avoir été mis à jour à l’heure où ces lignes pa­raî­tront et Mac OS a dé­jà été pat­ché de­puis la ver­sion 10.13.2 [ 4]. FreeBSD ne l’est mal­heu­reu­se­ment pas [ 5], car les équipes de dé­ve­lop­pe­ment n’ont été pré­ve­nues que fin dé­cembre. Les autres xBSD sont éga­le­ment vul­né­rables, et pour l’en­semble de ceux- ci, il ne faut pas s’at­tendre à des ver­sions cor­ri­gées avant plu­sieurs se­maines au mieux, vu l’am­pleur des mo­di­fi­ca­tions à ap­por­ter. KP­TI fonc­tionne, mais son im­pact sur les per­for­mances n’est pas né­gli­geable. À chaque ap­pel sys­tème, le pro­ces­seur doit main­te­nant jon­gler entre les tables de trans­la­tion du noyau et celles des pro­ces­sus uti­li­sa­teurs et vi­der le cache de ni­veau 12. Le ré­sul­tat est une perte de per­for­mance es­ti­mée entre 5 % et 30 % par­fois, voire plus. En réa­li­té, l’im­pact dé­pend du pro­fil de l’ap­pli­ca­tion. Les ap­pli­ca­tions pu­re­ment gra­phiques, comme les jeux, ou les ap­pli­ca­tions de cal­cul in­ten­sif, qui ne réa­lisent que très peu d’en­trée- sor­tie, ne se­ront pas vrai­ment ra­len­ties. En re­vanche, les bases de don­nées et les ser­veurs web su­bi­ront le contre- coup de plein fouet. [ 7] donne plus de dé­tails sous Li­nux. Tou­te­fois, les pro­prié­taires de pro­ces­seurs dis­po­sant de l’ex­ten­sion PCID semblent pou­voir s’en ti­rer mieux que les autres : leurs pro­ces­seurs ne se­raient que très lé­gè­re­ment af­fec­tés par KP­TI. Apple, dont la plu­part des ma­chines sont équi­pées de pro­ces­seurs pos­sé­dant cette ex­ten­sion, a af­fir­mé n’en­re­gis­trer que des ra­len­tis­se­ments mar­gi­naux [ 4].

SPECTRE

La deuxième at­taque, bap­ti­sée Spectre, se base sur le même prin­cipe : leur­rer l’uni­té spé­cu­la­tive du pro­ces­seur pour lui faire faire exé­cu­ter des ins­truc­tions « fan­tômes » qui ne se­ront ja­mais va­li­dées, mais lais­se­ront une trace dans le cache. Spectre re­couvre deux as­pects dis­tincts. La vul­né­ra­bi­li­té CVE- 20175753 concerne l’ac­cès aux don­nées hors bornes. Consi­dé­rons le pro­gramme C sui­vant : ( 1) if ( in­dex < si­ze_ of_ ar­ray) { ( 2) foo = ar­ray [ in­dex] ( 3) bar = ar­ray2 [ foo * 4096] } Les bonnes pra­tiques exigent qu’une va­riable d’in­dex soit sys­té­ma­ti­que­ment vé­ri­fiée avant un ac­cès in­di­rect à une table pour s’as­su­rer qu’elle n’ex­cède pas le maxi­mum per­mis. Mais ici, cette me­sure de bon sens va se re­tour­ner contre nous. Sup­po­sons : A. Que la va­riable si­ze_ of_ ar­ray ne soit pas dans le cache ; B. Que le test ait été ( qua­si­ment) tou­jours vé­ri­fié dans le pas­sé ; C. Qu’un « ha­cker » ait réus­si à in­jec­ter dans la va­riable in­dex une va­leur hors li­mites, de telle sorte que ar­ray [ in­dex] pointe vers une don­née sen­sible dans la mé­moire uti­li­sa­teur du pro­ces­sus ; Que se passe- t- il ? Le pro­ces­seur, pour cal­cu­ler la va­leur lo­gique du test, est obli­gé d’ac­cé­der à la va­leur de la va­riable si­ze_ of_ ar­ray, qui se trouve hors cache, et doit donc être ra­pa­triée de­puis la RAM. Il ne va pas at­tendre sans rien faire. Au contraire, il va uti­li­ser son uni­té de pré­dic­tion pour de­vi­ner le ré­sul­tat du test ( ici, en rai­son de la condi­tion B, vrai), puis va com­men­cer à exé­cu­ter ( 2) et ( 3). Si ( 2) va cher­cher une don­née en cache, celle- ci se­ra dis­po­nible bien avant que le ré­sul­tat de ( 1) le soit. Le pro­ces­seur va donc pour­suivre avec ( 3), et on se re­trouve dans le même cas que Meltdown : l’ac­cès spé­cu­la­tif à la zone de mé­moire quel­conque poin­tée par ar­ray2 pro­vo­que­ra le char­ge­ment d’une ligne par­ti­cu­lière du cache, qui dé­pen­dra di­rec­te­ment de foo. Si au­cune des adresses de la table ar­ray2 ne se trouve dans le cache, alors il se­ra fa­cile de de­vi­ner quelle est la va­leur de foo, sim­ple­ment en me­su­rant les temps d’ac­cès à la table. [ 2] pré­sente et dis­cute une im­plé­men­ta­tion pos­sible de cette at­taque en C et en Ja­vas­cript. Mal­heu­reu­se­ment, l’uni­té spé­cu­la­tive de test n’est pas la seule qui semble at­ta­quable. L’uni­té spé­cu­la­tive de bran­che­ment l’est aus­si, ce qui consti­tue une at­taque sé­pa­rée ( CVE- 2017- 5715). Cette uni­té se charge de com­mu­ni­quer une adresse de saut pro­bable lorsque celle- ci ne peut être dé­ter­mi­née ra­pi­de­ment, ce qui ar­rive, par exemple, lors­qu’elle se trouve à un em­pla­ce­ment mé­moire ré­fé­ren­cé par un re­gistre ( jmp*% eax), et que cet em­pla­ce­ment mé­moire ne se trouve pas en cache. Ici aus­si, il semble théo­ri­que­ment pos­sible de « cor­rompre » cette uni­té pour leur­rer le pro­ces­seur vers une fausse adresse à la­quelle il exé­cu­te­ra spé­cu­la­ti­ve­ment une sé­rie d’ins­truc­tions préa­la­ble­ment choi­sie, condui­sant vrai­sem­bla­ble­ment à la di­vul­ga­tion d’une don­née se­crète par le même mé­ca­nisme que pré­cé­dem­ment. Les dé­tails d’im­plé­men­ta­tion manquent à ce stade, car le fonc­tion­ne­ment de l’uni­té spé­cu­la­tive de bran­che­ment reste as­sez né­bu­leux.

Im­pli­ca­tions de Spectre en termes de sé­cu­ri­té

Spectre per­met de ga­gner un ac­cès à des don­nées sen­sibles qui se trouvent dans l’es­pace mé­moire uti­li­sa­teur d’un pro­ces­sus, par exemple des mots de passe. Cette faille pa­raît dif­fi­cile à ex­ploi­ter sur des exé­cu­tables purs : il faut ma­ni­pu­ler l’une des uni­tés d’exé­cu­tion spé­cu­la­tive à son in­su, ce qui est fai­sable, mais dé­li­cat. Hé­las, ces uni­tés spé­cu­la­tives semblent par­ta­gées entre hy­per- threads, ce qui si­gni­fie qu’un pro­ces­sus peut dé­li­bé­ré­ment in­fluen­cer un autre pro­ces­sus qui tour­ne­rait dans un hy­per- thread dif­fé­rent

mais sur le même coeur. Heu­reu­se­ment, les coeurs semblent iso­lés entre eux. En re­vanche, les na­vi­ga­teurs in­ter­net consti­tuent des cibles fa­ciles, car ils sont conçus pour exé­cu­ter des pro­grammes en Ja­vaS­cript à l’ori­gine non- contrô­lée, et, comme le prouve [ 2], quelques lignes suf­fisent pour ex­ploi­ter la faille. Des patches se­raient en cours de dé­ve­lop­pe­ment pour le Ja­vaS­cript avec, là aus­si, un im­pact mi­ni­mal sur la per­for­mance [ 4]. Spectre af­fecte un nombre plus im­por­tant de pro­ces­seurs que Meltdown : les mo­dèles ré­cents d’In­tel, d’AMD et mêmes cer­tains mo­dèles ARM sont tou­chés [ 2]. Les smart­phones sont donc éga­le­ment concer­nés.

Pa­rades contre Spectre

Une pa­rade pu­re­ment lo­gi­cielle contre Spectre semble dif­fi­cile à créer. Là aus­si, la seule me­sure vrai­ment ef­fi­cace consis­te­rait à « sé­ria­li­ser » l’exé­cu­tion des ap­pli­ca­tions, ce qui est im­pos­sible. En at­ten­dant, les com­pi­la­teurs pour­raient in­sé­rer cer­taines ins­truc­tions telles que mfence et lfence en x86, qui pro­voquent une syn­chro­ni­sa­tion de l’exé­cu­tion, après chaque test. Ce­pen­dant, cette so­lu­tion reste à éva­luer. Une autre pos­si­bi­li­té consiste à in­sé­rer une boucle d’at­tente ca­li­brée après chaque test, ce qui pa­ra­lyse le pro­ces­seur le temps que le ré­sul­tat du test ar­rive ; une nou­velle fois, l’im­pact sur la per­for­mance pa­raît non- né­gli­geable, d’au­tant que le pro­ces­seur peut par­fois exé­cu­ter spé­cu­la­ti­ve­ment plus de cent ins­truc­tions à l’avance. Un an­ti­dote simple, bap­ti­sé ret­po­line [ 8], contre l’ex­ploi­ta­tion de l’uni­té spé­cu­la­tive de bran­che­ment ( CVE2017- 5715), dé­jà in­té­gré aux branches de dé­ve­lop­pe­ment de LLVM et GCC, consiste à rem­pla­cer le saut in­di­rect jmp *% eax par la sé­quence ( syn­taxe AT& T) : call ijeax stall: pause jmp stall … ijeax: mov % eax, (% rsp) ret Un ap­pel à un sous- pro­gramme se sub­sti­tue au bran­che­ment. Cet ap­pel sau­ve­garde l’adresse de re­tour dans la pile, poin­tée par le re­gistre rsp, et si­mul­ta­né­ment dans un cache in­terne. Le sous- pro­gramme écrase l’adresse de re­tour nor­male par l’adresse de des­ti­na­tion du saut conte­nue dans eax, puis exé­cute ret. En at­ten­dant que l’adresse de re­tour réelle soit ra­pa­triée, le pro­ces­seur uti­lise l’adresse en cache. Il re­tourne donc au pro­gramme prin­ci­pal et tombe en stall sur une boucle d’at­tente in­fi­nie. Lorsque l’adresse de re­tour, égale main­te­nant à la va­leur conte­nue dans eax, ar­rive, le pro­ces­seur s’y rend. L’uni­té spé­cu­la­tive de bran­che­ment n’est plus uti­li­sée. [ 8] dis­cute l’im­pact de ret­po­line sur les per­for­mances et la ma­nière de le mi­ni­mi­ser.

Conclu­sion

Meltdown et Spectre ex­ploitent la fra­gi­li­té in­hé­rente à l’exé­cu­tion spé­cu­la­tive des ins­truc­tions, une op­ti­mi­sa­tion pré­sente dans la plu­part des pro­ces­seurs gra­vés de­puis dix ans. Meltdown, en par­ti­cu­lier, re­pré­sente une me­nace grave pour le fonc­tion­ne­ment de tous les ser­veurs mul­ti- uti­li­sa­teurs ou hé­ber­geant des ma­chines vir­tuelles à noyau par­ta­gé. Les so­lu­tions dé­ployées en ur­gence pour contrer ces deux failles ma­jeures ne sont que des pis- al­ler, dans la me­sure où l’im­pact sur les per­for­mances des ma­chines ne se­ra pas né­gli­geable. Les an­ciennes ma­chines, ou celles équi­pées de pro­ces­seurs d’en­trée de gamme, se­ront les plus tou­chées. Cer­tains sys­tèmes d’ex­ploi­ta­tion, comme FreeBSD, se­ront vul­né­rables pen­dant en­core plu­sieurs se­maines. On pour­ra dé­plo­rer que leurs équipes de dé­ve­lop­pe­ment n’aient pas été pré­ve­nues plus tôt. La ré­ac­tion des prin­ci­paux hé­ber­geurs à Meltdown semble ra­pide ( par exemple [ 9]), mal­gré le coût que ce­la re­pré­sente. Ceux- ci de­vront éga­le­ment re­ti­rer les ver­sions vul­né­rables des noyaux pro­po­sés aux uti­li­sa­teurs, quitte à les for­cer à chan­ger pour une ver­sion cor­ri­gée. Les pe­tits four­nis­seurs, qui pro­posent sou­vent des noyaux en re­tard d’une ou deux ité­ra­tions, se re­trouvent au pied du mur. Si Meltdown semble sous contrôle, Spectre ne l’est que par­tiel­le­ment. Il est à no­ter que les me­sures pal­lia­tives prises contre Meltdown n’af­fectent pas Spectre, et vice- ver­sa. Seul un chan­ge­ment ma­té­riel d’ar­chi­tec­ture per­met­tra de cor­ri­ger si­mul­ta­né­ment ces failles. L’ité­ra­tion 10 nm des pro­ces­seurs In­tel, dé­jà re­pous­sée à la fin 2018, risque de su­bir un nou­veau re­tard. D’ici là, le fon­deur conti­nue­ra à com­mer­cia­li­ser des pro­ces­seurs vul­né­rables. Comme la tran­si­tion vers la nou­velle ar­chi­tec­ture ne se­ra pas ins­tan­ta­née, pré­pa­rons- nous à co­ha­bi­ter pen­dant quelques an­nées avec ces failles. ❍

TEMPS D’AC­CÈS EN LEC­TURE DE 256 PAGES CONSÉCUTIV­ES. L’UNIQUE PAGE RÉ­SI­DENTE DANS LE CACHE EST AI­SÉ­MENT IDENTIFIAB­LE, SON TEMPS D’AC­CÈS ÉTANT EN MOYENNE IN­FÉ­RIEUR DE MOI­TIÉ AUX AUTRES. D’APRÈS [ 1].

Newspapers in French

Newspapers from France

© PressReader. All rights reserved.