Extension MTE (Memory Tagging Extension) Arm

Arm v9 introduit l'extension d'ajout de balises de mémoire (MTE) Arm, une implémentation matérielle de la mémoire taguée.

De manière générale, MTE ajoute des métadonnées supplémentaires à chaque allocation/désallocation de mémoire. Il attribue une balise à un emplacement mémoire, qui peut ensuite être associé à des pointeurs qui font référence à cet emplacement mémoire. Au moment de l'exécution, le processeur vérifie que le pointeur et les balises de métadonnées correspondent à chaque chargement et stockage.

Dans Android 12, l'outil d'allocation de mémoire de tas de kernel et d'espace utilisateur peut enrichir chaque allocation de métadonnées. Cela permet de détecter les bugs d'utilisation après libération et de dépassement de mémoire tampon, qui sont la source la plus courante de bugs de sécurité de la mémoire dans nos codebases.

Modes de fonctionnement avec MTE

MTE propose trois modes de fonctionnement:

  • Mode synchrone (SYNC)
  • Mode asynchrone (ASYNC)
  • Mode asymétrique (ASYMM)

Mode synchrone (SYNC)

Ce mode est optimisé pour la précision de la détection des bugs plutôt que pour les performances. Il peut être utilisé comme un outil précis de détection de bugs quand un impact plus important sur les performances est acceptable. Lorsque ce mode est activé avec MTE, il agit comme une mesure de sécurité. En cas de non-concordance des tags, le processeur interrompt immédiatement l'exécution et met fin au processus avec SIGSEGV (code SEGV_MTESERR) et des informations complètes sur l'accès à la mémoire et l'adresse défaillante.

Nous vous recommandons d'utiliser ce mode lors des tests comme alternative à HWASan/KASAN ou en production lorsque le processus cible représente une surface d'attaque vulnérable. En outre, lorsque le mode ASYNC a indiqué la présence d'un bug, vous pouvez obtenir un rapport de bug précis à l'aide des API d'exécution pour passer l'exécution en mode SYNC.

En mode SYNC, l'allocateur Android enregistre des traces de pile pour toutes les allocations et désallocations, et les utilise pour fournir de meilleurs rapports d'erreurs incluant une explication d'une erreur de mémoire, telle qu'une erreur d'utilisation après libération ou un dépassement de mémoire tampon, ainsi que les traces de pile des événements de mémoire pertinents. Ces rapports fournissent des informations avec davantage de contexte, et permettent de suivre et de corriger les bugs plus facilement.

Mode asynchrone (ASYNC)

Ce mode est optimisé pour les performances plutôt que pour la précision des rapports de bugs. Il peut également être utilisé pour détecter les bugs de sécurité de la mémoire à faible coût.
En cas de non-concordance des tags, le processeur poursuit l'exécution jusqu'à l'entrée de noyau la plus proche (telle qu'un appel système ou une interruption de minuteur), où il met fin au processus avec SIGSEGV (code SEGV_MTEAERR) sans enregistrer l'adresse défaillante ni l'accès à la mémoire.
Nous vous recommandons d'utiliser ce mode en production sur des codebases bien testés, où la densité de bugs de sécurité liés à la mémoire est connue pour être faible (ce qui est obtenu en utilisant le mode SYNC pendant les tests).

Mode asymétrique (ASYMM)

Fonctionnalité supplémentaire d'Arm v8.7-A, le mode MTE asymétrique fournit une vérification synchrone des lectures de mémoire et une vérification asynchrone des écritures de mémoire, avec des performances similaires à celles du mode ASYNC. Dans la plupart des cas, ce mode est une amélioration par rapport au mode ASYNC. Nous vous recommandons de l'utiliser à la place d'ASYNC chaque fois qu'il est disponible.

C'est pourquoi aucune des API décrites ci-dessous ne mentionne le mode asymétrique. À la place, l'OS peut être configuré pour utiliser toujours le mode asymétrique lorsque l'option asynchrone est demandée. Pour en savoir plus, consultez la section "Configurer le niveau MTE préféré spécifique au processeur".

MTE dans l'espace utilisateur

Les sections suivantes expliquent comment activer MTE pour les processus système et les applications. MTE est désactivé par défaut, sauf si l'une des options ci-dessous est définie pour un processus particulier (voir les composants pour lesquels MTE est activé ci-dessous).

