AddressSanitizer

AddressSanitizer (ASan) es una herramienta rápida basada en compiladores para detectar errores de memoria en código nativo.

ASan detecta:

  • Desabastecimiento de búfer de pila
  • Uso de pila después de liberación
  • Uso de pila fuera del alcance
  • Cierre doble o cierre wild

ASan se ejecuta en ARM de 32 y 64 bits, además de x86 y x86-64. La sobrecarga de la CPU de ASan es de aproximadamente el doble, el tamaño del código aumenta entre el 50% y el doble, y la sobrecarga de la memoria es considerable (depende de tus patrones de asignación, pero es de aproximadamente el doble).

Android 10 y la rama de versión más reciente del AOSP en AArch64 admiten Hardware-assisted AddressSanitizer (HWASan), una herramienta similar con una menor sobrecarga de RAM y un mayor rango de errores detectados. HWASan detecta el uso de pila después de la devolución, además de los errores que detecta ASan.

HWASan tiene una sobrecarga de CPU y tamaño del código similar, pero una sobrecarga de RAM mucho menor (15%). HWASan no es determinístico. Solo hay 256 valores de etiquetas posibles, por lo que hay una probabilidad plana del 0.4% de no detectar ningún error. HWASan no tiene las zonas rojas de tamaño limitado de ASan para detectar desbordamientos ni la cuarentena de capacidad limitada para detectar el uso después de la liberación, por lo que no importa para HWASan qué tan grande sea el desbordamiento ni hace cuánto tiempo se desasignó la memoria. Esto hace que HWASan sea mejor que ASan. Puedes leer más sobre el diseño de HWASan o sobre el uso de HWASan en Android.

ASan detecta desbordamientos de pila o globales, además de los desbordamientos de montón, y es rápido con una sobrecarga de memoria mínima.

En este documento, se describe cómo compilar y ejecutar partes o todo Android con ASan. Si compilas una app para SDK o NDK con ASan, consulta Address Sanitizer.

Cómo limpiar ejecutables individuales con ASan

Agrega LOCAL_SANITIZE:=address o sanitize: { address: true } a la regla de compilación del ejecutable. Puedes buscar en el código ejemplos existentes o encontrar los otros filtros disponibles.

Cuando se detecta un error, ASan imprime un informe detallado en la salida estándar y en logcat, y luego falla el proceso.

Cómo limpiar bibliotecas compartidas con ASan

Debido a la forma en que funciona ASan, una biblioteca compilada con ASan solo puede usarse con un ejecutable compilado con ASan.

Para sanear una biblioteca compartida que se usa en varios ejecutables, no todos compilados con ASan, necesitas dos copias de la biblioteca. La forma recomendada de hacerlo es agregar lo siguiente a Android.mk para el módulo en cuestión:

LOCAL_SANITIZE:=address
LOCAL_MODULE_RELATIVE_PATH := asan

Esto coloca la biblioteca en /system/lib/asan en lugar de /system/lib. Luego, ejecuta tu archivo ejecutable con el siguiente comando:

LD_LIBRARY_PATH=/system/lib/asan

Para los daemons del sistema, agrega lo siguiente a la sección correspondiente de /init.rc o /init.$device$.rc.

setenv LD_LIBRARY_PATH /system/lib/asan

Verifica que el proceso use bibliotecas de /system/lib/asan cuando estén presentes leyendo /proc/$PID/maps. Si no es así, es posible que debas inhabilitar 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.

Mejores seguimientos de pila

ASan usa un desenredador rápido basado en punteros de marco para registrar un seguimiento de pila para cada evento de asignación y desasignación de memoria en el programa. La mayor parte de Android se compila sin punteros de marco. Como resultado, a menudo solo obtienes uno o dos fotogramas significativos. Para corregir este problema, vuelve a compilar la biblioteca con ASan (recomendado) o con lo siguiente:

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

O bien, configura ASAN_OPTIONS=fast_unwind_on_malloc=0 en el entorno de procesamiento. Esta última puede consumir mucha CPU, según la carga.

Simbología

