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 errores de comportamiento no definidos , Android admite:

  • alineación
  • booleano
  • límites
  • enumeración
  • flotador-desbordamiento
  • flotador-dividir-por-cero
  • entero-dividido-por-cero
  • atributo no nulo
  • nulo
  • devolver
  • devuelve atributo no nulo
  • base de cambios
  • exponente de desplazamiento
  • desbordamiento de entero con signo
  • inalcanzable
  • desbordamiento de entero sin signo
  • vinculado a vla

unsigned-integer-overflow, aunque no es un comportamiento técnicamente indefinido, se incluye en el desinfectante y se utiliza 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 no definidos 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)

Y la configuración del plano equivalente (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: {
            misc_undefined: [
                "alignment",
                "bounds",
                "null",
                "unreachable",
                "integer",
            ],
        },
    },

}

Atajos de UBSan

Android también tiene dos atajos, integer y default-ub , para habilitar un conjunto de desinfectantes al mismo tiempo. integer habilita integer-divide-by-zero , signed-integer-overflow y unsigned-integer-overflow . default-ub habilita las comprobaciones que tienen problemas mínimos de rendimiento del compilador: bool, integer-divide-by-zero, return, returns-nonnull-attribute, shift-exponent, unreachable and vla-bound . La clase de desinfectante de números 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 cancelar. 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 no definido encontrado, información de línea de código fuente y archivos. Para habilitar este informe de errores con comprobaciones de números 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 configurar LOCAL_SANITIZE y LOCAL_SANITIZE_DIAG con valores diferentes, pero solo se habilitan esas comprobaciones en LOCAL_SANITIZE. Si no se especifica una verificación en LOCAL_SANITIZE, pero sí en LOCAL_SANITIZE_DIAG, la verificación no está habilitada y no se brindan mensajes de diagnóstico.

A continuación se muestra 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')

Desinfección de desbordamiento de enteros

Los desbordamientos de enteros no deseados pueden provocar daños en la memoria o vulnerabilidades de divulgación de información en variables asociadas con accesos o asignaciones de memoria. Para combatir esto, agregamos los desinfectantes de desbordamiento de enteros firmados y sin firmar de Clang, UndefinitedBehaviorSanitizer (UBSan), para reforzar el marco multimedia en Android 7.0. En Android 9, ampliamos UBSan para cubrir más componentes y mejoramos la compatibilidad del sistema de compilación.

Esto está diseñado para agregar comprobaciones en torno a operaciones/instrucciones aritméticas (que podrían desbordarse) para abortar de forma segura un proceso si se produce un desbordamiento. Estos desinfectantes pueden mitigar toda una clase de vulnerabilidades de corrupción de memoria y divulgación de información donde la causa principal es un desbordamiento de enteros, como la vulnerabilidad Stagefright original.

Ejemplos y fuente

El compilador proporciona la desinfección de desbordamiento de enteros (IntSan) y agrega instrumentación al binario durante el tiempo de compilación para detectar desbordamientos aritméticos. Está habilitado de forma predeterminada en varios componentes de la plataforma, por ejemplo /platform/external/libnl/Android.bp .

Implementación

IntSan utiliza los desinfectantes de desbordamiento de enteros con y sin signo de UBSan. Esta mitigación se habilita a nivel de módulo. Ayuda a mantener seguros los componentes críticos de Android y no debe desactivarse.

Le recomendamos encarecidamente que habilite la desinfección de desbordamiento de enteros para componentes adicionales. Los candidatos ideales son código nativo privilegiado o código nativo que analiza entradas de usuarios que no son de confianza. Existe una pequeña sobrecarga de rendimiento asociada con el desinfectante que depende del uso del código y de la prevalencia de las operaciones aritméticas. Espere un pequeño porcentaje de gastos generales y pruebe si el rendimiento es un problema.

Apoyando a IntSan en archivos MAKE

Para habilitar IntSan en un archivo MAKE, agregue:

