Desinfectantes LLVM

LLVM, la infraestructura del compilador utilizada para compilar Android, contiene varios componentes que realizan análisis estáticos y dinámicos. De estos componentes, los desinfectantes, específicamente AddressSanitizer y UndefinedBehaviorSanitizer, se pueden usar ampliamente para analizar Android. Los desinfectantes son componentes de instrumentación basados ​​en compiladores contenidos en external/compiler-rt que se pueden usar durante el desarrollo y las pruebas para eliminar errores y mejorar Android. El conjunto actual de desinfectantes de Android puede descubrir y diagnosticar muchos errores de uso indebido de memoria y comportamiento indefinido potencialmente peligroso.

Se recomienda que las compilaciones de Android arranquen y se ejecuten con desinfectantes habilitados, como AddressSanitizer y UndefinedBehaviorSanitizer. Esta página presenta AddressSanitizer, UndefinedBehaviorSanitizer y KernelAddressSanitizer, muestra cómo se pueden usar dentro del sistema de compilación de Android y brinda ejemplos de archivos Android.mk y Android.bp que compilan componentes nativos con estos desinfectantes habilitados.

DirecciónDesinfectante

AddressSanitizer (ASan) es una capacidad de instrumentación basada en compilador que detecta muchos tipos de errores de memoria en código C/C++ en tiempo de ejecución. ASan puede detectar muchas clases de errores de memoria, que incluyen:

  • Acceso a memoria fuera de los límites
  • Doble gratis
  • uso después de libre

Android permite la instrumentación ASan en el nivel de compilación completa y el nivel de la aplicación con asanwrapper.

AddressSanitizer combina la instrumentación de todas las llamadas a funciones relacionadas con la memoria, incluidas alloca, malloc y free, y el relleno de todas las variables y regiones de memoria asignadas con memoria que desencadena una devolución de llamada ASan cuando se lee o se escribe.

La instrumentación permite que ASan detecte errores de uso de memoria no válidos, incluidos el alcance doble libre y el uso posterior, el retorno y la liberación, mientras que el relleno de la región de la memoria detecta lecturas o escrituras fuera de los límites. Si se produce una lectura o escritura en esta región de relleno, ASan la detecta y genera información para ayudar a diagnosticar la violación de la memoria, incluida la pila de llamadas, el mapa de memoria oculta, el tipo de violación de la memoria, lo que se leyó o escribió, la instrucción que causó la falla. violación, y el contenido de la memoria.

pixel-xl:/ # sanitizer-status                                                                                            
=================================================================
==14164==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x0032000054b0 at pc 0x005df16ffc3c bp 0x007fc236fdf0 sp 0x007fc236fdd0
WRITE of size 1 at 0x0032000054b0 thread T0
    #0 0x5df16ffc3b in test_crash_malloc sanitizer-status/sanitizer-status.c:36:13
    #1 0x5df17004e3 in main sanitizer-status/sanitizer-status.c:76:7
    #2 0x794cf665f3 in __libc_init (/system/lib64/libc.so+0x1b5f3)
    #3 0x5df16ffa53 in do_arm64_start (/system/bin/sanitizer-status+0xa53)

0x0032000054b0 is located 0 bytes to the right of 32-byte region [0x003200005490,0x0032000054b0)
allocated by thread T0 here:
    #0 0x794d0bdc67 in malloc (/system/lib64/libclang_rt.asan-aarch64-android.so+0x74c67)
    #1 0x5df16ffb47 in test_crash_malloc sanitizer-status/sanitizer-status.c:34:25
    #2 0x5df17004e3 in main sanitizer-status/sanitizer-status.c:76:7
    #3 0x794cf665f3 in __libc_init (/system/lib64/libc.so+0x1b5f3)
    #4 0x5df16ffa53 in do_arm64_start (/system/bin/sanitizer-status+0xa53)
    #5 0x794df78893  (<unknown module>)

SUMMARY: AddressSanitizer: heap-buffer-overflow sanitizer-status/sanitizer-status.c:36:13 in test_crash_malloc

A veces, el proceso de descubrimiento de errores puede parecer no determinista, especialmente para los errores que requieren una configuración especial o técnicas más avanzadas, como preparación de montón o explotación de condiciones de carrera. Muchos de estos errores no son evidentes de inmediato y podrían aparecer miles de instrucciones lejos de la violación de la memoria que fue la causa raíz real. ASan instrumenta todas las funciones relacionadas con la memoria y rellena los datos con áreas a las que no se puede acceder sin activar una devolución de llamada de ASan. Esto significa que las violaciones de la memoria se detectan en el instante en que ocurren, en lugar de esperar a que se produzca una corrupción que provoque un bloqueo. Esto es extremadamente útil en el descubrimiento de errores y el diagnóstico de causa raíz.

Para verificar que ASAN funciona en un dispositivo de destino, Android ha incluido el ejecutable asan_test. El ejecutable asan_test prueba y valida la funcionalidad ASAN en un dispositivo de destino, proporcionando mensajes de diagnóstico con el estado de cada prueba. Cuando se utiliza una compilación de Android ASAN, se encuentra en /data/nativetest/asan_test/asan_test o /data/nativetest64/asan_test/asan_test de forma predeterminada.

UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer (UBSan) realiza instrumentación en tiempo de compilación para verificar varios tipos de comportamiento indefinido. Si bien UBSan es capaz de detectar muchos comportamientos indefinidos , Android admite alineación, bool, límites, enumeración, desbordamiento de emisión flotante, división flotante por cero, división entera por cero, atributo no nulo, nulo, retorno, devuelve atributo no nulo, base de desplazamiento, exponente de desplazamiento, desbordamiento de entero con signo, inalcanzable, desbordamiento de entero sin signo y límite de VLA. El desbordamiento de enteros sin firmar, aunque técnicamente no es un comportamiento indefinido, se incluye en el desinfectante y se usa en muchos módulos de Android, incluidos los componentes del servidor de medios, para eliminar cualquier vulnerabilidad latente de desbordamiento de enteros.

Implementación

En el sistema de compilación de Android, puede habilitar UBSan global o localmente. Para habilitar UBSan globalmente, configure SANITIZE_TARGET en Android.mk. Para habilitar UBSan a nivel de módulo, configure LOCAL_SANITIZE y especifique los comportamientos indefinidos que desea buscar en Android.mk. Por ejemplo:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS := -std=c11 -Wall -Werror -O0

LOCAL_SRC_FILES:= sanitizer-status.c

LOCAL_MODULE:= sanitizer-status

LOCAL_SANITIZE := alignment bounds null unreachable integer
LOCAL_SANITIZE_DIAG := alignment bounds null unreachable integer

include $(BUILD_EXECUTABLE)

El sistema de compilación de Android aún no admite diagnósticos tan detallados en archivos blueprint como lo hace en archivos MAKE. Aquí está el equivalente más cercano escrito como modelo (Android.bp):

cc_binary {

    cflags: [
        "-std=c11",
        "-Wall",
        "-Werror",
        "-O0",
    ],

    srcs: ["sanitizer-status.c"],

    name: "sanitizer-status",

    sanitize: {
        misc_undefined: [
            "alignment",
            "bounds",
            "null",
            "unreachable",
            "integer",
        ],
        diag: {
            undefined : true
        },
    },

}

Atajos de UBSan

Android también tiene dos accesos directos, integer y default-ub , para habilitar un conjunto de desinfectantes al mismo tiempo. integer habilita integer-divide-by-zero , signed-integer-overflow y el desbordamiento unsigned-integer-overflow . default-ub habilita las comprobaciones que tienen problemas mínimos de rendimiento del compilador: bool, entero-divide-by-zero, return, return-nonnull-attribute, shift-exponent, unreachable y vla-bound. La clase de desinfectante de enteros se puede usar con SANITIZE_TARGET y LOCAL_SANITIZE, mientras que default-ub solo se puede usar con SANITIZE_TARGET.

Mejor informe de errores

La implementación UBSan predeterminada de Android invoca una función específica cuando se encuentra un comportamiento indefinido. Por defecto, esta función es abortar. Sin embargo, a partir de octubre de 2016, UBSan en Android tiene una biblioteca de tiempo de ejecución opcional que brinda informes de errores más detallados, incluido el tipo de comportamiento indefinido encontrado, el archivo y la información de la línea de código fuente. Para habilitar este informe de errores con comprobaciones de enteros, agregue lo siguiente a un archivo Android.mk:

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

El valor LOCAL_SANITIZE habilita el desinfectante durante la compilación. LOCAL_SANITIZE_DIAG activa el modo de diagnóstico para el desinfectante especificado. Es posible establecer LOCAL_SANITIZE y LOCAL_SANITIZE_DIAG en diferentes valores, pero solo se habilitan las comprobaciones en LOCAL_SANITIZE. Si no se especifica una verificación en LOCAL_SANITIZE, pero se especifica en LOCAL_SANITIZE_DIAG, la verificación no está habilitada y no se brindan mensajes de diagnóstico.

Este es un ejemplo de la información proporcionada por la biblioteca de tiempo de ejecución de UBSan:

pixel-xl:/ # sanitizer-status ubsan
sanitizer-status/sanitizer-status.c:53:6: runtime error: unsigned integer overflow: 18446744073709551615 + 1 cannot be represented in type 'size_t' (aka 'unsigned long')

Desinfectante de direcciones del kernel

Al igual que los desinfectantes basados ​​en LLVM para los componentes del espacio de usuario, Android incluye el desinfectante de direcciones del kernel (KASAN). KASAN es una combinación de modificaciones del kernel y del tiempo de compilación que dan como resultado un sistema instrumentado que permite un descubrimiento de errores y un análisis de causa raíz más simples.

KASAN puede detectar muchos tipos de violaciones de memoria en el kernel. También puede detectar lecturas y escrituras fuera de límites en la pila, el montón y las variables globales, y puede detectar el uso después de la liberación y las liberaciones dobles.

Similar a ASAN, KASAN usa una combinación de instrumentación de funciones de memoria en tiempo de compilación y memoria oculta para rastrear los accesos a la memoria en tiempo de ejecución. En KASAN, una octava parte del espacio de la memoria del kernel se dedica a la memoria oculta, que determina si un acceso a la memoria es válido o no.

