Mémoire d'exécution seule (XOM) pour les binaires AArch64

Les sections de code exécutables pour les binaires du système AArch64 sont par défaut marquées en exécution seule (non lisible) afin de renforcer l'atténuation des attaques de réutilisation de code juste à temps. Le code qui mélange les données et le code et le code qui inspecte délibérément ces sections (sans remapper au préalable les segments de mémoire comme lisibles) ne fonctionnent plus. Les applications avec un SDK cible de 10 (niveau d'API 29 ou supérieur) sont concernées si l'application tente de lire des sections de code de bibliothèques système activées par la mémoire d'exécution seule (XOM) en mémoire sans marquer au préalable la section comme lisible.

Pour bénéficier pleinement de cette atténuation, la prise en charge du matériel et du noyau est requise. Sans ce soutien, les mesures d’atténuation pourraient n’être appliquées que partiellement. Le noyau commun Android 4.9 contient les correctifs appropriés pour fournir une prise en charge complète sur les appareils ARMv8.2.

Mise en œuvre

Les binaires AArch64 générés par le compilateur supposent que le code et les données ne sont pas mélangés. L'activation de cette fonctionnalité n'affecte pas négativement les performances de l'appareil.

Pour le code qui doit effectuer une introspection intentionnelle de la mémoire sur ses segments exécutables, il est conseillé d'appeler mprotect sur les segments de code nécessitant une inspection pour leur permettre d'être lisibles, puis de supprimer la lisibilité une fois l'inspection terminée.
Cette implémentation provoque des lectures dans des segments de mémoire marqués comme étant en exécution seule, ce qui entraîne une erreur de segmentation ( SEGFAULT ). Cela peut se produire à la suite d’un bug, d’une vulnérabilité, de données mélangées à du code (regroupement littéral) ou d’une introspection intentionnelle de la mémoire.

Prise en charge et impact des appareils

Les appareils dotés d'un matériel antérieur ou de noyaux antérieurs (inférieurs à 4.9) sans les correctifs requis peuvent ne pas prendre entièrement en charge ou bénéficier de cette fonctionnalité. Les appareils sans prise en charge du noyau peuvent ne pas imposer les accès des utilisateurs à la mémoire d'exécution uniquement, mais le code du noyau qui vérifie explicitement si une page est lisible peut toujours appliquer cette propriété, comme process_vm_readv() .

L'indicateur du noyau CONFIG_ARM64_UAO doit être défini dans le noyau pour garantir que le noyau respecte les pages utilisateur marquées en exécution uniquement. Les appareils ARMv8 antérieurs, ou les appareils ARMv8.2 avec le remplacement de l'accès utilisateur (UAO) désactivé, peuvent ne pas en bénéficier pleinement et peuvent toujours être en mesure de lire les pages d'exécution uniquement à l'aide d'appels système.

Refactoriser le code existant

Le code porté depuis AArch32 peut contenir des données et du code mélangés, ce qui entraîne des problèmes. Dans de nombreux cas, résoudre ces problèmes est aussi simple que de déplacer les constantes vers une section .data du fichier assembly.

L’assemblage manuscrit devra peut-être être refactorisé pour séparer les constantes regroupées localement.

Exemples:

Les binaires générés par le compilateur Clang ne devraient avoir aucun problème avec les données mélangées dans le code. Si le code généré par la collection du compilateur GNU (GCC) est inclus (à partir d'une bibliothèque statique), inspectez le binaire de sortie pour vous assurer que les constantes n'ont pas été regroupées dans des sections de code.

Si une introspection du code est nécessaire sur les sections de code exécutables, appelez d'abord mprotect pour marquer le code lisible. Ensuite, une fois l'opération terminée, appelez à nouveau mprotect pour le marquer comme illisible.

Activation

L'exécution seule est activée par défaut pour tous les binaires 64 bits du système de build.

Désactivation

Vous pouvez désactiver l'exécution uniquement au niveau d'un module, pour une arborescence de sous-répertoires entière ou globalement pour une build entière.

XOM peut être désactivé pour les modules individuels qui ne peuvent pas être refactorisés ou qui doivent lire leur code exécutable, en définissant les variables LOCAL_XOM et xom sur false .

// Android.mk
LOCAL_XOM := false

// Android.bp
cc_binary { // or other module types
   ...
   xom: false,
}

Si la mémoire d'exécution seule est désactivée dans une bibliothèque statique, le système de construction l'applique à tous les modules dépendants de cette bibliothèque statique. Vous pouvez remplacer cela en utilisant xom: true, .

Pour désactiver la mémoire d'exécution seule dans un sous-répertoire particulier (par exemple, foo/bar/), transmettez la valeur à XOM_EXCLUDE_PATHS .

make -j XOM_EXCLUDE_PATHS=foo/bar

Vous pouvez également définir la variable PRODUCT_XOM_EXCLUDE_PATHS dans la configuration de votre produit.

Vous pouvez désactiver globalement les binaires à exécution seule en passant ENABLE_XOM=false à votre commande make .

make -j ENABLE_XOM=false

Validation

Aucun test CTS ou de vérification n'est disponible pour la mémoire d'exécution seule. Vous pouvez vérifier manuellement les binaires en utilisant readelf et en vérifiant les indicateurs de segment.