AddressSanitizer

AddressSanitizer (ASan) est un outil rapide basé sur un compilateur qui permet de détecter les bugs liés à la mémoire dans le code natif.

ASan détecte les bugs suivants :

  • Débordement positif/négatif de la pile et du tampon de tas de mémoire
  • Bugs "use-after-free" au niveau des tas de mémoire
  • Utilisation de la pile en dehors du champ d'application
  • Bugs de type "double free"/"wild free"

ASan fonctionne sur les versions ARM 32 bits et 64 bits, ainsi que sur x86 et x86-64. La surcharge processeur d'ASan est plus ou moins multipliée par deux, la surcharge de la taille du code est comprise entre 50 % et deux fois supérieure, et la surcharge de la mémoire est élevée (en fonction de vos modèles d'allocation, mais de l'ordre de deux fois supérieure).

Android 10 et la dernière branche de version AOSP sur AArch64 sont compatibles avec Hardware-assisted AddressSanitizer (HWASan), un outil similaire avec une surcharge de RAM inférieure et une plus grande variété de bugs détectés. HWASan détecte l'utilisation de la pile après le retour, en plus des bugs détectés par ASan.

HWASan présente une surcharge processeur et de taille du code similaire, mais une surcharge de RAM beaucoup plus faible (15 %). HWASan est non déterministe. Il n'existe que 256 valeurs de tag possibles.La probabilité de manquer un bug est donc de 0, 4 %. HWASan ne dispose pas des zones rouges de taille limitée d'ASan pour détecter les dépassements de capacité ni de la quarantaine à capacité limitée pour détecter les erreurs d'utilisation après libération. La taille du dépassement de capacité ou l'ancienneté de la désallocation de la mémoire n'ont donc pas d'importance pour HWASan. HWASan est donc plus performant qu'ASan. Pour en savoir plus sur la conception de HWASan ou sur l'utilisation de HWASan sur Android, consultez les pages correspondantes.

ASan détecte les dépassements de pile/globaux en plus des dépassements de tas de mémoire. Il est rapide et n'entraîne qu'une surcharge de mémoire minimale.

Ce document explique comment compiler et exécuter tout ou partie d'Android avec ASan. Si vous créez une application SDK/NDK avec ASan, consultez plutôt Address Sanitizer.

Assainir des exécutables individuels avec ASan

Ajoutez LOCAL_SANITIZE:=address ou sanitize: { address: true } à la règle de compilation de l'exécutable. Vous pouvez rechercher des exemples existants dans le code ou trouver les autres outils de désinfection disponibles.

Lorsqu'un bug est détecté, ASan imprime un rapport détaillé dans la sortie standard et dans logcat, puis plante le processus.

Assainir les bibliothèques partagées avec ASan

En raison du fonctionnement d'ASan, une bibliothèque créée avec ASan ne peut être utilisée que par un exécutable créé avec ASan.

Pour assainir une bibliothèque partagée utilisée dans plusieurs exécutables, dont certains ne sont pas créés avec ASan, vous avez besoin de deux copies de la bibliothèque. La méthode recommandée consiste à ajouter les éléments suivants à Android.mk pour le module en question :

LOCAL_SANITIZE:=address
LOCAL_MODULE_RELATIVE_PATH := asan

La bibliothèque est alors placée dans /system/lib/asan au lieu de /system/lib. Exécutez ensuite votre fichier exécutable avec la commande suivante :

LD_LIBRARY_PATH=/system/lib/asan

Pour les daemons système, ajoutez les éléments suivants à la section appropriée de /init.rc ou /init.$device$.rc.

setenv LD_LIBRARY_PATH /system/lib/asan

Vérifiez que le processus utilise les bibliothèques de /system/lib/asan, le cas échéant, en lisant /proc/$PID/maps. Si ce n'est pas le cas, vous devrez peut-être désactiver SELinux :

adb root
adb shell setenforce 0
# restart the process with adb shell kill $PID
# if it is a system service, or may be adb shell stop; adb shell start.

Meilleures traces de la pile

ASan utilise un dérouleur rapide basé sur un pointeur de frame pour enregistrer une trace de la pile pour chaque événement d'allocation et de désallocation de mémoire dans le programme. La plupart des éléments d'Android sont conçus sans pointeurs de frame. Par conséquent, vous n'obtenez souvent qu'une ou deux images pertinentes. Pour résoudre ce problème, reconstruisez la bibliothèque avec ASan (recommandé) ou avec :

LOCAL_CFLAGS:=-fno-omit-frame-pointer
LOCAL_ARM_MODE:=arm