KASAN es compatible con las arquitecturas x86_64 y arm64. Ha sido parte del núcleo ascendente desde 4.0 y se ha adaptado a los núcleos basados ​​en Android 3.18. KASAN ha sido probado en kernels de Android compilados con gcc basados ​​en 4.9.2.

Además de KASAN, kcov es otra modificación del núcleo que es útil para realizar pruebas. kcov fue desarrollado para permitir pruebas de fuzz guiadas por cobertura en el kernel. Mide la cobertura en términos de entradas de llamada al sistema y es útil con sistemas fuzzing, como syzkaller .

Implementación

Para compilar un kernel con KASAN y kcov habilitados, agregue los siguientes indicadores de compilación a la configuración de compilación de su kernel:

CONFIG_KASAN 
CONFIG_KASAN_INLINE 
CONFIG_TEST_KASAN 
CONFIG_KCOV 
CONFIG_SLUB 
CONFIG_SLUB_DEBUG 
CONFIG_CC_OPTIMIZE_FOR_SIZE

Y eliminando lo siguiente:

CONFIG_SLUB_DEBUG_ON 
CONFIG_SLUB_DEBUG_PANIC_ON 
CONFIG_KASAN_OUTLINE 
CONFIG_KERNEL_LZ4

Luego construya y actualice su kernel como de costumbre. El núcleo KASAN es considerablemente más grande que el original. Si corresponde, modifique los parámetros de inicio y la configuración del cargador de inicio para tener esto en cuenta.

Después de actualizar el kernel, verifique los registros de arranque del kernel para ver si KASAN está habilitado y ejecutándose. El núcleo se iniciará con información del mapa de memoria para KASAN, como:

...
[    0.000000] c0      0 Virtual kernel memory layout:
[    0.000000] c0      0     kasan   : 0xffffff8000000000 - 0xffffff9000000000   (    64 GB)
[    0.000000] c0      0     vmalloc : 0xffffff9000010000 - 0xffffffbdbfff0000   (   182 GB)
[    0.000000] c0      0     vmemmap : 0xffffffbdc0000000 - 0xffffffbfc0000000   (     8 GB maximum)
[    0.000000] c0      0               0xffffffbdc0000000 - 0xffffffbdc3f95400   (    63 MB actual)
[    0.000000] c0      0     PCI I/O : 0xffffffbffa000000 - 0xffffffbffb000000   (    16 MB)
[    0.000000] c0      0     fixed   : 0xffffffbffbdfd000 - 0xffffffbffbdff000   (     8 KB)
[    0.000000] c0      0     modules : 0xffffffbffc000000 - 0xffffffc000000000   (    64 MB)
[    0.000000] c0      0     memory  : 0xffffffc000000000 - 0xffffffc0fe550000   (  4069 MB)
[    0.000000] c0      0       .init : 0xffffffc001d33000 - 0xffffffc001dce000   (   620 KB)
[    0.000000] c0      0       .text : 0xffffffc000080000 - 0xffffffc001d32284   ( 29385 KB)
...

Y así es como se verá un error:

[   18.539668] c3      1 ==================================================================
[   18.547662] c3      1 BUG: KASAN: null-ptr-deref on address 0000000000000008
[   18.554689] c3      1 Read of size 8 by task swapper/0/1
[   18.559988] c3      1 CPU: 3 PID: 1 Comm: swapper/0 Tainted: G        W      3.18.24-xxx #1
[   18.569275] c3      1 Hardware name: Android Device
[   18.577433] c3      1 Call trace:
[   18.580739] c3      1 [<ffffffc00008b32c>] dump_backtrace+0x0/0x2c4
[   18.586985] c3      1 [<ffffffc00008b600>] show_stack+0x10/0x1c
[   18.592889] c3      1 [<ffffffc001481194>] dump_stack+0x74/0xc8
[   18.598792] c3      1 [<ffffffc000202ee0>] kasan_report+0x11c/0x4d0
[   18.605038] c3      1 [<ffffffc00020286c>] __asan_load8+0x20/0x80
[   18.611115] c3      1 [<ffffffc000bdefe8>] android_verity_ctr+0x8cc/0x1024
[   18.617976] c3      1 [<ffffffc000bcaa2c>] dm_table_add_target+0x3dc/0x50c
[   18.624832] c3      1 [<ffffffc001bdbe60>] dm_run_setup+0x50c/0x678
[   18.631082] c3      1 [<ffffffc001bda8c0>] prepare_namespace+0x44/0x1ac
[   18.637676] c3      1 [<ffffffc001bda170>] kernel_init_freeable+0x328/0x364
[   18.644625] c3      1 [<ffffffc001478e20>] kernel_init+0x10/0xd8
[   18.650613] c3      1 ==================================================================

Además, si los módulos están habilitados en su kernel, puede cargar el módulo de kernel test_kasan para realizar más pruebas. El módulo intenta acceder a la memoria fuera de los límites y usar después de liberar y es útil para probar KASAN en un dispositivo de destino.