Activer MTE à l'aide du système de compilation

En tant que propriété à l'échelle du processus, MTE est contrôlée par le paramètre de temps de compilation de l'exécutable principal. Les options suivantes permettent de modifier ce paramètre pour des exécutables individuels ou pour des sous-répertoires entiers de l'arborescence source. Ce paramètre est ignoré pour les bibliothèques ou toute cible qui n'est ni exécutable ni test.

1. Activer MTE dans Android.bp (exemple) pour un projet particulier:

Mode MTE Paramètre
MTE asynchrone
  sanitize: {
  memtag_heap: true,
  }
MTE synchrone
  sanitize: {
  memtag_heap: true,
  diag: {
  memtag_heap: true,
  },
  }

ou dans Android.mk:

Mode MTE Paramètre
Asynchronous MTE LOCAL_SANITIZE := memtag_heap
Synchronous MTE LOCAL_SANITIZE := memtag_heap
LOCAL_SANITIZE_DIAG := memtag_heap

2. Activer MTE sur un sous-répertoire de l'arborescence source à l'aide d'une variable de produit:

Mode MTE Liste "Inclure" Liste d'exclusion
async PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS MEMTAG_HEAP_ASYNC_INCLUDE_PATHS PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS
Synchronisation PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS MEMTAG_HEAP_SYNC_INCLUDE_PATHS

ou

Mode MTE Paramètre
MTE asynchrone MEMTAG_HEAP_ASYNC_INCLUDE_PATHS
MTE synchrone MEMTAG_HEAP_SYNC_INCLUDE_PATHS

ou en spécifiant le chemin d'exclusion d'un exécutable:

Mode MTE Paramètre
MTE asynchrone PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS
MTE synchrone

Exemple (utilisation similaire à PRODUCT_CFI_INCLUDE_PATHS) :

  PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS=vendor/$(vendor)
  PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS=vendor/$(vendor)/projectA \
                                    vendor/$(vendor)/projectB

Activer MTE à l'aide de propriétés système

Les paramètres de compilation ci-dessus peuvent être ignorés au moment de l'exécution en définissant la propriété système suivante:

arm64.memtag.process.<basename> = (off|sync|async)

basename correspond au nom de base de l'exécutable.

Par exemple, pour définir /system/bin/ping ou /data/local/tmp/ping pour utiliser MTE asynchrone, utilisez adb shell setprop arm64.memtag.process.ping async.

Activer MTE à l'aide d'une variable d'environnement

Une autre façon de remplacer le paramètre de compilation consiste à définir la variable d'environnement: MEMTAG_OPTIONS=(off|sync|async). Si la variable d'environnement et la propriété système sont définies, la variable est prioritaire.

Activer MTE pour les applications

Si aucune valeur n'est spécifiée, MTE est désactivé par défaut, mais les applications qui souhaitent l'utiliser peuvent le faire en définissant android:memtagMode sous la balise <application> ou <process> dans le fichier AndroidManifest.xml.

android:memtagMode=(off|default|sync|async)

Lorsqu'il est défini sur la balise <application>, l'attribut affecte tous les processus utilisés par l'application et peut être ignoré pour chaque processus en spécifiant la balise <process>.

Pour effectuer des tests, vous pouvez changer la compatibilité afin de définir la valeur par défaut de l'attribut memtagMode pour une application où aucune valeur n'est spécifiée dans le fichier manifeste (ou dont la valeur est default).
se trouve sous System > Advanced > Developer options > App Compatibility Changes dans le menu des paramètres globaux. Si vous définissez NATIVE_MEMTAG_ASYNC ou NATIVE_MEMTAG_SYNC, MTE est activé pour une application particulière.
Vous pouvez également définir cela à l'aide de la commande am comme suit:

$ adb shell am compat enable NATIVE_MEMTAG_[A]SYNC my.app.name

Créer une image système MTE

Nous vous recommandons vivement d'activer MTE sur tous les binaires natifs lors du développement et de la mise en service. Cela permet de détecter rapidement les bugs de sécurité de la mémoire et de fournir une couverture utilisateur réaliste, si cette option est activée dans les builds de test.

Nous vous recommandons vivement d'activer MTE en mode synchrone sur tous les binaires natifs pendant le développement.

SANITIZE_TARGET=memtag_heap SANITIZE_TARGET_DIAG=memtag_heap m