LOCAL_SANITIZE := integer_overflow
    # Optional features
    LOCAL_SANITIZE_DIAG := integer_overflow
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
  • LOCAL_SANITIZE toma una lista de desinfectantes separados por comas, siendo integer_overflow un conjunto preempaquetado de opciones para los desinfectantes de desbordamiento de enteros individuales con y sin signo con una BLOCKLIST predeterminada .
  • LOCAL_SANITIZE_DIAG activa el modo de diagnóstico para los desinfectantes. Utilice el modo de diagnóstico solo durante las pruebas porque esto no anulará los desbordamientos, anulando por completo la ventaja de seguridad de la mitigación. Consulte Solución de problemas para obtener detalles adicionales.
  • LOCAL_SANITIZE_BLOCKLIST le permite especificar un archivo BLOCKLIST para evitar que se desinfecten funciones y archivos fuente. Consulte Solución de problemas para obtener detalles adicionales.

Si desea un control más granular, habilite los desinfectantes individualmente usando una o ambas banderas:

LOCAL_SANITIZE := signed-integer-overflow, unsigned-integer-overflow
    LOCAL_SANITIZE_DIAG := signed-integer-overflow, unsigned-integer-overflow

Apoyando a IntSan en archivos de planos

Para habilitar la desinfección de desbordamiento de enteros en un archivo de plano, como /platform/external/libnl/Android.bp , agregue:

   sanitize: {
          integer_overflow: true,
          diag: {
              integer_overflow: true,
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

Al igual que con los archivos make, la propiedad integer_overflow es un conjunto preempaquetado de opciones para los desinfectantes de desbordamiento de enteros individuales con y sin signo con una BLOCKLIST predeterminada .

El diag de propiedades de diagnóstico habilita el modo de diagnóstico para los desinfectantes. Utilice el modo de diagnóstico sólo durante las pruebas. El modo de diagnóstico no cancela los desbordamientos, lo que anula por completo la ventaja de seguridad de la mitigación en las compilaciones de usuarios. Consulte Solución de problemas para obtener detalles adicionales.

La propiedad BLOCKLIST permite la especificación de un archivo BLOCKLIST que permite a los desarrolladores evitar que se desinfecten funciones y archivos fuente. Consulte Solución de problemas para obtener detalles adicionales.

Para habilitar los desinfectantes individualmente, use:

   sanitize: {
          misc_undefined: ["signed-integer-overflow", "unsigned-integer-overflow"],
          diag: {
              misc_undefined: ["signed-integer-overflow",
                               "unsigned-integer-overflow",],
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

Solución de problemas

Si habilita la desinfección de desbordamiento de enteros en componentes nuevos, o confía en bibliotecas de plataforma que han tenido desinfección de desbordamiento de enteros, puede encontrarse con algunos problemas con desbordamientos de enteros benignos que causan abortos. Debe probar los componentes con la desinfección habilitada para garantizar que puedan salir a la superficie desbordamientos benignos.

Para encontrar cancelaciones causadas por desinfección en compilaciones de usuarios, busque fallas SIGABRT con mensajes de cancelación que indiquen un desbordamiento detectado por UBSan, como por ejemplo:

pid: ###, tid: ###, name: Binder:###  >>> /system/bin/surfaceflinger <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: sub-overflow'

El seguimiento de la pila debe incluir la función que causa la cancelación; sin embargo, los desbordamientos que ocurren en funciones en línea pueden no ser evidentes en el seguimiento de la pila.

Para determinar más fácilmente la causa raíz, habilite los diagnósticos en la biblioteca que desencadenan la cancelación e intente reproducir el error. Con el diagnóstico habilitado, el proceso no se cancelará y, en cambio, continuará ejecutándose. No abortar ayuda a maximizar la cantidad de desbordamientos benignos en una ruta de ejecución particular sin tener que volver a compilar después de corregir cada error. Diagnóstico genera un mensaje de error que incluye el número de línea y el archivo fuente que causa la cancelación:

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:2188:32: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'size_t' (aka 'unsigned long')

Una vez localizada la operación aritmética problemática, asegúrese de que el desbordamiento sea benigno y intencionado (por ejemplo, no tenga implicaciones de seguridad). Puede abordar la interrupción del desinfectante de la siguiente manera:

  • Refactorizando el código para evitar el desbordamiento ( ejemplo )
  • Desbordamiento explícito a través de las funciones __builtin_*_overflow de Clang ( ejemplo )
  • Deshabilitar la desinfección en la función especificando el atributo no_sanitize ( ejemplo )
  • Deshabilitar la desinfección de una función o archivo fuente a través de un archivo BLOCKLIST ( ejemplo )

Debes utilizar la solución más granular posible. Por ejemplo, una función grande con muchas operaciones aritméticas y una única operación de desbordamiento debería tener refactorizada la operación única en lugar de toda la función BLOCKLISTed.

Los patrones comunes que pueden provocar desbordamientos benignos incluyen:

  • Conversiones implícitas en las que se produce un desbordamiento sin firmar antes de convertirlo a un tipo firmado ( ejemplo )
  • Eliminaciones de listas vinculadas que disminuyen el índice de bucle al eliminarlas ( ejemplo )
  • Asignar un tipo sin firmar a -1 en lugar de especificar el valor máximo real ( ejemplo )
  • Bucles que disminuyen un entero sin signo en la condición ( ejemplo , ejemplo )

Se recomienda que los desarrolladores se aseguren de que los casos en los que el desinfectante detecte un desbordamiento sean realmente benignos y no tengan efectos secundarios no deseados ni implicaciones de seguridad antes de desactivar la desinfección.

Deshabilitar IntSan

Puede desactivar IntSan con BLOCKLIST o atributos de función. Deshabilite con moderación y solo cuando refactorizar el código no sea razonable o si hay una sobrecarga de rendimiento problemática.

Consulte la documentación anterior de Clang para obtener más información sobre cómo deshabilitar IntSan con atributos de función y formato de archivo BLOCKLIST . BLOCKLISTing debe limitarse al desinfectante en particular mediante el uso de nombres de sección que especifiquen el desinfectante de destino para evitar afectar a otros desinfectantes.

Validación

Actualmente, no existe ninguna prueba CTS específica para la desinfección por desbordamiento de enteros. En su lugar, asegúrese de que las pruebas CTS pasen con o sin IntSan habilitado para verificar que no esté impactando el dispositivo.

Sanitización de límites

BoundsSanitizer (BoundSan) agrega instrumentación a los binarios para insertar comprobaciones de límites alrededor de los accesos a la matriz. Estas comprobaciones se agregan si el compilador no puede demostrar en el momento de la compilación que el acceso será seguro y si el tamaño de la matriz se conocerá en el tiempo de ejecución, para poder compararlo. Android 10 implementa BoundSan en Bluetooth y códecs. BoundSan lo proporciona el compilador y está habilitado de forma predeterminada en varios componentes de la plataforma.

Implementación

BoundSan utiliza el desinfectante de límites de UBSan . Esta mitigación se habilita a nivel de módulo. Ayuda a mantener seguros los componentes críticos de Android y no debe desactivarse.

Le recomendamos encarecidamente que habilite BoundSan para componentes adicionales. Los candidatos ideales son código nativo privilegiado o código nativo complejo que analiza entradas de usuarios que no son de confianza. La sobrecarga de rendimiento asociada con la habilitación de BoundSan depende de la cantidad de accesos a la matriz que no se puede demostrar que sean seguros. Espere un pequeño porcentaje de gastos generales en promedio y pruebe si el rendimiento es un problema.

Habilitando BoundSan en archivos de planos

BoundSan se puede habilitar en archivos de planos agregando "bounds" a la propiedad sanitize misc_undefined para módulos binarios y de biblioteca:

    sanitize: {
       misc_undefined: ["bounds"],
       diag: {
          misc_undefined: ["bounds"],
       },
       BLOCKLIST: "modulename_BLOCKLIST.txt",
diagnóstico

La propiedad diag habilita el modo de diagnóstico para los desinfectantes. Utilice el modo de diagnóstico sólo durante las pruebas. El modo de diagnóstico no cancela los desbordamientos, lo que anula la ventaja de seguridad de la mitigación y conlleva una mayor sobrecarga de rendimiento, por lo que no se recomienda para compilaciones de producción.

LISTA DE BLOQUEOS

La propiedad BLOCKLIST permite especificar un archivo BLOCKLIST que los desarrolladores pueden usar para evitar que se desinfecten funciones y archivos fuente. Utilice esta propiedad sólo si el rendimiento es un problema y los archivos/funciones de destino contribuyen sustancialmente. Audite manualmente estos archivos/funciones para garantizar que los accesos a la matriz sean seguros. Consulte Solución de problemas para obtener detalles adicionales.

Habilitando BoundSan en archivos MAKE

BoundSan se puede habilitar en archivos MAKE agregando "bounds" a la variable LOCAL_SANITIZE para módulos binarios y de biblioteca:

    LOCAL_SANITIZE := bounds
    # Optional features
    LOCAL_SANITIZE_DIAG := bounds
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt

LOCAL_SANITIZE acepta una lista de desinfectantes separados por una coma.

LOCAL_SANITIZE_DIAG activa el modo de diagnóstico. Utilice el modo de diagnóstico sólo durante las pruebas. El modo de diagnóstico no cancela los desbordamientos, lo que anula la ventaja de seguridad de la mitigación y conlleva una mayor sobrecarga de rendimiento, por lo que no se recomienda para compilaciones de producción.

LOCAL_SANITIZE_BLOCKLIST permite la especificación de un archivo BLOCKLIST que permite a los desarrolladores evitar que se desinfecten funciones y archivos fuente. Utilice esta propiedad sólo si el rendimiento es un problema y los archivos/funciones de destino contribuyen sustancialmente. Audite manualmente estos archivos/funciones para garantizar que los accesos a la matriz sean seguros. Consulte Solución de problemas para obtener detalles adicionales.

Deshabilitar BoundSan

Puede desactivar BoundSan en funciones y archivos fuente con BLOCKLIST o atributos de función. Es mejor mantener BoundSan habilitado, así que desactívelo solo si la función o el archivo genera una gran sobrecarga de rendimiento y la fuente se ha revisado manualmente.

Para obtener más información sobre cómo deshabilitar BoundSan con atributos de función y formato de archivo BLOCKLIST , consulte la documentación de Clang LLVM. Alcance la BLOCKLISTing para el desinfectante en particular mediante el uso de nombres de sección que especifiquen el desinfectante de destino para evitar afectar a otros desinfectantes.

Validación

No existe una prueba CTS específica para BoundSan. En su lugar, asegúrese de que las pruebas CTS pasen con o sin BoundSan habilitado para verificar que no esté afectando el dispositivo.

Solución de problemas

Pruebe minuciosamente los componentes después de habilitar BoundSan para garantizar que se aborde cualquier acceso fuera de límites no detectado previamente.

Los errores de BoundSan se pueden identificar fácilmente ya que incluyen el siguiente mensaje de cancelación de desecho:

    pid: ###, tid: ###, name: Binder:###  >>> /system/bin/foobar <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: out-of-bounds'

Cuando se ejecuta en modo de diagnóstico, el archivo fuente, el número de línea y el valor del índice se imprimen en logcat . De forma predeterminada, este modo no genera un mensaje de cancelación. Revise logcat para comprobar si hay errores.

    external/foo/bar.c:293:13: runtime error: index -1 out of bounds for type 'int [24]'