Par défaut, les sections de code exécutable des binaires système AArch64 sont marquées comme exécutables uniquement (non lisibles) afin de renforcer la protection contre les attaques de réutilisation de code juste-à-temps. Le code qui mélange des données et du code, et le code qui inspecte intentionnellement ces sections (sans remapper d'abord les segments de mémoire en tant que lisibles) ne fonctionnent plus. Les applications dont le SDK cible est défini sur 10 (niveau d'API 29 ou version ultérieure) sont concernées si elles tentent de lire des sections de code de bibliothèques système compatibles avec la mémoire XOM (mémoire en lecture seule) en mémoire sans marquer au préalable la section comme lisible.
Pour profiter pleinement de cette atténuation, une compatibilité matérielle et du noyau est requise. Sans cette prise en charge, la mitigation ne sera peut-être appliquée que partiellement. Le kernel commun Android 4.9 contient les correctifs appropriés pour assurer une compatibilité complète avec les appareils ARMv8.2.
Implémentation
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'a pas d'incidence négative sur les performances de l'appareil.
Pour le code qui doit effectuer une introspection de mémoire intentionnelle sur ses segments exécutables, il est conseillé d'appeler mprotect
sur les segments de code nécessitant une inspection pour les rendre lisibles, puis de supprimer la lisibilité une fois l'inspection terminée.
Cette implémentation entraîne une erreur de segmentation (SEGFAULT
) lors des lectures dans des segments de mémoire marqués comme "exécuter uniquement". Cela peut se produire en raison d'un bug, d'une faille, de données mélangées à du code (pool de littéraux) ou d'une introspection de la mémoire intentionnelle.
Compatibilité et impact sur les appareils
Les appareils équipés de matériel ou de noyaux antérieurs (version antérieure à 4.9) sans les correctifs requis peuvent ne pas être entièrement compatibles avec cette fonctionnalité ou ne pas en bénéficier. Les appareils sans prise en charge du noyau peuvent ne pas appliquer les accès utilisateur à la mémoire en lecture seule. Toutefois, 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 kernel CONFIG_ARM64_UAO
doit être défini dans le kernel pour s'assurer qu'il respecte les pages de l'espace utilisateur marquées comme exécutables uniquement. Les appareils ARMv8 antérieurs ou les appareils ARMv8.2 avec la fonctionnalité de forçage de l'accès utilisateur (UAO) désactivée peuvent ne pas en profiter pleinement et peuvent toujours lire des pages en lecture seule à l'aide de syscalls.
Refactorer le code existant
Le code porté depuis AArch32 peut contenir des données et du code mélangés, ce qui peut entraîner des problèmes. Dans de nombreux cas, la résolution de ces problèmes est aussi simple que de déplacer les constantes vers une section .data
dans le fichier d'assemblage.
L'assemblage manuscrit peut nécessiter une refactorisation pour séparer les constantes groupées localement.
Exemples :
Les binaires générés par le compilateur Clang ne devraient pas poser de problème de mélange de données dans le code. Si du code généré par la collection de compilateurs 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 l'introspection du code est nécessaire sur les sections de code exécutable, appelez d'abord mprotect
pour marquer le code comme lisible. Une fois l'opération terminée, appelez à nouveau mprotect
pour la marquer comme illisible.
Activer XOM
L'exécution en lecture seule est activée par défaut pour tous les binaires 64 bits du système de compilation.
Désactiver XOM
Vous pouvez désactiver l'exécution en mode "execute-only" au niveau d'un module, d'un arbre de sous-répertoires entier ou globalement pour l'ensemble d'un build.
XOM peut être désactivé pour des 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 en mode "exécution uniquement" est désactivée dans une bibliothèque statique, le système de compilation l'applique à tous les modules dépendants de cette bibliothèque statique. Vous pouvez remplacer cette valeur à l'aide de xom: true,
.
Pour désactiver la mémoire en lecture 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 les binaires exécutables uniquement de manière globale en transmettant ENABLE_XOM=false
à votre commande make
.
make -j ENABLE_XOM=false
Validation
Aucun test CTS ou de validation n'est disponible pour la mémoire en mode "exécuter uniquement". Vous pouvez vérifier manuellement les binaires à l'aide de readelf
et en vérifiant les indicateurs de segment.