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 indefinidos , Android admite:

  • alineación
  • bool
  • límites
  • enumeración
  • float-cast-desbordamiento
  • flotante-dividir-por-cero
  • entero-dividir-por-cero
  • atributo no nulo
  • nulo
  • devolver
  • devuelve el atributo no nulo
  • cambio de base
  • cambio de exponente
  • desbordamiento de enteros con signo
  • inalcanzable
  • desbordamiento de enteros sin signo
  • enlazado a vla

Unsigned-integer-overflow, 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)

Y la configuración de blueprint 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 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 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 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')

Limpieza de desbordamiento de enteros

Los desbordamientos de enteros no deseados pueden causar daños en la memoria o vulnerabilidades de divulgación de información en las variables asociadas con los accesos a la memoria o las asignaciones de memoria. Para combatir esto, agregamos UndefinedBehaviorSanitizer (UBSan) de Clang, desinfectantes de desbordamiento de enteros firmados y sin firmar para fortalecer el marco de medios 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 manera segura un proceso si ocurre un desbordamiento. Estos desinfectantes pueden mitigar toda una clase de corrupción de memoria y vulnerabilidades de divulgación de información donde la causa raíz es un desbordamiento de enteros, como la vulnerabilidad Stagefright original.

Ejemplos y fuente

El compilador proporciona 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 está habilitada 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 el código nativo privilegiado o el código nativo que analiza la entrada del usuario que no es de confianza. Hay una pequeña sobrecarga de rendimiento asociada con el desinfectante que depende del uso del código y la prevalencia de las operaciones aritméticas. Espere un pequeño porcentaje de gastos generales y pruebe si el rendimiento es una preocupación.

Compatibilidad con IntSan en makefiles

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 separada por comas de desinfectantes, siendo integer_overflow un conjunto de opciones preempaquetado para los desinfectantes de desbordamiento de enteros individuales firmados y sin firmar 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, ya que 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 las funciones y los 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

Compatibilidad con IntSan en archivos blueprint

Para habilitar la desinfección de desbordamiento de enteros en un archivo blueprint, 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 individuales de desbordamiento de enteros firmados y sin firmar con una BLOCKLIST predeterminada .

El conjunto de propiedades diag habilita el modo de diagnóstico para los desinfectantes. Use el modo de diagnóstico solo durante la prueba. El modo de diagnóstico no aborta en 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 las funciones y los 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 está habilitando 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, es posible que tenga algunos problemas con desbordamientos de enteros benignos que causan abortos. Debe probar los componentes con la desinfección habilitada para asegurarse de que puedan surgir desbordamientos benignos.

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

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 el aborto; sin embargo, los desbordamientos que ocurren en las 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 activan la cancelación e intente reproducir el error. Con los diagnósticos habilitados, el proceso no se cancelará y, en su lugar, 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. Diagnostics genera un mensaje de error que incluye el número de línea y el archivo de origen que provoca 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 que se encuentra la operación aritmética problemática, asegúrese de que el desbordamiento sea benigno y intencionado (por ejemplo, no tiene implicaciones de seguridad). Puede abordar el aborto del desinfectante de la siguiente manera:

  • Refactorización del 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 el saneamiento de una función o archivo fuente a través de un archivo BLOCKLIST ( ejemplo )

Debe 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 la única operación refactorizada en lugar de toda la función BLOCKLIST.

Los patrones comunes que pueden resultar en desbordamientos benignos incluyen:

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

Se recomienda que los desarrolladores se aseguren de que, en los casos en los que el sanitizer detecte un desbordamiento, sea realmente benigno y no tenga efectos secundarios no deseados ni implicaciones de seguridad antes de deshabilitar el sanitization.

Deshabilitar IntSan

Puede deshabilitar IntSan con BLOCKLIST o atributos de función. Desactive con moderación y solo cuando la refactorización del código no sea razonable o si hay una sobrecarga de rendimiento problemática.

Consulte la documentación de Clang upstream para obtener más información sobre cómo deshabilitar IntSan con atributos de función y formato de archivo BLOCKLIST . La LISTA DE BLOQUEO 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 una prueba CTS específica para la desinfección de desbordamiento de enteros. En su lugar, asegúrese de que las pruebas de CTS pasen con o sin IntSan habilitado para verificar que no esté afectando el dispositivo.

Desinfección de límites

BoundsSanitizer (BoundSan) agrega instrumentación a los binarios para insertar comprobaciones de límites alrededor de los accesos a matrices. Estas comprobaciones se añaden si el compilador no puede probar en el momento de la compilación que el acceso será seguro y si el tamaño de la matriz se conocerá en tiempo de ejecución, de modo que pueda comprobarse. 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 está habilitada 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 el código nativo privilegiado o el código nativo complejo que analiza la entrada del usuario que no es de confianza. La sobrecarga de rendimiento asociada con la habilitación de BoundSan depende de la cantidad de accesos al arreglo que no se puede probar que sean seguros. Espere un pequeño porcentaje de gastos generales en promedio y pruebe si el rendimiento es una preocupación.

Habilitación de BoundSan en archivos blueprint

BoundSan se puede habilitar en archivos blueprint 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. Use el modo de diagnóstico solo durante la prueba. El modo de diagnóstico no se cancela en 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 la especificación de un archivo BLOCKLIST que los desarrolladores pueden usar para evitar que se desinfecten las funciones y los archivos fuente. Use esta propiedad solo si el rendimiento es una preocupación y los archivos/funciones de destino contribuyen sustancialmente. Audite manualmente estos archivos/funciones para asegurarse de que los accesos a la matriz sean seguros. Consulte Solución de problemas para obtener detalles adicionales.

Habilitación de BoundSan en archivos MAKE

BoundSan se puede habilitar en makefiles 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. Use el modo de diagnóstico solo durante la prueba. El modo de diagnóstico no se cancela en 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 las funciones y los archivos fuente. Use esta propiedad solo si el rendimiento es una preocupación y los archivos/funciones de destino contribuyen sustancialmente. Audite manualmente estos archivos/funciones para asegurarse de que los accesos a la matriz sean seguros. Consulte Solución de problemas para obtener detalles adicionales.

Deshabilitar BoundSan

Puede deshabilitar BoundSan en funciones y archivos fuente con BLOCKLIST o atributos de función. Es mejor mantener habilitado BoundSan, así que solo desactívelo si la función o el archivo genera una gran sobrecarga de rendimiento y la fuente se revisó 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 LISTA DE BLOQUEOS para el desinfectante en particular mediante el uso de nombres de sección que especifiquen el desinfectante de destino para evitar el impacto de otros desinfectantes.

Validación

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

Solución de problemas

Pruebe exhaustivamente los componentes después de habilitar BoundSan para asegurarse de que se aborden los accesos fuera de los límites no detectados anteriormente.

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 de origen, el número de línea y el valor del índice se imprimen en logcat . De forma predeterminada, este modo no arroja 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]'