Comme pour toute variable du système de compilation, SANITIZE_TARGET peut être utilisé comme variable d'environnement ou comme paramètre make (par exemple, dans un fichier product.mk).
Notez que cela active MTE pour tous les processus natifs, mais pas pour les applications (qui sont dérivées de zygote64) pour lesquelles MTE peut être activé en suivant les instructions ci-dessus.

Configurer le niveau MTE préféré spécifique au processeur

Sur certains processeurs, les performances de MTE en mode ASYMM ou même SYNC peuvent être similaires à celles d'ASYNC. Il est donc intéressant d'activer des vérifications plus strictes sur ces processeurs lorsqu'un mode de vérification moins strict est demandé, afin de bénéficier des avantages de détection d'erreurs des vérifications plus strictes sans les inconvénients de performances.
Par défaut, les processus configurés pour s'exécuter en mode ASYNC s'exécutent en mode ASYNC sur tous les processeurs. Pour configurer le noyau afin qu'il exécute ces processus en mode SYNC sur des processeurs spécifiques, la synchronisation des valeurs doit être écrite dans l'entrée /sys/devices/system/cpu/cpu<N>/mte_tcf_preferred de sysfs au démarrage. Vous pouvez le faire à l'aide d'un script d'initialisation. Par exemple, pour configurer les processeurs 0 à 1 pour exécuter des processus en mode ASYNC en mode SYNC et les processeurs 2 à 3 pour exécuter en mode ASYMM, vous pouvez ajouter ce qui suit à la clause d'initialisation d'un script d'initialisation du fournisseur:

  write /sys/devices/system/cpu/cpu0/mte_tcf_preferred sync
  write /sys/devices/system/cpu/cpu1/mte_tcf_preferred sync
  write /sys/devices/system/cpu/cpu2/mte_tcf_preferred asymm
  write /sys/devices/system/cpu/cpu3/mte_tcf_preferred asymm

Les pierres tombales des processus en mode ASYNC exécutés en mode SYNC contiennent une trace de pile précise de l'emplacement de l'erreur de mémoire. Toutefois, elles n'incluent pas de trace de pile d'allocation ou de désallocation. Ces traces de pile ne sont disponibles que si le processus est configuré pour s'exécuter en mode SYNC.

int mallopt(M_THREAD_DISABLE_MEM_INIT, level)

level est 0 ou 1.
Désactive l'initialisation de la mémoire dans malloc et évite de modifier les balises de mémoire, sauf si cela est nécessaire pour la correction.

int mallopt(M_MEMTAG_TUNING, level)

level correspond à:

  • M_MEMTAG_TUNING_BUFFER_OVERFLOW
  • M_MEMTAG_TUNING_UAF

Sélectionne la stratégie d'allocation des balises.

  • La valeur par défaut est M_MEMTAG_TUNING_BUFFER_OVERFLOW.
  • M_MEMTAG_TUNING_BUFFER_OVERFLOW : permet une détection déterministe des bugs de débordement et de sous-dépassement de tampon linéaire en attribuant des valeurs de balise distinctes aux allocations adjacentes. Ce mode a une probabilité légèrement réduite de détecter les bugs d'utilisation après libération, car seule la moitié des valeurs de balise possibles est disponible pour chaque emplacement de mémoire. N'oubliez pas que MTE ne peut pas détecter les débordements au sein du même granule de balise (bloc aligné sur 16 octets) et peut manquer de petits débordements, même dans ce mode. Un tel débordement ne peut pas être à l'origine de la corruption de la mémoire, car la mémoire d'un granule n'est jamais utilisée pour plusieurs allocations.
  • M_MEMTAG_TUNING_UAF : permet d'activer des balises aléatoires indépendamment pour une probabilité uniforme d'environ 93% de détecter à la fois les bugs spatiaux (dépassement de mémoire tampon) et temporels (utilisation après libération).

