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 associe des métadonnées supplémentaires à chaque allocation/désallocation de mémoire. Il attribue un tag à un emplacement de mémoire, qui peut ensuite être associé à des pointeurs qui font référence à cet emplacement de mémoire. Lors de l'exécution, le processeur vérifie que le pointeur et les tags de métadonnées correspondent à chaque chargement et enregistrement.
Dans Android 12, l'allocateur de mémoire de tas du noyau et de l'espace utilisateur peut augmenter chaque allocation avec des 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é au niveau de la mémoire dans nos bases de code.
Modes de fonctionnement avec MTE
MTE comporte trois modes de fonctionnement :
- Mode synchrone (SYNC)
- Mode asynchrone (ASYNC)
- Mode asymétrique (ASYMM)
Mode synchrone (SYNC)
Ce mode est optimisé pour la détection correcte 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 également 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.
Lorsqu'il s'exécute en mode SYNC, l'allocateur Android enregistre les traces de pile pour toutes les allocations et désallocations, et les utilise pour fournir de meilleurs rapports d'erreur qui incluent une explication d'une erreur de mémoire, telle que l'utilisation après libération ou le dépassement de mémoire tampon, ainsi que les traces de pile des événements de mémoire concernés. 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 (par exemple, 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)
Le mode MTE asymétrique, une fonctionnalité supplémentaire d'Arm v8.7-A, permet une vérification synchrone des lectures de mémoire et une vérification asynchrone des écritures de mémoire, avec des performances semblables à 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 du mode ASYNC chaque fois qu'il est disponible.
Pour cette raison, aucune des API décrites ci-dessous ne mentionne le mode asymétrique. Au lieu de cela, l'OS peut être configuré pour toujours utiliser le mode asymétrique lorsque le mode asynchrone est demandé. Pour en savoir plus, veuillez consulter la section "Configurer le niveau MTE préféré spécifique au processeur".
MTE dans l'espace utilisateur
Les sections suivantes décrivent 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é par le paramètre 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. Le paramètre est ignoré pour les bibliothèques ou toute cible qui n'est ni exécutable ni un test.
1. Activer MTE dans Android.bp
(exemple) pour un projet spécifique :
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 produit :
Mode MTE | Liste d'inclusion | 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 semblable à 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 des propriétés système
Les paramètres de compilation ci-dessus peuvent être remplacés au moment de l'exécution en définissant la propriété système suivante :
arm64.memtag.process.<basename> = (off|sync|async)
où basename
correspond au nom de base de l'exécutable.
Par exemple, pour définir /system/bin/ping
ou /data/local/tmp/ping
sur l'utilisation de 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 pour les processus natifs (non-applications) 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. Toutefois, les applications qui souhaitent utiliser MTE 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 utiliser les modifications de 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
).
Vous trouverez ces modifications sous System > Advanced > Developer options
> App Compatibility Changes
dans le menu des paramètres généraux. 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 les bugs de sécurité de la mémoire de manière précoce et offre une couverture utilisateur réaliste, si elle est activée dans les versions 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 issues 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 du mode 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 la détection des erreurs des vérifications plus strictes sans les inconvénients en termes 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 valeur "sync" doit être écrite dans l'entrée sysfs
/sys/devices/system/cpu/cpu<N>/mte_tcf_preferred
au moment du démarrage. Pour ce faire, utilisez un script d'initialisation. Par exemple, pour configurer les processeurs 0 à 1 afin qu'ils exécutent les processus en mode ASYNCHRONOUS en mode SYNCHRONOUS, et les processeurs 2 à 3 afin qu'ils s'exécutent en mode ASYMMETRICAL, les éléments suivants peuvent être ajoutés à la clause init 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 fichiers Tombstone des processus en mode ASYNC exécutés en mode SYNC contiendront une trace de pile précise de l'emplacement de l'erreur de mémoire. Toutefois, elles n'incluront 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)
où level
est égal à 0 ou 1.
Désactive l'initialisation de la mémoire dans malloc et évite de modifier les tags de mémoire
sauf si cela est nécessaire pour la correction.
int mallopt(M_MEMTAG_TUNING, level)
où level
correspond à :
M_MEMTAG_TUNING_BUFFER_OVERFLOW
M_MEMTAG_TUNING_UAF
Sélectionne la stratégie d'attribution des tags.
- Le paramètre par défaut est
M_MEMTAG_TUNING_BUFFER_OVERFLOW
. M_MEMTAG_TUNING_BUFFER_OVERFLOW
: permet la détection déterministe des bugs de dépassement et de dépassement négatif de tampon linéaire en attribuant des valeurs de tag 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 tag possibles sont disponibles pour chaque emplacement de mémoire. Veuillez noter que MTE ne peut pas détecter les débordements dans le même granule de tag (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 la cause d'une corruption de la mémoire, car la mémoire d'un granule n'est jamais utilisée pour plusieurs allocations.M_MEMTAG_TUNING_UAF
: active les tags aléatoires indépendants pour une probabilité uniforme d'environ 93 % de détecter 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 souhaiter connaître les informations suivantes :
- La définition du registre matériel
PSTATE.TCO
peut temporairement supprimer la vérification des balises (exemple). Par exemple, lorsque vous copiez une plage de mémoire dont le contenu de la balise est inconnu ou lorsque vous résolvez un goulot d'étranglement des performances dans une boucle chaude. - Lorsque vous utilisez
M_HEAP_TAGGING_LEVEL_SYNC
, le gestionnaire d'erreur système fournit des informations supplémentaires, telles que des traces de pile d'allocation et de désallocation. Cette fonctionnalité nécessite d'accéder aux bits de balise. Pour l'activer, transmettez l'indicateurSA_EXPOSE_TAGBITS
lors de la définition du gestionnaire de signaux. Il est recommandé à tout programme qui définit son propre gestionnaire de signaux et délègue les plantages inconnus à celui du système de faire 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
.
Cela peut être contrôlé au moment du 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 asynchrone (par défaut :sync
).kasan.stacktrace=[on|off]
: indique si les traces de pile doivent être collectées (par défaut :on
).- La collecte des traces de pile nécessite également
stack_depot_disable=off
.
- La collecte des traces de pile nécessite également
kasan.fault=[report|panic]
: indique si le rapport doit uniquement être imprimé ou si le noyau doit également paniquer (par défaut :report
). Quelle que soit cette option, la vérification des tags est désactivée après la première erreur signalée.
Utilisation recommandé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 du système de compilation. Dans ce mode, les bugs sont détectés tôt dans le processus de développement, la base de code est stabilisée plus rapidement et le coût de la détection des bugs plus tard en production est évité.
Nous vous recommandons vivement d'utiliser le mode ASYNC en production. Il s'agit d'un outil à faible coût permettant de détecter la présence de bugs de sécurité de la mémoire dans un processus, ainsi que d'une défense en profondeur supplémentaire. Une fois un bug 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 Asymm présente généralement les mêmes caractéristiques de performances que le mode ASYNC et est presque toujours préférable. Les petits cœurs dans l'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 les bugs des utilisateurs finaux. Pour en savoir plus sur le débogage du code natif Android, consultez cette page.
Composants de plate-forme compatibles avec MTE
Dans Android 12, un certain nombre de composants système essentiels à la 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 :
- Utilitaires et daemons réseau (à l'exception de
netd
) - Bluetooth, SecureElement, HAL NFC et applications système
- Daemon
statsd
system_server
zygote64
(pour permettre aux applications d'utiliser 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 n'a pas)
- Traite des entrées non fiables (règle des deux)
- Ralentissement acceptable des performances (le ralentissement ne crée pas de latence visible par l'utilisateur)
Nous encourageons les fournisseurs à activer MTE en production pour davantage de composants, en respectant les critères mentionnés ci-dessus. Pendant le développement, nous vous recommandons de tester ces composants en mode SYNC pour détecter les bugs faciles à corriger et évaluer l'impact du mode 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 futures conceptions matérielles.