Inicialmente, los informes de ASan contienen referencias a desplazamientos en bibliotecas compartidas y archivos binarios. Existen dos formas de obtener información sobre el archivo fuente y la línea:

  • Asegúrate de que el objeto binario llvm-symbolizer esté presente en /system/bin. llvm-symbolizer se compila a partir de fuentes en third_party/llvm/tools/llvm-symbolizer.
  • Filtra el informe a través de la secuencia de comandos external/compiler-rt/lib/asan/scripts/symbolize.py.

El segundo enfoque puede proporcionar más datos (es decir, ubicaciones de file:line) debido a la disponibilidad de bibliotecas simbolizadas en el host.

ASan en apps

ASan no puede ver el código Java, pero sí puede detectar errores en las bibliotecas de JNI. Para ello, debes compilar el ejecutable con ASan, que, en este caso, es /system/bin/app_process(32|64). Esto habilita ASan en todas las apps del dispositivo al mismo tiempo, lo que genera una carga pesada, pero un dispositivo con 2 GB de RAM debería poder controlarlo.

Agrega LOCAL_SANITIZE:=address a la regla de compilación app_process en frameworks/base/cmds/app_process.

Edita la sección service zygote del archivo system/core/rootdir/init.zygote(32|64).rc correspondiente para agregar las siguientes líneas al bloque de líneas con sangría que contiene class main, también con la misma sangría:

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

Compila, sincroniza con adb, escribe en la memoria flash con fastboot y reinicia.

Cómo usar la propiedad de ajuste

El enfoque de la sección anterior coloca ASan en cada app del sistema (en realidad, en cada descendiente del proceso Zygote). Es posible ejecutar solo una (o varias) apps con ASan, lo que implica un mayor uso de memoria a cambio de un inicio más lento de la app.

Para ello, inicia tu app con la propiedad wrap.. En el siguiente ejemplo, se ejecuta la app de Gmail con ASan:

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

En este contexto, asanwrapper reescribe /system/bin/app_process como /system/bin/asan/app_process, que se compila con ASan. También agrega /system/lib/asan al comienzo de la ruta de búsqueda de la biblioteca dinámica. De esta manera, se prefieren las bibliotecas instrumentadas con ASan de /system/lib/asan a las bibliotecas normales de /system/lib cuando se ejecutan con asanwrapper.

Si se encuentra un error, la app falla y el informe se imprime en el registro.

SANITIZE_TARGET

Android 7.0 y versiones posteriores incluyen compatibilidad para compilar toda la plataforma de Android con ASan a la vez. (Si compilas una versión superior a Android 9, HWASan es una mejor opción).

Ejecuta los siguientes comandos en el mismo árbol de compilación.

make -j42
SANITIZE_TARGET=address make -j42

En este modo, userdata.img contiene bibliotecas adicionales y también se debe escribir en la memoria flash del dispositivo. Usa la siguiente línea de comandos:

fastboot flash userdata && fastboot flashall

Esto compila dos conjuntos de bibliotecas compartidas: normales en /system/lib (la primera invocación de make) y con ASan en /data/asan/lib (la segunda invocación de make). Los ejecutables de la segunda compilación reemplazan a los de la primera. Los ejecutables instrumentados con ASan obtienen una ruta de búsqueda de biblioteca diferente que incluye /data/asan/lib antes de /system/lib a través del uso de /system/bin/linker_asan en PT_INTERP.

El sistema de compilación sobrescribe los directorios de objetos intermedios cuando cambia el valor de $SANITIZE_TARGET. Esto fuerza una recompilación de todos los destinos y conserva los archivos binarios instalados en /system/lib.

Algunos destinos no se pueden compilar con ASan:

  • Ejecutables vinculados de forma estática
  • LOCAL_CLANG:=false objetivos
  • Los LOCAL_SANITIZE:=false no están ASan'd para SANITIZE_TARGET=address

Los ejecutables como estos se omiten en la compilación de SANITIZE_TARGET, y la versión de la primera invocación de make se deja en /system/bin.

Las bibliotecas como esta se compilan sin ASan. Pueden contener código de ASan de las bibliotecas estáticas de las que dependen.

Documentación de respaldo