web-dev-qa-db-fra.com

Qu'est-ce qu'une rétine et comment ça marche?

Afin de limiter la divulgation de mémoire dans le noyau ou entre processus (l’attaque Spectre ), le noyau Linux1 sera compilé avec une nouvelle option , -mindirect-branch=thunk-extern introduit dans gcc pour effectuer des appels indirects par le biais d’une rétine .

Cela semble être un terme nouvellement inventé puisqu’une recherche sur Google n’est utilisée que très récemment (généralement en 2018).

Qu'est-ce qu'une rétine et comment empêche-t-elle les attaques récentes de divulgation d'informations dans le noyau?


1 Ce n'est pas spécifique à Linux, cependant - une construction similaire ou identique semble être utilisée dans le cadre de stratégies d'atténuation sur d'autres systèmes d'exploitation.

229
BeeOnRope

L'article mentionné par sgbj dans les commentaires de Paul Turner, de Google, explique ce qui suit de manière beaucoup plus détaillée, mais je vais essayer:

Pour autant que je puisse rassembler ces informations à partir des informations limitées pour le moment, une répline est un trampoline de retour qui utilise une boucle infinie qui n'est jamais exécutée. empêche le processeur de spéculer sur la cible d'un saut indirect.

L'approche de base peut être vue dans branche du noyau d'Andi Kleen traitant de cette question:

Il introduit le nouvel appel __x86.indirect_thunk qui charge l'appel cible dont l'adresse mémoire (que j'appellerai ADDR) est stockée au sommet de la pile et exécute le saut à l'aide d'un l'instruction RET. Le thunk lui-même est ensuite appelé à l'aide de la macro NOSPEC_JMP/CALL , utilisée pour remplacer de nombreux (sinon tous) appels et sauts indirects. La macro place simplement la cible de l'appel sur la pile et définit l'adresse de retour correctement, si nécessaire (notez le flux de contrôle non linéaire):

_.macro NOSPEC_CALL target
    jmp     1221f            /* jumps to the end of the macro */
1222:
    Push    \target          /* pushes ADDR to the stack */
    jmp __x86.indirect_thunk /* executes the indirect jump */
1221:
    call    1222b            /* pushes the return address to the stack */
.endm
_

Le placement de call à la fin est nécessaire pour que, lorsque l'appel indirect est terminé, le flux de contrôle continue après l'utilisation de la macro _NOSPEC_CALL_, de sorte qu'il puisse être utilisé à la place d'un call

Le thunk lui-même ressemble à ceci:

_    call retpoline_call_target
2:
    lfence /* stop speculation */
    jmp 2b
retpoline_call_target:
    lea 8(%rsp), %rsp 
    ret
_

Le flux de contrôle peut être un peu déroutant ici, alors laissez-moi préciser:

  • call place le pointeur d'instruction en cours (label 2) sur la pile.
  • lea ajoute 8 au pointeur de pile, en ignorant le quadword le plus récemment poussé, qui est la dernière adresse de retour (pour le libellé 2). Ensuite, le sommet de la pile pointe à nouveau sur l'adresse de retour réelle ADDR.
  • ret passe à _*ADDR_ et réinitialise le pointeur de pile au début de la pile d'appels.

En fin de compte, tout ce comportement équivaut pratiquement à sauter directement à _*ADDR_. L’avantage est que le prédicteur de branche utilisé pour les instructions de retour (Return Stack Buffer, RSB) lors de l’exécution de l’instruction call suppose que l’instruction correspondante ret passe à l’étiquette 2.

La partie après l'étiquette 2 n'est en réalité jamais exécutée, il s'agit simplement d'une boucle infinie qui, en théorie, remplirait le pipeline d'instructions avec des instructions JMP. En utilisant LFENCE, PAUSE ou plus généralement, une instruction provoquant le blocage du pipeline d'instructions empêche la CPU de perdre de la puissance et du temps lors de cette exécution spéculative. En effet, dans le cas où l'appel à retpoline_call_target retournerait normalement, la LFENCE serait la prochaine instruction à exécuter. C’est aussi ce que le prédicteur de branche prédira en fonction de l’adresse de retour initiale (l’étiquette 2).

Pour citer le manuel d'architecture d'Intel:

Les instructions qui suivent un LFENCE peuvent être extraites de la mémoire avant le LFENCE, mais elles ne seront pas exécutées avant la fin du processus.

Notez cependant que la spécification ne mentionne jamais que LFENCE et PAUSE provoquent le blocage du pipeline. Je lis donc un peu entre les lignes ici.

Revenons maintenant à votre question initiale: la divulgation d’informations sur la mémoire du noyau est possible grâce à la combinaison de deux idées:

  • Même si l'exécution spéculative devrait être exempte d'effets secondaires lorsque la spéculation était fausse, l'exécution spéculative affecte toujours la hiérarchie du cache. Cela signifie que lorsqu'un chargement de mémoire est exécuté de manière spéculative, une ligne de cache doit toujours être expulsée. Cette modification de la hiérarchie du cache peut être identifiée en mesurant soigneusement le temps d'accès à la mémoire mappé sur le même ensemble de cache.
    Vous pouvez même perdre des bits de mémoire arbitraire lorsque l’adresse source de la mémoire lue a elle-même été lue à partir de la mémoire du noyau.

  • Le prédicteur de branche indirecte des processeurs Intel n'utilise que les 12 bits les plus bas de l'instruction source. Il est donc facile d'empoisonner les 2 ^ 12 historiques de prédiction possibles avec des adresses de mémoire contrôlées par l'utilisateur. Celles-ci peuvent alors, lorsque le saut indirect est prédit dans le noyau, être exécutées de manière spéculative avec les privilèges du noyau. En utilisant le canal latéral de synchronisation du cache, vous pouvez ainsi libérer de la mémoire du noyau arbitraire.

UPDATE: Sur la liste de diffusion du noya , une discussion en cours m'amène à penser que les stratégies de réponse ne permettent pas de résoudre complètement les problèmes de prédiction de branche, comme lorsque la pile de retour Le tampon (RSB) fonctionne à vide, les architectures Intel les plus récentes (Skylake +) se rabattent sur le tampon vulnérable Branch Branch (BTB):

Retpoline, en tant que stratégie d'atténuation, remplace les branches indirectes par des retours afin d'éviter d'utiliser des prédictions issues du BTB, celles-ci pouvant être empoisonnées par un attaquant. Le problème avec Skylake + est qu’un sous-débit RSB revient à utiliser une prédiction BTB, ce qui permet à l’attaquant de prendre le contrôle de la spéculation.

151
Tobias Ribizel

Un retpoline est conçu pour protéger contre l'exploit d'injection de cible de branche ( CVE-2017-5715 ). Il s'agit d'une attaque dans laquelle une instruction de branche indirecte dans le noyau est utilisée pour forcer l'exécution spéculative d'un bloc de code arbitraire. Le code choisi est un "gadget" qui est en quelque sorte utile aux attaquants. Par exemple, le code peut être choisi de manière à laisser filtrer les données du noyau en affectant le cache. La rétpoline empêche cet exploit en remplaçant simplement toutes les instructions de branchement indirect par une instruction de retour.

Je pense que la clé de la répline est simplement la partie "ret", qui remplace la branche indirecte par une instruction de retour, de sorte que le processeur utilise le prédicteur de pile de retour au lieu du prédicteur de branche exploitable. Si une simple instruction Push et une instruction de retour étaient utilisées à la place, le code qui serait exécuté de manière spéculative serait le code sur lequel la fonction retournera de toute façon, pas un gadget utile à l'attaquant. Le principal avantage de la partie trampoline semble être de maintenir la pile de retour afin que, lorsque la fonction retourne à l'appelant, cela soit correctement prédit.

L'idée de base derrière l'injection de cible de branche est simple. Il tire parti du fait que la CPU n'enregistre pas l'adresse complète de la source et de la destination des branches dans les mémoires tampons de ses branches. L’attaquant peut donc remplir la mémoire tampon à l’aide de sauts dans son propre espace d’adresse, ce qui entraînera des succès de prédiction lorsqu’un saut indirect particulier sera exécuté dans l’espace du noyau.

Notez que retpoline n'empêche pas directement la divulgation d'informations dans le noyau, il empêche uniquement l'utilisation d'instructions de branche indirectes pour exécuter de façon spéculative un gadget susceptible de divulguer des informations. Si l'attaquant parvient à trouver un autre moyen d'exécuter le gadget de manière spéculative, la réplication n'empêche pas l'attaque.

Le papier Spectre Attacks: Exploitation de l'exécution spéculative par Paul Kocher, Daniel Genkin, Daniel Gruss, Werner Haas, Mike Hamburg, Moritz Lipp, Stefan Mangard, Thomas Prescher, Michael Schwarz et Yuval Yarom donne le survol suivant de la façon dont les branches indirectes peuvent être exploitées:

Exploitation de branches indirectes. Dessin tiré de la programmation orientée retour (ROP), l’attaquant choisit dans cette méthode un gadget à partir de l'espace d'adressage de la victime et influence celle-ci à exécuter le gadget de manière spéculative. Contrairement à ROP, l'attaquant ne s'appuie pas sur une vulnérabilité du code victime. Au lieu de cela, l'attaquant entraîne le BTB (Branch Target Buffer) pour prédire de manière erronée une branche d'une instruction de branche indirecte à l'adresse du gadget, ce qui entraîne une exécution spéculative du gadget. Alors que les instructions spéculativement exécutées sont abandonnées, leurs effets sur le cache ne sont pas annulés. Le gadget peut utiliser ces effets pour divulguer des informations sensibles. Nous montrons comment, avec une sélection minutieuse d'un gadget, cette méthode peut être utilisée pour lire de la mémoire arbitraire de la victime.

Pour éviter le BTB, l’attaquant trouve l’adresse virtuelle du gadget dans l’espace adresse de la victime, puis effectue des branchements indirects vers cette adresse. Cette formation s’effectue à partir de l’espace adresse de l’attaquant. Peu importe ce qui réside à l’adresse du gadget dans l’espace adresse de l’attaquant; il suffit que la branche utilisée pour former les branches utilise la même adresse virtuelle de destination. (En fait, tant que l'attaquant gère les exceptions, l'attaque peut fonctionner même s'il n'y a pas de code mappé à l'adresse virtuelle du gadget dans l'espace d'adressage de l'attaquant.) Il n'est pas non plus nécessaire de faire correspondre l'adresse source de la branche utilisée pour la formation et l’adresse de la branche visée. Ainsi, l'attaquant dispose d'une grande souplesse pour mettre en place l'entraînement.

Une entrée de blog intitulée Lecture de la mémoire privilégiée avec un canal latéral par l'équipe Project Zero de Google fournit un autre exemple de la manière dont l'injection de cible de branche peut être utilisée pour créer un exploit.

45
Ross Ridge

Cette question a été posée il y a un moment et mérite une réponse plus récente.

Résumé :

Les séquences "Retpoline" sont une construction logicielle qui permet d’isoler les branches indirectes d’une exécution spéculative. Cela peut être appliqué pour protéger les fichiers binaires sensibles (tels que les implémentations de système d'exploitation ou d'hypervisor) des attaques par injection de cible de branche contre leurs branches indirectes.

Le mot " ret poline " est un portemantea des mots "return" et "trampoline ", un peu comme l'amélioration" rel poline "a été créé à partir de" appel relatif "et" trampoline ". Il s’agit d’une construction de trampoline construite à l’aide d’opérations de retour assurant de manière figurative que toute exécution spéculative associée "rebondira" indéfiniment.

Afin de limiter la divulgation de mémoire dans le noyau ou entre processus (l’attaque de Spectre), le noyau Linux [1] sera compilé avec une nouvelle option, -mindirect-branch=thunk-extern introduite dans gcc pour effectuer des appels indirects par l’intermédiaire d’une ligne appelée "retpoline".

[1] Ce n'est pas spécifique à Linux, cependant - une construction similaire ou identique semble être utilisée dans le cadre des stratégies d'atténuation sur d'autres systèmes d'exploitation.

L'utilisation de cette option du compilateur uniquement protège contre Spectre V2 dans les processeurs concernés disposant de la mise à jour du microcode requise pour CVE-2017-. 5715. Il utilisera 'work' sur n'importe quel code (pas seulement un noyau), mais seul le code contenant "secrets" mérite d'être attaqué.

Cela semble être un terme nouvellement inventé, car une recherche sur Google n’est utilisée que très récemment (généralement en 2018).

Le compilateur LLVM a eu un commutateur -mretpoline depuis avant le 4 janvier 2018 . Cette date correspond au moment où la vulnérabilité était première publication publique . GCC a mis ses correctifs à disposition 7 janvier 2018.

La date CVE suggère que la vulnérabilité était 'découverte' en 2017, mais qu'elle affecte certains des processeurs fabriqués au cours des deux dernières décennies (elle a donc probablement été découverte il y a longtemps).

Qu'est-ce qu'une rétine et comment empêche-t-elle les attaques récentes de divulgation d'informations dans le noyau?

Premièrement, quelques définitions:

  • Trampoline - Parfois appelés vecteurs de saut indirect, les trampolines sont des emplacements mémoire contenant des adresses pointant vers des routines de service d'interruption, des routines d'E/S, etc. le terme trampoline. GCC a traditionnellement pris en charge les fonctions imbriquées en créant un trampoline exécutable au moment de l'exécution, lorsque l'adresse d'une fonction imbriquée est utilisée. C'est un petit morceau de code qui réside normalement dans la pile, dans le cadre de pile de la fonction contenant. Le trampoline charge le registre de chaîne statique puis saute à l'adresse réelle de la fonction imbriquée.

  • Thunk - Un thunk est un sous-programme utilisé pour injecter un calcul supplémentaire dans un autre sous-programme. Les Thunks sont principalement utilisés pour retarder un calcul jusqu'à ce que le résultat soit nécessaire, ou pour insérer des opérations au début ou à la fin de l'autre sous-routine.

  • Memoization - Une fonction mémoisée "se souvient" des résultats correspondant à un ensemble d'entrées spécifiques. Les appels suivants avec des entrées mémorisées renvoient le résultat mémorisé au lieu de le recalculer, éliminant ainsi le coût primaire d'un appel avec des paramètres donnés de tout sauf du premier appel passé à la fonction avec ces paramètres.

Très approximativement, une rétine est un trampoline avec un retourne en tant que thunk , en 'gâcher 'mémoization dans le prédicteur de branche indirecte.

Source : La réplique inclut une instruction PAUSE pour Intel, mais une instruction LFENCE est nécessaire pour AMD car, sur ce processeur, l’instruction PAUSE n’est pas une instruction de sérialisation. La boucle pause/jmp utilise donc une puissance excessive. il est spéculé sur l'attente d'un retour imprévisible à la bonne cible.

Arstechnica a une explication simple du problème:

"Chaque processeur a un comportement architectural (le comportement documenté qui décrit le fonctionnement des instructions et sur lequel les programmeurs écrivent pour écrire leurs programmes) et un comportement microarchitural (le comportement d'une implémentation réelle de l'architecture). Ceux-ci peuvent diverger de manière subtile. Par exemple, sur le plan architectural, un programme qui charge une valeur d’une adresse particulière en mémoire attendra que l’adresse soit connue avant d’essayer de la charger.Merciellement, le processeur peut tenter de deviner de façon spéculative l’adresse pour pouvoir la démarrer. charger la valeur de la mémoire (ce qui est lent) avant même qu'il soit absolument certain de l'adresse à utiliser.

Si le processeur devine mal, il ignorera la valeur estimée et effectuera le chargement à nouveau, cette fois avec l'adresse correcte. Le comportement défini par l'architecture est ainsi préservé. Mais cette hypothèse erronée perturbera d'autres parties du processeur, en particulier le contenu du cache. Ces perturbations microarchitecturales peuvent être détectées et mesurées en chronométrant le temps nécessaire pour accéder aux données qui devraient (ou ne devraient pas) être dans le cache, permettant ainsi à un programme malveillant de faire des déductions sur les valeurs stockées en mémoire. ".

Du papier d'Intel: " Retpoline: Une atténuation d'injection de cible de branche " ( . PDF ):

"Une séquence de rétpoline empêche l'exécution spéculative du processeur d'utiliser le" prédicteur de branche indirecte "(un moyen de prédire le flux du programme) pour spéculer sur une adresse contrôlée par un exploit (élément satisfaisant 4 des cinq éléments d'injection de cible de branche (variante Spectre 2 ) exploiter la composition susmentionnée). ".

Remarque, l'élément 4 est le suivant: "L'exploit doit réussir à influencer cette branche indirecte de manière à prédire de manière spéculative et à exécuter un gadget. Ce gadget, choisi par l'exploit, laisse filtrer les données secrètes via un canal latéral, généralement par synchronisation dans le cache.".

7
Rob