Qu'est-ce que Adresse de retour ?

Adresse à laquelle revenir une fois la fonction terminée. En RISC-V, l'instruction jal place l'adresse de l'instruction suivante dans ra (x1) ; en x86, l'instruction call l'empile.

Nom japonais: リターンアドレス

« Adresse de retour » apparaît dans 10 questions d'entraînement réparties sur 4 thèmes, dont ISA et assembleur, CPU et architecture, Éditeurs de liens, chargeurs et exécutables.

Domaine: ISA et assembleur

Comment un appel la sauvegarde

Un appel doit enregistrer l'endroit où l'exécution doit reprendre, c'est-à-dire l'adresse de l'instruction qui suit l'appel. En RISC-V, jal (jump and link) écrit PC + 4 dans un registre, par convention ra (x1), puis effectue le saut ; le retour se fait par ret, une pseudo-instruction équivalant à jalr x0, 0(ra). Les appels via des pointeurs de fonction, des fonctions virtuelles et des stubs de bibliothèques partagées utilisent jalr avec une cible dans un registre. En x86, call empile l'adresse de retour et ret la dépile dans le pointeur d'instruction : la pile la contient donc toujours.

Fonctions feuilles et non feuilles

Comme chaque jal écrase ra, une fonction RISC-V qui appelle d'autres fonctions doit d'abord sauvegarder sa propre adresse de retour. Un prologue typique est addi sp, sp, -16 pour allouer un cadre sur la pile, qui croît vers le bas, puis sw ra, 12(sp) et sw s0, 8(sp) pour sauvegarder ra et un registre préservé par l'appelé. L'épilogue les recharge, restaure sp et exécute ret. Une fonction feuille, qui n'appelle rien, peut laisser ra dans son registre et n'a parfois besoin d'aucun cadre de pile, ce qui rend les petites fonctions peu coûteuses.

Pourquoi les attaquants la ciblent

Une adresse de retour stockée sur la pile se trouve à côté des tampons locaux : un dépassement de tampon peut donc l'écraser et détourner l'exécution au retour de la fonction. Les attaques classiques écrivaient du code machine sur la pile puis y sautaient ; les politiques W^X, avec le bit de non-exécution de la table des pages, bloquent cela. Les attaquants se sont alors tournés vers la programmation orientée retour (ROP), qui enchaîne des fragments de code existants. D'autres défenses incluent les canaris de pile vérifiés avant le retour, la randomisation de l'espace d'adressage (ASLR) et les piles fantômes (shadow stacks) matérielles, qui conservent une seconde copie protégée de chaque adresse de retour.

Les adresses de retour dans le matériel

Les CPU à exécution dans le désordre prédisent la destination d'un retour bien avant que ra ou la pile ne soient lus. Une pile d'adresses de retour (RAS) dans le prédicteur de branchement empile l'adresse suivante à chaque appel et la dépile à chaque retour, ce qui donne des prédictions très précises pour du code bien imbriqué. Les exceptions et les interruptions sont différentes : le CPU sauvegarde le PC interrompu dans un registre dédié, comme mepc ou sepc en RISC-V, avec le mode de privilège précédent, et mret ou sret les restaure tous en une fois, au lieu d'un simple ret.

S'entraîner : questions sur Adresse de retour

Quelle zone est généralement utilisée pour sauvegarder temporairement l'adresse de retour, entre autres, lors de l'appel d'un sous-programme (fonction) ?

  1. ALa pile
  2. BLes étiquettes du cache
  3. CUne zone fixe de la ROM
Afficher la réponse et l'explication

Réponse : A. La pile

À l'appel d'un sous-programme, l'adresse de retour, les valeurs de registres à sauvegarder et les variables locales sont empilées. La pile est une structure où le dernier élément entré sort le premier (LIFO), ce qui permet de revenir dans le bon ordre même avec des appels imbriqués ou récursifs. Le sommet est indiqué par le pointeur de pile. Une récursion trop profonde épuise la zone et provoque un débordement de pile, et certaines attaques réécrivent l'adresse de retour par un dépassement de tableau.

Questions fréquentes

Où est stockée l'adresse de retour en RISC-V ?

Dans le registre ra (x1), écrit par jal ou jalr lors de l'appel. Une fonction non feuille la copie dans son cadre de pile avant d'effectuer ses propres appels et la restaure avant ret.

Quelle différence entre call/ret en x86 et jal/ret en RISC-V ?

En x86, call empile toujours l'adresse de retour et ret la dépile, tandis qu'en RISC-V, jal la place dans un registre et ne touche la mémoire que si la fonction choisit de la sauvegarder.

Pourquoi écraser l'adresse de retour est-il dangereux ?

Au retour de la fonction, le CPU saute à l'adresse qui s'y trouve : un attaquant qui l'écrase par un dépassement de tampon peut donc prendre le contrôle du programme.

« Adresse de retour » dans les explications

Questions mentionnant « Adresse de retour » (10)

  1. Quelle zone est généralement utilisée pour sauvegarder temporairement l'adresse de retour, entre autres, lors de l'appel d'un sous-programme (fonction) ?
  2. Que contient le compteur ordinal (PC) du processeur ?
  3. Quelle propriété du registre x0 (zero) de RISC-V est correcte ?
  4. Quel est le fonctionnement correct de l'instruction RISC-V « jal ra, func » ?
  5. Dans une fonction qui n'appelle aucune autre fonction (fonction feuille), il n'est souvent pas nécessaire de sauvegarder ra sur la pile. Quelle en est la raison ?
  6. Quel est l'objectif de l'exécution de « addi sp, sp, -16 » au début d'une fonction RISC-V ?
  7. Le code ci-dessus est le début (prologue) d'une fonction. Que fait cette partie ?
  8. En RISC-V, quelle instruction réalise généralement un appel par pointeur de fonction en C, « (*fp)(x) » ?
  9. Quelle attaque est plus facile à prévenir en n'autorisant jamais simultanément « inscriptible » et « exécutable » sur une page mémoire (W^X, séparation écriture-exécution) ?
  10. Pour revenir au programme initial après le traitement d’une interruption ou d’une exception, pourquoi utiliser une instruction de retour dédiée (sret, mret, etc. en RISC-V) plutôt que l’instruction de retour de fonction ordinaire ?

Termes associés

Cadre de pile (stack frame) Débordement (spill) Interruption Pointeur de pile Convention d'appel Pseudo-instruction Fonction feuille Architecture du jeu d'instructions (ISA) Tas (heap) Bascule (flip-flop)

Retour au glossaire