Reptar : vulnérabilité FSRM/REX des CPU Intel
(lock.cmpxchg8b.com)- Sur certains processeurs Intel, lorsque
rep movset un préfixerex.rredondant interagissent avec l’optimisation FSRM, le CPU peut entrer dans un état de « glitch » qui échappe aux règles normales - La cause tient au décodage permissif des préfixes de x86 et au fait que, dans des instructions à opérandes implicites comme
movsb, un préfixerexqui devrait normalement être dépourvu de sens emprunte un chemin d’optimisation particulier - En août 2023, le pipeline de validation de Google a découvert des résultats imprévisibles avec cette combinaison, et a observé des branchements inattendus, l’ignorance de branchements inconditionnels, ainsi que des anomalies d’enregistrement du pointeur d’instruction avec
xsaveetcall - Si plusieurs cœurs déclenchent simultanément le même bug, cela peut provoquer une machine check exception et un arrêt du système ; le problème étant reproductible même depuis une VM invitée non privilégiée, il est important pour les environnements cloud
- Intel a publié une mise à jour du microcode pour les processeurs concernés ; si la mise à jour est impossible, il est possible de désactiver les fast strings via
IA32_MISC_ENABLE, au prix d’une forte dégradation des performances
Préfixes x86 et rep movsb
rep movsbest une manière idiomatique de déplacer de la mémoire en x86 : une fois la source, la destination, la direction et le compteur configurés, le processeur gère la copie répétée- L’instruction réelle est
movsb, etrepest un préfixe qui modifie cette instruction pour la répéter plusieurs fois - Le décodage des instructions x86 est relativement permissif : même si des préfixes sans effet ou conflictuels sont ajoutés, ils sont généralement ignorés
- Les compilateurs peuvent utiliser ces préfixes redondants pour compléter une instruction unique jusqu’à la frontière d’alignement souhaitée
rex,vexetevexsont des préfixes qui modifient la façon dont l’instruction suivante est décodée
La combinaison où le préfixe rex pose problème
- i386 comptait 8 registres généraux, ce qui permettait de désigner un registre avec 3 bits, mais x86-64 en compte 16 et nécessite donc des bits supplémentaires
- Le préfixe
rexfournit des bits additionnels que l’instruction suivante peut emprunter lors de l’encodage de ses opérandes- On l’écrit généralement sous la forme
rex.rxb, avec les bitsb,x,retwdéfinis de manière optionnelle
- On l’écrit généralement sous la forme
movsbn’a pas d’opérandes explicitement indiqués dans l’instruction : ils sont tous implicites, donc les bitsrexderex.rxb rep movsbdevraient être sans effet- En général, le processeur ignore silencieusement ce type de préfixe
rex, mais sur les systèmes prenant en charge fast short repeat move, cette combinaison conduit à une vulnérabilité
FSRM et processeurs concernés
- FSRM est une fonctionnalité introduite avec Ice Lake, qui corrige les limites d’ERMS pour le traitement des chaînes courtes
- ERMS (enhanced repeat move/store) peut accélérer le code
rep movsbexistant en gérant l’alignement des buffers et les écritures larges dans le microcode- Son coût d’initialisation est élevé, ce qui le rend inadapté aux chaînes très courtes
- FSRM est une fonctionnalité destinée à traiter plus rapidement les petits déplacements de 128 octets ou moins
- La prise en charge peut être vérifiée via le flag
fsrmdans la ligneflagsde/proc/cpuinfo - Exemples de processeurs intégrant FSRM :
- Ice Lake
- Rocket Lake
- Tiger Lake
- Raptor Lake
- Alder Lake
- Sapphire Rapids
- Cette liste n’est pas exhaustive ; pour la liste complète, il faut consulter l’avis Intel INTEL-SA-00950
Découverte et reproduction
- Le pipeline de validation de Google exécute deux formes de programmes générés aléatoirement au moyen de la technique Oracle Serialization, puis compare leur état final
- Une explication associée figure dans le précédent billet Oracle Serialization
- En août 2023, l’ajout d’un préfixe
rex.rredondant àrep movsoptimisé par FSRM a provoqué des résultats imprévisibles - Les comportements anormaux observés sont les suivants :
- Branchement vers un emplacement inattendu
- Ignorance d’un branchement inconditionnel
- Enregistrement incorrect du pointeur d’instruction (instruction pointer) avec les instructions
xsaveoucall - Signalement par le débogueur d’un état impossible
- Lorsque plusieurs cœurs déclenchent le même bug, le processeur peut signaler une machine check exception et s’arrêter
- Le problème est reproductible même à l’intérieur d’une VM invitée non privilégiée, ce qui en fait un enjeu de sécurité important pour les fournisseurs cloud
- Les outils de reproduction et les ressources de recherche sont publiés dans le security research repository de Google
- Un miroir local de l’outil
icebreakest également fourni sous la forme icebreak.tar.gz
- Un miroir local de l’outil
icebreaktente de reproduire le problème en désignant différentes paires de cœurs- Sur les systèmes non concernés, il ne devrait rien afficher, comme une boucle infinie
- Sur les systèmes concernés, un
.est affiché à chaque reproduction réussie - Sur des cœurs frères SMT, des branchements arbitraires peuvent être observés
- Sur des cœurs frères SMP du même package, une machine check peut être observée
- Si deux cœurs différents ne sont pas désignés, un thread de martèlement peut être nécessaire
Causes possibles et effets observés
- Le fonctionnement du microcode des systèmes modernes n’étant pas public, la cause profonde ne peut être traitée que comme une théorie fondée sur les observations
- Un CPU se divise globalement entre frontend et backend
- Le frontend récupère et décode les instructions pour générer des μops
- Le backend exécute les instructions dans le désordre, puis stocke et finalise les résultats dans le ROB (reorder buffer)
- Ce bug semble amener le frontend à mal calculer la taille de l’instruction
movsb, ce qui provoque ensuite un état où des entrées du ROB sont associées à une mauvaise adresse - Dans cet état, une situation confuse apparaît, où le pointeur d’instruction est mal calculé
- Le système peut finir par revenir à un état cohérent en interne, mais les résultats intermédiaires peuvent être incorrects
- Si plusieurs cœurs SMT ou SMP entrent simultanément dans cet état, une corruption suffisante de l’état microarchitectural peut se produire et forcer une machine check
- L’état du système peut être corrompu au point de provoquer une machine check, et des interférences entre threads ont été observées dans l’exécution de processus planifiés sur des cœurs frères SMT
- Il n’a pas été confirmé qu’il soit possible de contrôler la corruption avec assez de précision pour permettre une élévation de privilèges
Mesures de mitigation
- Intel a publié un microcode mis à jour pour tous les processeurs concernés via INTEL-SA-00950
- Les fournisseurs de systèmes d’exploitation ou de BIOS peuvent déjà proposer la mise à jour
- S’il n’est pas possible de mettre à jour, les fast strings peuvent être désactivés via le registre spécifique au modèle
IA32_MISC_ENABLE - La désactivation des fast strings entraîne une forte dégradation des performances et ne doit donc être utilisée qu’en cas de stricte nécessité
Ressources sur des bugs CPU associés
- Google publie les bugs CPU qu’il découvre, et certains méritent d’être lus même lorsqu’ils n’ont pas d’impact de sécurité
- Exemples de ressources :
- movlps just doesn’t work : cas où
movlpsne fonctionne pas - registers can sometimes roll back : cas où des registres reviennent parfois à une valeur précédente
- movlps just doesn’t work : cas où
1 commentaires
Avis de Hacker News
Article lié : https://cloud.google.com/blog/products/identity-security/goo...
Contenu provenant de https://news.ycombinator.com/item?id=38268043, mais les commentaires ont été regroupés ici
Lire cet article me fait prendre conscience à quel point je connais mal le matériel sur lequel tourne mon logiciel
Il dit que « les préfixes permettent de modifier le comportement des instructions en activant ou désactivant des fonctionnalités » ; je me demande pourquoi il faut des « préfixes » pour activer et désactiver des fonctionnalités
Est-ce pour basculer dynamiquement des fonctionnalités sans entrer dans le BIOS ?
Le préfixe REP est le plus courant : il sert à répéter une même instruction un nombre variable de fois
Le nombre de répétitions est pris dans le registre CX, ce qui permet de rendre très courtes des boucles courantes, comme déplacer des objets en mémoire
La fonction memcpy est souvent inlinée sous la forme d’une seule instruction REP MOVS, avec au besoin une instruction qui copie le compteur dans CX
Le préfixe REX est lui aussi assez courant, parce que les programmes 64 bits manipulent fréquemment des valeurs et des adresses 64 bits
Aucun préfixe ne bascule quelque chose qui pourrait être configuré globalement via le BIOS ou autre ; ils ne font tous que préciser ce que doit faire l’instruction suivante
Les modes d’adressage rarement utilisés portent un « préfixe de segment » pour utiliser un segment autre que DS, et le préfixe « REX » de x86_64 ajoute des bits aux champs de registres afin de permettre l’utilisation de 16 registres généraux
De même, le préfixe « LOCK », même si sa spécification d’origine était médiocre, rend certaines opérations mémoire atomiques vis-à-vis du reste du système, par exemple pour implémenter un test-and-set avec « LOCK CMPXCHG »
D’autres architectures CPU expriment aussi ce genre d’opérations, mais les placent généralement dans l’espace d’instructions existant, ce qui nécessite davantage de bits pour représenter toutes les instructions
Le préfixe « REP » mis en cause ici est un cas à part : c’est un préfixe de répétition microcodé hérité de temps très anciens
Il représente néanmoins des opérations encore critiques pour les performances aujourd’hui, comme memset/memmove, donc les fabricants de CPU ont intérêt à continuer à les optimiser, et ce bug semble être apparu dans ce processus
Le plus gros problème est qu’il a « utilisé efficacement » l’espace d’encodage des instructions
Quand de nouvelles instructions, et pire encore des registres supplémentaires, sont arrivés plus tard, il a fallu caser d’une manière ou d’une autre de nouvelles variantes d’instructions, et la solution a été d’ajouter des préfixes
À chaque extension du jeu d’instructions, une partie de l’espace des opcodes a été évidée pour y fourrer un nouveau préfixe
Vu qu’Intel a proposé encore une nouvelle méthode cette année, cette tendance semble appelée à continuer
Le processus de diagnostic me rappelle ce qui s’est passé quand qemu a rencontré repz ret : https://repzret.org/p/repzret/
À mon avis, les règles de HN devraient interdire ce genre de titre
Il ne dit absolument pas de quoi parle le lien, et l’URL embrouille encore plus
Avec un titre aussi dénué de sens, je pense que l’auteur de la soumission devrait ajouter une brève description
J’ai déjà vu que quand le titre contient un maximum de contexte, les gens ne cliquent pas sur le lien et se contentent, dans les commentaires, de peaufiner leurs centres d’intérêt comme s’ils réagissaient à un tweet
HN choisit un juste milieu qui encourage la curiosité intellectuelle et le clic sur les liens
Même si un titre ambigu pousse certains à refuser de cliquer, au moins ils répondent à ceux qui ont cliqué, ce qui est mieux que sur le reste d’Internet
Les articles dont la récompense ne justifie pas un titre ambigu et spirituel descendent de la une, contrairement à celui-ci
L’article est très bien écrit
Je ne connais presque rien à la programmation assembleur ni au jeu d’instructions Intel, et encore moins à la microarchitecture, mais j’ai pu suivre l’explication et j’ai l’impression d’avoir compris dans les grandes lignes ce qui se passe ici
Je me demande si quelqu’un sait si les CPU AMD sont aussi touchés
Si le problème est réellement que le processeur se trompe sur la longueur des instructions, il est impressionnant que cela puisse être corrigé par microcode sans grosse perte de performances
Mon intuition est peut-être complètement fausse, mais j’aurais pensé que le calcul de la longueur des instructions était synthétisé directement en portes logiques
En y repensant, le décodeur d’uOP est peut-être matériellement correct, tandis qu’une routine de copie optimisée en microcode tente d’inférer quelque chose de faux à propos du flux d’uOP
Par exemple : « ah, c’est un rep mov, donc pour la boucle il suffit de revenir deux uOP en arrière »
L’équipe CPU d’Intel ne publiera sans doute pas les détails
Je ne connais pas bien « ERMS » et « FSRM », et Google ne semble presque pas fournir de bonnes ressources à ce sujet
Je me demande s’il s’agit simplement de flags CPUID indiquant qu’on peut utiliser rep movsb à pleine performance au lieu d’une implémentation SSE optimisée de memcpy, ou bien d’un encodage ou d’un préfixe spécial qui rend rep movsb plus rapide
Si c’est la seconde option, je ne comprends pas pourquoi c’est nécessaire, ni comment fsrm est exploité
ERMS semble avoir été une alternative moins coûteuse à AVX, et FSRM une meilleure version pour les blocs courts
« Les versions d’entrée de gamme des processeurs ultérieurs, les Celeron et Pentium Kaby Lake sortis en 2017, n’ont pas AVX pour les copies mémoire rapides, mais disposent d’Enhanced REP MOVSB
Et certaines architectures mobiles et basse consommation d’Intel sorties après 2018 n’étaient pas basées sur SkyLake, mais copient avec REP MOVSB environ deux fois plus d’octets par cycle CPU que les microarchitectures de la génération précédente »
« Avant Fast Short REP MOV(FSRM) de la microarchitecture Ice Lake, Enhanced REP MOVSB(ERMSB) n’était plus rapide qu’une copie AVX ou qu’une copie via registres généraux que lorsque la taille du bloc était d’au moins 256 octets
Pour les blocs de moins de 64 octets, le coût de démarrage interne d’ERMSB, d’environ 35 cycles, était élevé et le rendait beaucoup plus lent ; la fonctionnalité FSRM visait à rendre rapides aussi les blocs de moins de 128 octets »
[1] https://stackoverflow.com/a/43837564
[2] http://www.intel.com/content/dam/www/public/us/en/documents/...
Le choix optimal des instructions et de leur ordonnancement peut se faire statiquement à la compilation, ou dynamiquement à l’exécution en choisissant l’une de plusieurs fonctions de bibliothèque, ou via JIT
Pour détecter à l’exécution quel ordonnancement d’instructions est optimal, il faut connaître le CPU réel
On pourrait maintenir une table de tous les modèles de CPU, mais on peut aussi demander au système d’exploitation si le CPU en cours d’exécution implémente cette optimisation
Linux avait besoin d’un patch pour pouvoir signaler que le CPU implémentait cette optimisation
https://www.phoronix.com/news/Intel-5.6-FSRM-Memmove
J’ai vu que l’avis d’Intel [1] disait ceci
Intel remercie les employés d’Intel qui ont découvert ce problème en interne, ainsi que les employés de Google qui l’ont signalé
[1] https://www.intel.com/content/www/us/en/security-center/advi...
L’avis d’Intel avec la description de l’impact mérite aussi d’être consulté : https://www.intel.com/content/www/us/en/security-center/advi...
« Sur certains processeurs Intel(R), une séquence d’instructions processeur peut provoquer un comportement inattendu, permettant potentiellement à un utilisateur authentifié disposant d’un accès local d’obtenir une élévation de privilèges, une divulgation d’informations ou un déni de service »
Konrad Magnusson, de l’équipe Victoria 3 chez Paradox Interactive, a trouvé quelque chose lié à cela et à mimalloc : https://github.com/microsoft/mimalloc/issues/807
Je ne sais pas si c’est totalement lié, mais c’est possible