UndefinedBehaviorSanitizer (UBSan) realiza la instrumentación en tiempo de compilación para verificar varios tipos de comportamiento indefinido. Si bien UBSan puede detectar muchos errores de comportamiento indefinido, Android admite lo siguiente:
- alineación
- bool
- límites
- enum
- float-cast-overflow
- float-divide-by-zero
- integer-divide-by-zero
- nonnull-attribute
- null
- return
- returns-nonnull-attribute
- shift-base
- shift-exponent
- signed-integer-overflow
- inaccesible
- unsigned-integer-overflow
- vla-bound
El desbordamiento de números enteros sin signo, si bien no es técnicamente un comportamiento indefinido, se incluye en el sanitizador y se usa en muchos módulos de Android, incluidos los componentes de mediaserver, para eliminar cualquier vulnerabilidad latente de desbordamiento de números enteros.
Implementación
En el sistema de compilación de Android, puedes habilitar UBSan de forma global o local. Para habilitar UBSan de forma global, establece SANITIZE_TARGET en Android.mk. Para habilitar UBSan a nivel de cada módulo, establece LOCAL_SANITIZE y especifica los comportamientos indefinidos que deseas 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 esquema 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",
],
},
},
}
Combinaciones de teclas de UBSan
Android también tiene dos accesos directos, integer y default-ub, para habilitar un conjunto de sanitizadores al mismo tiempo. El número entero habilita integer-divide-by-zero, signed-integer-overflow y unsigned-integer-overflow.
default-ub habilita las verificaciones 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 sanitización de números enteros se puede usar con SANITIZE_TARGET y LOCAL_SANITIZE, mientras que default-ub solo se puede usar con SANITIZE_TARGET.
Mejores informes de errores
La implementación predeterminada de UBSan de Android invoca una función especificada cuando se detecta un comportamiento indefinido. De forma predeterminada, esta función es abort. Sin embargo, a partir de octubre de 2016, UBSan en Android tiene una biblioteca de tiempo de ejecución opcional que proporciona informes de errores más detallados, incluido el tipo de comportamiento indefinido encontrado, información del archivo y la línea del código fuente. Para habilitar estos informes de errores con verificaciones de números enteros, agrega lo siguiente a un archivo Android.mk:
LOCAL_SANITIZE:=integer LOCAL_SANITIZE_DIAG:=integer
El valor LOCAL_SANITIZE habilita el sanitizador durante la compilación. LOCAL_SANITIZE_DIAG activa el modo de diagnóstico para el filtro especificado. Es posible establecer LOCAL_SANITIZE y LOCAL_SANITIZE_DIAG en valores diferentes, pero solo se habilitan las verificaciones en LOCAL_SANITIZE. Si no se especifica una verificación en LOCAL_SANITIZE, pero sí en LOCAL_SANITIZE_DIAG, la verificación no se habilita y no se proporcionan mensajes de diagnóstico.
A continuación, se muestra un ejemplo de la información que proporciona 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 números enteros
Los desbordamientos de números enteros no intencionales pueden causar daños en la memoria o vulnerabilidades de divulgación de información en variables asociadas con accesos a la memoria o asignaciones de memoria. Para combatir este problema, agregamos los verificadores de desbordamiento de números enteros con signo y sin signo de UndefinedBehaviorSanitizer (UBSan) de Clang para reforzar el framework de medios en Android 7.0. En Android 9, expandimos UBSan para abarcar más componentes y mejoramos la compatibilidad del sistema de compilación con él.
Está diseñado para agregar verificaciones en torno a las operaciones aritméticas y las instrucciones (que podrían desbordarse) para anular de forma segura un proceso si se produce un desbordamiento. Estos sanitizadores pueden mitigar toda una clase de vulnerabilidades de corrupción de memoria y divulgación de información en las que la causa raíz es un desbordamiento de números enteros, como la vulnerabilidad original de Stagefright.
Ejemplos y fuente
El compilador proporciona la limpieza de desbordamiento de enteros (IntSan), que agrega instrumentación al archivo 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 usa los limpiadores de desbordamiento de números enteros con firma y sin firma de UBSan. Esta mitigación se habilita de a un nivel por módulo. Ayuda a mantener seguros los componentes importantes de Android y no debe inhabilitarse.
Te recomendamos que habilites la limpieza 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 no confiable. Hay una pequeña sobrecarga de rendimiento asociada con el sanitizador que depende del uso del código y la prevalencia de las operaciones aritméticas. Se espera un pequeño porcentaje de sobrecarga y se recomienda probar si el rendimiento es un problema.
Compatibilidad con IntSan en archivos makefile
Para habilitar IntSan en un archivo makefile, agrega lo siguiente:
LOCAL_SANITIZE := integer_overflow # Optional features LOCAL_SANITIZE_DIAG := integer_overflow LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
LOCAL_SANITIZEtoma una lista de sanitizadores separados por comas, coninteger_overflowcomo un conjunto preempaquetado de opciones para los sanitizadores individuales de desbordamiento de números enteros con signo y sin signo con una BLOCKLIST predeterminada.LOCAL_SANITIZE_DIAGactiva el modo de diagnóstico para los saneadores. Usa el modo de diagnóstico solo durante las pruebas, ya que no se anulará en caso de desbordamiento, lo que anulará por completo la ventaja de seguridad de la mitigación. Consulta Solución de problemas para obtener más detalles.LOCAL_SANITIZE_BLOCKLISTte permite especificar un archivo de BLOCKLIST para evitar que se saneen las funciones y los archivos fuente. Consulta Solución de problemas para obtener más detalles.
Si deseas un control más detallado, habilita los sanitizadores de forma individual con una o ambas marcas:
LOCAL_SANITIZE := signed-integer-overflow, unsigned-integer-overflow LOCAL_SANITIZE_DIAG := signed-integer-overflow, unsigned-integer-overflow
Se agregó compatibilidad con IntSan en archivos de esquema
Para habilitar la limpieza de desbordamiento de enteros en un archivo de plano, como /platform/external/libnl/Android.bp, agrega lo siguiente:
sanitize: {
integer_overflow: true,
diag: {
integer_overflow: true,
},
BLOCKLIST: "modulename_BLOCKLIST.txt",
},
Al igual que con los archivos de compilación, la propiedad integer_overflow es un conjunto preempaquetado de opciones para los verificadores de desbordamiento de números enteros con signo y sin signo individuales con una BLOCKLIST predeterminada.
El conjunto de propiedades diag habilita el modo de diagnóstico para los saneadores. Usa el modo de diagnóstico solo durante las pruebas. El modo de diagnóstico no se anula en caso de desbordamiento, lo que anula por completo la ventaja de seguridad de la mitigación en las compilaciones del usuario. Consulta Solución de problemas para obtener más detalles.
La propiedad BLOCKLIST permite especificar un archivo de BLOCKLIST que permite a los desarrolladores evitar que se saneen funciones y archivos fuente. Consulta Solución de problemas para obtener más detalles.
Para habilitar los sanitizadores de forma individual, usa lo siguiente:
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 habilitas la sanitización de desbordamiento de números enteros en componentes nuevos o dependes de bibliotecas de la plataforma que ya tienen esta sanitización, es posible que te encuentres con algunos problemas relacionados con desbordamientos de números enteros benignos que causan anulaciones. Debes probar los componentes con la sanitización habilitada para asegurarte de que se puedan mostrar los desbordamientos benignos.
Para encontrar las anulaciones causadas por la limpieza en las compilaciones del usuario, busca fallas de SIGABRT con mensajes de anulación que indiquen un desbordamiento detectado por UBSan, como los siguientes:
pid: ###, tid: ###, name: Binder:### >>> /system/bin/surfaceflinger <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'ubsan: sub-overflow'
El registro de pila debe incluir la función que causa la anulación, sin embargo, los desbordamientos que ocurren en las funciones intercaladas pueden no ser evidentes en el registro de pila.
Para determinar la causa raíz con mayor facilidad, habilita el diagnóstico en la biblioteca que activa la anulación y trata de reproducir el error. Si habilitas el diagnóstico, el proceso no se anulará 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. El diagnóstico produce un mensaje de error que incluye el número de línea y el archivo fuente que causan la anulació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 localice la operación aritmética problemática, asegúrate de que el desbordamiento sea benigno y previsto (p.ej., que no tenga implicaciones de seguridad). Puedes abordar la anulación del sanitizador de las siguientes maneras:
- Refactorización del código para evitar el desbordamiento (ejemplo)
- Desbordamiento de forma explícita a través de las funciones __builtin_*_overflow de Clang (ejemplo)
- Inhabilita la limpieza en la función especificando el atributo
no_sanitize. (Ejemplo) - Cómo inhabilitar la limpieza de una función o un archivo fuente a través de un archivo BLOCKLIST (ejemplo)
Debes usar la solución más detallada posible. Por ejemplo, una función grande con muchas operaciones aritméticas y una sola operación de desbordamiento debería refactorizarse en la operación única en lugar de incluirse en la LISTA DE BLOQUEO toda la función.
Entre los patrones comunes que pueden generar desbordamientos benignos, se incluyen los siguientes:
- Conversiones implícitas en las que se produce un desbordamiento sin signo antes de la conversión a un tipo con signo (ejemplo)
- Borrado de listas vinculadas que disminuye el índice de bucle en el borrado (ejemplo)
- Asignar un tipo sin firma a -1 en lugar de especificar el valor máximo real (ejemplo)
- Bucles que disminuyen un número entero sin signo en la condición (ejemplo, ejemplo)
Se recomienda que los desarrolladores se aseguren de que los casos en los que el sanitizador detecta un desbordamiento sean benignos y no tengan efectos secundarios no deseados ni implicaciones de seguridad antes de inhabilitar la sanitización.
Inhabilita IntSan
Puedes inhabilitar IntSan con BLOCKLISTs o atributos de función. Inhabilita con moderación y solo cuando refactorizar el código sea irrazonable o si hay una sobrecarga de rendimiento problemática.
Consulta la documentación de Clang upstream para obtener más información sobre cómo inhabilitar IntSan con atributos de función y el formato de archivo BLOCKLIST. El BLOQUEO debe limitarse al filtro específico usando nombres de sección que especifiquen el filtro objetivo para evitar afectar a otros filtros.
Validación
Actualmente, no hay pruebas del CTS específicas para la sanitización de desbordamiento de números enteros. En cambio, asegúrate de que las pruebas de CTS se aprueben con IntSan habilitado o inhabilitado para verificar que no afecte al dispositivo.
Limpieza de límites
BoundsSanitizer (BoundSan) agrega instrumentación a los archivos binarios para insertar verificaciones de límites alrededor de los accesos a arrays. Estas verificaciones se agregan si el compilador no puede demostrar en tiempo de compilación que el acceso será seguro y si el tamaño del array se conocerá en tiempo de ejecución, de modo que se pueda verificar. Android 10 implementa BoundSan en Bluetooth y códecs. BoundSan es proporcionado por el compilador y está habilitado de forma predeterminada en varios componentes de la plataforma.
Implementación
BoundSan usa el limpiador de límites de UBSan. Esta mitigación se habilita de a un nivel por módulo. Ayuda a mantener seguros los componentes importantes de Android y no debe inhabilitarse.
Te recomendamos que habilites 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 no confiable. La sobrecarga de rendimiento asociada con la habilitación de BoundSan depende de la cantidad de accesos a arrays cuya seguridad no se puede demostrar. Espera un pequeño porcentaje de sobrecarga en promedio y prueba si el rendimiento es un problema.
Habilita BoundSan en los archivos de esquema
BoundSan se puede habilitar en los archivos de Blueprint agregando "bounds"
a la propiedad de sanitización misc_undefined para los módulos
binarios y de biblioteca:
sanitize: {
misc_undefined: ["bounds"],
diag: {
misc_undefined: ["bounds"],
},
BLOCKLIST: "modulename_BLOCKLIST.txt",diag
La propiedad diag habilita el modo de diagnóstico para los sanitizadores.
Usa el modo de diagnóstico solo durante las pruebas. El modo de diagnóstico no se anula en caso de desbordamiento, 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 las compilaciones de producción.
LISTA DE ENTIDADES BLOQUEADAS
La propiedad BLOCKLIST permite especificar un archivo de BLOCKLIST que los desarrolladores pueden usar para evitar que se saneen las funciones y los archivos fuente. Usa esta propiedad solo si el rendimiento es un problema y los archivos o las funciones objetivo contribuyen de manera sustancial. Audita manualmente estos archivos o funciones para asegurarte de que los accesos a los arrays sean seguros. Consulta Solución de problemas para obtener más detalles.
Habilita BoundSan en los archivos make
BoundSan se puede habilitar en los archivos make agregando "bounds" a la variable LOCAL_SANITIZE para los 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 saneadores separados por una coma.
LOCAL_SANITIZE_DIAG activa el modo de diagnóstico. Utiliza el modo de diagnóstico solo durante las pruebas. El modo de diagnóstico no se anula en caso de desbordamiento, 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 las compilaciones de producción.
LOCAL_SANITIZE_BLOCKLIST permite especificar un archivo de BLOCKLIST que permite a los desarrolladores evitar que se saneen funciones y archivos fuente. Usa esta propiedad solo si el rendimiento es un problema y los archivos o las funciones objetivo contribuyen de manera sustancial. Audita manualmente estos archivos o funciones para asegurarte de que los accesos a los arrays sean seguros. Consulta Solución de problemas para obtener más detalles.
Inhabilita BoundSan
Puedes inhabilitar BoundSan en funciones y archivos fuente con BLOCKLISTs o atributos de función. Lo mejor es mantener habilitado BoundSan, por lo que solo debes inhabilitarlo si la función o el archivo generan una gran cantidad de sobrecarga de rendimiento y la fuente se revisó de forma manual.
Para obtener más información sobre cómo inhabilitar BoundSan con atributos de función y el formato de archivo BLOCKLIST, consulta la documentación de Clang LLVM. Delimita el BLOQUEO al filtro específico usando nombres de sección que especifiquen el filtro objetivo para evitar afectar a otros filtros.
Validación
No hay pruebas de CTS específicas para BoundSan. En su lugar, asegúrate de que las pruebas del CTS se aprueben con BoundSan habilitado o inhabilitado para verificar que no afecte al dispositivo.
Solución de problemas
Prueba los componentes a fondo después de habilitar BoundSan para asegurarte de que se aborden todos los accesos fuera de límites que no se detectaron anteriormente.
Los errores de BoundSan se pueden identificar fácilmente, ya que incluyen el siguiente mensaje de anulación de lápida:
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 arroja un mensaje de anulación. Revisa logcat para detectar errores.
external/foo/bar.c:293:13: runtime error: index -1 out of bounds for type 'int [24]'