En plus des API décrites ci-dessus, les utilisateurs expérimentés peuvent prendre connaissance des points suivants:

  • Définir le registre matériel PSTATE.TCO peut supprimer temporairement la vérification des balises (exemple). Par exemple, lors de la copie d'une plage de mémoire avec un contenu de balise inconnu ou lors de la résolution d'un goulot d'étranglement des performances dans une boucle active.
  • Lorsque vous utilisez M_HEAP_TAGGING_LEVEL_SYNC, le gestionnaire de plantage du système fournit des informations supplémentaires, telles que des traces de pile d'allocation et de désallocation. Cette fonctionnalité nécessite l'accès aux bits de balise et est activée en transmettant l'indicateur SA_EXPOSE_TAGBITS lors de la définition du gestionnaire de signaux. Il est recommandé que tout programme qui définit son propre gestionnaire de signaux et délègue les plantages inconnus au gestionnaire système fasse de même.

MTE dans le noyau

Pour activer KASAN accéléré par MTE pour le noyau, configurez le noyau avec CONFIG_KASAN=y, CONFIG_KASAN_HW_TAGS=y. Ces configurations sont activées par défaut sur les noyaux GKI, à partir de Android 12-5.10.
Vous pouvez contrôler cela au démarrage à l'aide des arguments de ligne de commande suivants:

  • kasan=[on|off] : active ou désactive KASAN (par défaut : on)
  • kasan.mode=[sync|async] : choisissez entre le mode synchrone et le mode asynchrone (valeur par défaut : sync)
  • kasan.stacktrace=[on|off] : indique si les traces de pile doivent être collectées (par défaut : on)
    • La collecte de la trace de la pile nécessite également stack_depot_disable=off.
  • kasan.fault=[report|panic] : indique si le rapport doit être imprimé uniquement ou si le noyau doit également être mis en panique (valeur par défaut : report). Quelle que soit cette option, la vérification des balises est désactivée après la première erreur signalée.

Nous vous recommandons vivement d'utiliser le mode SYNC lors de la mise en service, du développement et des tests. Cette option doit être activée globalement pour tous les processus à l'aide de la variable d'environnement ou avec le système de compilation. Dans ce mode, les bugs sont détectés dès le début du processus de développement, le codebase est stabilisé plus rapidement et les coûts liés à la détection de bugs plus tard en production sont évités.

Nous vous recommandons vivement d'utiliser le mode ASYNC en production. Cela fournit un outil à faible coût pour détecter la présence de bugs de sécurité de la mémoire dans un processus, ainsi qu'une défense en profondeur supplémentaire. Une fois qu'un bug est détecté, le développeur peut utiliser les API d'exécution pour passer en mode SYNC et obtenir une trace de pile précise à partir d'un échantillon d'utilisateurs.

Nous vous recommandons vivement de configurer le niveau MTE préféré spécifique au processeur pour le SoC. Le mode asymétrique présente généralement les mêmes caractéristiques de performances que ASYNC et est presque toujours préférable. Les petits cœurs en ordre affichent souvent des performances similaires dans les trois modes et peuvent être configurés pour privilégier SYNC.

Les développeurs doivent vérifier la présence de plantages en consultant /data/tombstones, logcat ou en surveillant le pipeline DropboxManager du fournisseur pour détecter les bugs de l'utilisateur final. Pour en savoir plus sur le débogage du code natif Android, cliquez ici.

Composants de la plate-forme compatibles avec MTE

Dans Android 12, un certain nombre de composants système critiques de sécurité utilisent MTE ASYNC pour détecter les plantages des utilisateurs finaux et servir de couche supplémentaire de défense en profondeur. Ces composants sont les suivants:

  • Daemons et utilitaires réseau (à l'exception de netd)
  • Bluetooth, SecureElement, HAL NFC et applications système
  • Daemon statsd
  • system_server
  • zygote64 (pour permettre aux applications d'activer MTE)

Ces cibles ont été sélectionnées en fonction des critères suivants:

  • Un processus privilégié (défini comme un processus ayant accès à quelque chose que le domaine SELinux unprivileged_app ne possède pas)
  • Traite des entrées non fiables (règle de deux)
  • Ralentissement des performances acceptable (le ralentissement ne crée pas de latence visible par l'utilisateur)

Nous encourageons les fournisseurs à activer la MTE en production pour davantage de composants, conformément aux critères mentionnés ci-dessus. Lors du développement, nous vous recommandons de tester ces composants en mode SYNC, afin de détecter les bugs facilement réparables et d'évaluer l'impact d'ASYNC sur leurs performances.
À l'avenir, Android prévoit d'étendre la liste des composants système sur lesquels MTE est activé, en fonction des caractéristiques de performances des conceptions matérielles à venir.