Vous pouvez également définir ASAN_OPTIONS=fast_unwind_on_malloc=0 dans l'environnement de processus. Cette dernière peut être très gourmande en ressources processeur, selon la charge.

Symbolisation

Au départ, les rapports ASan contiennent des références à des décalages dans les binaires et les bibliothèques partagées. Il existe deux façons d'obtenir des informations sur les fichiers sources et les lignes :

  • Assurez-vous que le fichier binaire llvm-symbolizer est présent dans /system/bin. llvm-symbolizer est créé à partir des sources de third_party/llvm/tools/llvm-symbolizer.
  • Filtrez le rapport à l'aide du script external/compiler-rt/lib/asan/scripts/symbolize.py.

La deuxième approche peut fournir plus de données (c'est-à-dire file:line emplacements) en raison de la disponibilité des bibliothèques symbolisées sur l'hôte.

ASan dans les applications

ASan ne peut pas voir dans le code Java, mais il peut détecter les bugs dans les bibliothèques JNI. Pour ce faire, vous devez compiler l'exécutable avec ASan, qui dans ce cas est /system/bin/app_process(32|64). Cela permet d'activer ASan dans toutes les applications de l'appareil en même temps, ce qui représente une charge importante, mais un appareil doté de 2 Go de RAM devrait pouvoir la gérer.

Ajoutez LOCAL_SANITIZE:=address à la règle de compilation app_process dans frameworks/base/cmds/app_process.

Modifiez la section service zygote du fichier system/core/rootdir/init.zygote(32|64).rc approprié pour ajouter les lignes suivantes au bloc de lignes indentées contenant class main, également indentées du même montant :

    setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib
    setenv ASAN_OPTIONS allow_user_segv_handler=true

Compilation, adb sync, fastboot flash boot et redémarrage.

Utiliser la propriété "wrap"

L'approche de la section précédente place ASan dans chaque application du système (en fait, dans chaque descendant du processus Zygote). Il est possible d'exécuter une ou plusieurs applications avec ASan, en échange d'une surcharge de mémoire pour un démarrage plus lent de l'application.

Pour ce faire, démarrez votre application avec la propriété wrap.. L'exemple suivant exécute l'application Gmail sous ASan :

adb root
adb shell setenforce 0  # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"

Dans ce contexte, asanwrapper réécrit /system/bin/app_process en /system/bin/asan/app_process, qui est créé avec ASan. Il ajoute également /system/lib/asan au début du chemin de recherche de la bibliothèque dynamique. De cette façon, les bibliothèques instrumentées ASan de /system/lib/asan sont préférées aux bibliothèques normales de /system/lib lors de l'exécution avec asanwrapper.

Si un bug est détecté, l'application plante et le rapport est imprimé dans le journal.

SANITIZE_TARGET

Android 7.0 et versions ultérieures permettent de créer l'intégralité de la plate-forme Android avec ASan en une seule fois. (Si vous créez une version supérieure à Android 9, HWASan est un meilleur choix.)

Exécutez les commandes suivantes dans le même arbre de compilation.

make -j42
SANITIZE_TARGET=address make -j42

Dans ce mode, userdata.img contient des bibliothèques supplémentaires et doit également être flashé sur l'appareil. Exécutez la ligne de commande suivante :

fastboot flash userdata && fastboot flashall

Cela crée deux ensembles de bibliothèques partagées : normales dans /system/lib (la première invocation make) et instrumentées ASan dans /data/asan/lib (la deuxième invocation make). Les exécutables de la deuxième compilation écrasent ceux de la première. Les exécutables instrumentés par ASan obtiennent un chemin de recherche de bibliothèque différent qui inclut /data/asan/lib avant /system/lib grâce à l'utilisation de /system/bin/linker_asan dans PT_INTERP.

Le système de compilation écrase les répertoires d'objets intermédiaires lorsque la valeur $SANITIZE_TARGET a changé. Cela force la reconstruction de toutes les cibles tout en préservant les binaires installés sous /system/lib.

Certaines cibles ne peuvent pas être créées avec ASan :

  • Exécutables associés de manière statique
  • LOCAL_CLANG:=false cibles
  • Les LOCAL_SANITIZE:=false ne sont pas ASan'd pour SANITIZE_TARGET=address

Les exécutables de ce type sont ignorés dans la compilation SANITIZE_TARGET, et la version de la première invocation make est conservée dans /system/bin.

Les bibliothèques de ce type sont créées sans ASan. Ils peuvent contenir du code ASan provenant des bibliothèques statiques dont ils dépendent.

Pièces justificatives