DirecciónDesinfectante

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

ASan detecta:

  • Desbordamiento/subdesbordamiento del búfer de pila y montón
  • Uso del montón después de gratis
  • Uso de pila fuera del alcance
  • Doble gratis/wild gratis

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 aproximadamente 2x, la sobrecarga del tamaño del código está entre 50% y 2x, y una gran sobrecarga de memoria (depende de sus patrones de asignación, pero del orden de 2x).

Android 10 y la rama maestra de AOSP en AArch64 admiten ASan acelerado por hardware (HWASan) , una herramienta similar con una sobrecarga de RAM más baja y una gama más amplia de errores detectados. HWASan detecta el uso de la pila después de la devolución, además de los errores detectados por ASan.

HWASan tiene una sobrecarga de tamaño de código y CPU similar, pero una sobrecarga de RAM mucho menor (15%). HWASan no es determinista. 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 uso después de liberar, por lo que no le importa a HWASan cuán grande es el desbordamiento o cuánto tiempo hace que se desasignó la memoria. Esto hace que HWASan sea mejor que ASan. Puede leer más sobre el diseño de HWASan o sobre el uso de HWASan en Android .

ASan detecta desbordamientos de pila/globales además de desbordamientos de almacenamiento dinámico, y es rápido con una sobrecarga de memoria mínima.

Este documento describe cómo compilar y ejecutar partes o la totalidad de Android con ASan. Si está creando una aplicación SDK/NDK con ASan, consulte Desinfectante de direcciones en su lugar.

Desinfección de ejecutables individuales con ASan

Agregue LOCAL_SANITIZE:=address o sanitize: { address: true } a la regla de compilación para el ejecutable. Puede buscar en el código ejemplos existentes o encontrar otros desinfectantes disponibles.

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

Desinfectando bibliotecas compartidas con ASan

Debido a la forma en que funciona ASan, una biblioteca creada con ASan solo puede ser utilizada por un ejecutable creado con ASan.

Para desinfectar una biblioteca compartida que se usa en varios ejecutables, no todos creados con ASan, necesita dos copias de la biblioteca. La forma recomendada de hacer esto 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, ejecute su ejecutable con:

LD_LIBRARY_PATH=/system/lib/asan

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

setenv LD_LIBRARY_PATH /system/lib/asan

Verifique que el proceso esté usando bibliotecas de /system/lib/asan cuando esté presente leyendo /proc/$PID/maps . Si no es así, es posible que deba deshabilitar 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 rastros de pila

ASan utiliza un desbobinador rápido basado en un puntero de cuadro para registrar un seguimiento de la pila para cada evento de asignación y desasignación de memoria en el programa. La mayor parte de Android está construido sin punteros de marco. Como resultado, a menudo obtiene solo uno o dos marcos significativos. Para solucionar esto, reconstruya la biblioteca con ASan (¡recomendado!), o con:

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

O establezca ASAN_OPTIONS=fast_unwind_on_malloc=0 en el entorno del proceso. Este último puede hacer un uso intensivo de la CPU, dependiendo de la carga.

Simbolización

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

  • Asegúrese de que el binario llvm-symbolizer esté presente en /system/bin . llvm-symbolizer se crea a partir de fuentes en third_party/llvm/tools/llvm-symbolizer .
  • Filtre el informe a través del script external/compiler-rt/lib/asan/scripts/symbolize.py .

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

ASan en aplicaciones

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

Agregue LOCAL_SANITIZE:=address a la regla de compilación app_process en frameworks/base/cmds/app_process . Ignore el objetivo app_process__asan en el mismo archivo por ahora (si todavía está allí en el momento de leer esto).

Edite la sección de service zygote de servicio del archivo system/core/rootdir/init.zygote( 32|64 ).rc para agregar las siguientes líneas al bloque de líneas sangradas que contienen class main , también sangradas por la misma cantidad:

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

Compilación, sincronización adb, arranque flash fastboot y reinicio.

Usando la propiedad de envoltura

El enfoque de la sección anterior coloca ASan en todas las aplicaciones del sistema (en realidad, en todos los descendientes del proceso Zygote). Es posible ejecutar solo una (o varias) aplicaciones con ASan, cambiando parte de la sobrecarga de memoria por un inicio más lento de la aplicación.

Esto se puede hacer iniciando su aplicación con el wrap. propiedad. El siguiente ejemplo ejecuta la aplicación Gmail bajo 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 a /system/bin/asan/app_process , que está construido con ASan. También agrega /system/lib/asan al comienzo de la ruta de búsqueda de la biblioteca dinámica. De esta forma, se prefieren las bibliotecas instrumentadas con ASan de /system/lib/asan a las bibliotecas normales en /system/lib cuando se ejecuta con asanwrapper .

Si se encuentra un error, la aplicación falla y el informe se imprime en el registro.

SANITIZE_TARGET

Android 7.0 y superior incluye soporte para construir toda la plataforma Android con ASan a la vez. (Si está creando una versión superior a Android 9, HWASan es una mejor opción).

Ejecute 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 debe actualizarse en el dispositivo. Utilice la siguiente línea de comando:

fastboot flash userdata && fastboot flashall

Esto crea dos conjuntos de bibliotecas compartidas: normal en /system/lib (la primera invocación de make) y ASan instrumentada en /data/asan/lib (la segunda invocación de make). Los ejecutables de la segunda compilación sobrescriben los de la primera compilación. Los ejecutables instrumentados por ASan obtienen una ruta de búsqueda de biblioteca diferente que incluye /data/asan/lib antes /system/lib mediante el uso de /system/bin/linker_asan en PT_INTERP .

El sistema de compilación golpea los directorios de objetos intermedios cuando el valor $SANITIZE_TARGET ha cambiado. Esto fuerza una reconstrucción de todos los objetivos mientras se conservan los archivos binarios instalados en /system/lib .

Algunos objetivos no se pueden construir con ASan:

  • Ejecutables vinculados estáticamente
  • LOCAL_CLANG:=false
  • LOCAL_SANITIZE:=false no son ASan'd para SANITIZE_TARGET=address

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

Bibliotecas como esta se construyen sin ASan. Pueden contener algún código ASan de las bibliotecas estáticas de las que dependen.

Documentación de apoyo