URL: https://linuxfr.org/news/faille-lazy-fpu-state-restore Title: Faille Lazy FPU state restore Authors: David Marec ZeroHeure, Benoît Sibaud, Davy Defaud, palm123, BAud, patrick_g et claudex Date: 2018年06月20日T14:51:32+02:00 License: CC By-SA Tags: meltdown, lwn, cve-2018-3665, lazy_fpu_state_restore, intel, cve et vulnérabilité Score: 62 Intel est de nouveau confronté à la découverte d’une faille, le [_Lazy FPU State Restore flaw_](http://blog.cyberus-technology.de/posts/2018-06-06-intel-lazyfp-vulnerability.html). Cette fois, seule la famille des Intel _Core_ [serait concernée](https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00145.html). ---- [Explication des inventeurs](http://blog.cyberus-technology.de/posts/2018-06-06-intel-lazyfp-vulnerability.html) [La mécanique Lazy FPU state restore expliquée par les équipes NetBSD](http://www.netbsd.org/docs/kernel/lazyfpu.html) [Le message de Linus lors du passage en mode eager](https://marc.info/?l=linux-kernel&m=145367515208445) [Annonce officielle de FreeBSD](https://security.freebsd.org/advisories/FreeBSD-SA-18:07.lazyfpu.asc) ---- Le FPU ====== [Le FPU, c’est le bordel, par Ingo Molnar](https://lwn.net/Articles/643235/) L’unité de calcul en virgule flottante, le [FPU](https://fr.wikipedia.org/wiki/Unit%C3%A9_de_calcul_en_virgule_flottante "Floating‐point unit"), possède une série de registres qui lui permet de « définir » son état courant. Lors du basculement d’une tâche à une autre (*context switching*), cet état est alors restauré pour retrouver un contexte correspondant au processus en cours. Ces opérations peuvent être coûteuses car les registres du FPU sont plus gros que les autres, c’est pourquoi les FPU fournissent une option pour désactiver toute opération en virgule flottante (`CR0:TS`). Aussi, dès qu’un calcul en virgule flottante est appelé, une exception est lancée pour « réveiller » le FPU avant de lancer l’opération normalement. Lorsque cette exception (`fpudna`, _FPU Device Not Available_) se produit, un « gestionnaire de contexte FPU » vérifie quel processus a la main sur le FPU à ce moment‐là. S’il s’agit d’un autre processus, il procède à la sauvegarde puis la restauration des registres, ou s’il s’agit d’un nouveau contexte, la sauvegarde puis le nettoyage des registres ; sinon, il ne fait rien : c’est le mode « paresseux » (_lazy_). À la sortie du processus, il ne faut pas oublier de « nettoyer » ces tables et de (re)lever tous les drapeaux liés à cette exception. En mode _eager_ (zélé, volontaire), la sauvegarde et restauration des registres associés au FPU est effectuée quoiqu’il advienne, au moment du changement de tâche et non durant l’exécution de la tâche qui vient de prendre la main. Le bâton ======== Au fil des années, les processeurs ont multiplié les registres pour prendre en charge les instructions de type [SIMD](https://software.intel.com/sites/landingpage/IntrinsicsGuide/ "Single Instruction on Multiple Data"), soit une instruction capable de procéder au même calcul sur un ensemble de paires de données. Les registres `SSE`, `AVX` et `MMX` restent associés au FPU et seront donc intégrés au mécanisme de sauvegarde et restauration... et ils peuvent contenir jusqu’à 2 048 bits de données, rien que sur l’AVX. ``` [ 0.000000] Linux version 4.14.48-intel-pk-standard (oe-user@oe-host) (icc version 18.0.2 (gcc version 7.3.0 compatibility)) #2 SMP PREEMPT Wed Jun 20 13:21:48 UTC 2018 [ 0.000000] x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers' [ 0.000000] x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers' [ 0.000000] x86/fpu: Supporting XSAVE feature 0x008: 'MPX bounds registers' [ 0.000000] x86/fpu: Supporting XSAVE feature 0x010: 'MPX CSR' [ 0.000000] x86/fpu: xstate_offset[3]: 576, xstate_sizes[3]: 64 [ 0.000000] x86/fpu: xstate_offset[4]: 640, xstate_sizes[4]: 64 [ 0.000000] x86/fpu: Enabled xstate features 0x1b, context size is 704 bytes, using 'compacted' format. ``` Pour se faire battre ==================== Par le biais [désormais connu](https://meltdownattack.com/) de l’exécution spéculative puis de l’analyse de cache, un attaquant pourra lire ces registres depuis un autre processus, voire depuis une machine virtuelle. En effet, en mode _lazy_ la sauvegarde des registres d’une tâche s’effectue au cours de l’exécution d’une autre tâche. La spéculation ignorant le drapeau `CR0:TS`, tout est alors possible. Ces registres peuvent contenir des informations sensibles comme des clefs de chiffrement (_AES_), par le biais des instructions d’accélération matérielle [AES-NI](https://www.intel.com/content/www/us/en/architecture-and-technology/advanced-encryption-standard--aes-/data-protection-aes-general-technology.html).> _Delivers Fast, Affordable Data Protection and Security. AHeum._ [Colin Percival](http://t.co/fg12uOezcP), ex‐membre de l’équipe [sécurité de FreeBSD](https://www.freebsd.org/security/), a codé un _exploit_ en quelques heures et note, dans un [tweet](https://twitter.com/cperciva/status/1007010583244230656?lang=en) :> « _You need to be able to execute code on the same CPU as the target process in order to steal cryptographic keys this way. You also need to perform a specific sequence of operations before the CPU pipeline completes, so there’s a narrow window for execution._ » > « Vous devez être en mesure d’exécuter le code de [l’_exploit_] sur le même processeur que celui de la cible pour voler les clefs de cette manière. Vous devrez en outre appliquer une suite précise d’opérations avant que la chaîne de traitement du processeur ne se termine ; de fait, la fenêtre de tir est très étroite. » Ce qui semble vouloir dire que, pour l’instant, coder le vol de données depuis un script venu du Web n’est pas simple à réaliser. Le temps nécessaire au vol des données des registres est la clef de l’attaque. Il faut le terminer avant que le séquenceur ne préempte la victime et que les valeurs des registres ne soient modifiées. Pour y arriver, les chercheurs ont utilisé plusieurs méthodes : Exception --------- Il s’agit de coder la fuite de données à l’ombre d’une exception, sciemment provoquée, tel un `page fault`, par exemple. Mais il s’avère que cette solution est trop lente pour récupérer tout un jeu de registres. Intel TSX --------- Cette mécanique n’est disponible que sur les architectures récentes (à partir de _Haswell_), ce qui limite l’angle d’attaque. Cette technologie comporte un jeu d’instructions appelé RTM (_Restricted Transactional Memory_) qui permet d’annuler un bloc d’exécution en cas d’interruption ; il suffit d’y encadrer le code malicieux, qui va innocemment faire appel au FPU, pour lever l’exception `fpudna`... Ce serait presque « étudié pour ». Retpoline --------- Il s’agit au départ d’une contre‐mesure pour _Spectre_. Elle vise à fourvoyer sciemment le processeur sur l’adresse de retour d’un `RET` en plaçant une « fausse » boucle et donc le forcer à exécuter de manière spéculative un code innocent. Le code malicieux sera donc placé à cet endroit. Les correctifs ============== Le mode _lazy_ semble moins pertinent aujourd’hui. Les gains en performance sont faibles avec les architectures récentes et, surtout, selon les usages actuels. Le FPU étant même beaucoup plus utilisé dans nos logiciels, son usage serait contre‐productif. En effet, les compilateurs choisissent d’appeler les instructions _SIMD_ (i.e. `-sse`) pour optimiser le code des logiciels. De fait, ceux‐ci auront de toute façon sauvegardé et restauré les registres du FPU à chaque changement de contexte. La gestion de l’exception sera inutile et va juste alourdir le processus. En outre, l’empreinte d’une sauvegarde et restauration serait moindre que celle de la gestion des drapeaux, des registres et de leurs états suite à l’interruption, le transfert de registres FPU en mémoire étant plus rapide car optimisé. Il est donc préconisé d’éviter le mode _lazy_ au profit du mode _eager_. * Linux propose le mode _eager_ plutôt que le mode _lazy_ depuis la version 3.7 et l’active par défaut depuis la version 4.9 ; * ajoutez `eagerfpu=on` sur la ligne de démarrage pour les versions antérieures à la 4.9 ; * FreeBSD a poussé un [correctif](https://svnweb.freebsd.org/base?view=revision&revision=335196) pour la Release 11.2 ; c’est un FreeBSD 11.1 qui a servi de cobaye ; * DragonFly BSD a poussé un [correctif](https://www.dragonflydigest.com/2018/06/18/21414.html) dans la version 5.2.2 ; * Microsoft [poussera un correctif en juillet](https://windowsreport.com/lazy-fp-state-restore-intel-processor/) ; * OpenBSD a poussé un correctif [le 14 juin](https://ftp.openbsd.org/pub/OpenBSD/patches/6.3/common/010_intelfpu.patch.sig) pour la version 6.3 ; * NetBSD a poussé un correctif [le 16 juin](http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/arch/x86/x86/fpu.c) sur `MAIN` ; * Illumos a poussé un correctif [le 19 juin](https://www.illumos.org/issues/9598). Conclusion ========== Ils ne sont pas à la fête cette année, chez Intel. Le point positif est que la correction de cette faille devrait conduire à une amélioration des performances, voire de la consommation d’énergie. Theo de Raadt avait prévenu [11 ans auparavant](https://marc.info/?l=openbsd-misc&m=118296441702631&w=2) que l’architecture Intel Core 2 promettait ce genre de faille :> « _These processors are buggy as hell, and some of these bugs don’t just cause development/debugging problems, but will_ ASSUREDLY _be exploitable from userland code._ »> « Ces processeurs sont bogués comme jamais et nombre de ces bogues ne provoquent pas seulement des soucis de développement et d’analyse, mais ils vont *assurément* être exploitables depuis l’espace utilisateur. » Pour la petite histoire, l’embargo s’est terminé le 6 juin. Colin Percival, qui assistait à une conférence de Théo de Raadt lors de la [BSDCan 2018](https://www.openbsd.org/events.html#bsdcan2018), a codé un _exploit_ dans la foulée, qu’il n’a pas encore rendu public. Mais il a convaincu Intel de lever l’embargo au plus vite. Il est notable qu’aucun des deux n’avait été mis dans la confidence ; OpenBSD signale même qu’ils en ont fait la demande ([des rumeurs circulaient](https://marc.info/?l=openbsd-cvs&m=152818076013158&w=2) autour d’une énième version de _Spectre_), mais sans obtenir de réponse.> _Invitation to Embargo? No._> _We asked._> _No reply._