UndefinedBehaviorSanitizer (UBSan) realiza instrumentación en el tiempo de compilación para verificar varios tipos de comportamientos no definidos. Si bien UBSan es capaz de detectar muchos errores de comportamiento no definidos, 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
Si bien unsigned-integer-overflow no es un comportamiento técnicamente indefinido, se incluye en el validador 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 no definidos 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", ], }, }, }
Accesos directos de UBSan
Android también tiene dos combinaciones de teclas, integer
y default-ub
, para habilitar un conjunto de validadores 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 de rendimiento mínimos del compilador: bool, integer-divide-by-zero, return,
returns-nonnull-attribute, shift-exponent, unreachable and vla-bound
. La clase de sanitizador 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 encuentra un comportamiento no definido. De forma predeterminada, esta función se cancela. 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 no definido que se encontró, la información de la línea de archivo y 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 validador durante la compilación. LOCAL_SANITIZE_DIAG activa el modo de diagnóstico para el sanitizador especificado. Es posible configurar LOCAL_SANITIZE y LOCAL_SANITIZE_DIAG en valores diferentes, pero solo se habilitan las verificaciones de LOCAL_SANITIZE. Si no se especifica una verificación en LOCAL_SANITIZE, pero se especifica en LOCAL_SANITIZE_DIAG, la verificación no se habilita y no se muestran los mensajes de diagnóstico.
Este es un ejemplo de la información que proporciona la biblioteca del entorno 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 deseados pueden causar 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 validadores de desbordamiento de números enteros firmados y sin firmar de UndefinedBehaviorSanitizer (UBSan) de Clang para endurecer el framework de medios en Android 7.0. En Android 9, ampliamos UBSan para cubrir más componentes y mejoramos la compatibilidad con el sistema de compilación.
Se diseñó para agregar verificaciones alrededor de las operaciones aritméticas y las instrucciones, que podrían desbordarse, para abortar un proceso de forma segura si se produce un desbordamiento. Estos validadores pueden mitigar una clase completa 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úmero entero, como la vulnerabilidad original de Stagefright.
Ejemplos y fuente
El compilador proporciona la limpieza 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 usa los limpiadores de desbordamiento de números enteros con firma y sin ella 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 con privilegios o el código nativo que analiza la entrada del usuario no confiable. Hay una pequeña sobrecarga de rendimiento asociada con el validador que depende del uso del código y de la prevalencia de las operaciones aritméticas. Espera un pequeño porcentaje de sobrecarga y prueba 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_SANITIZE
toma una lista de validadores separados por comas, en la queinteger_overflow
es un conjunto de opciones empaquetadas previamente para los validadores de desbordamiento de números enteros firmados y sin firmar con una BLOCKLIST predeterminada.LOCAL_SANITIZE_DIAG
activa el modo de diagnóstico para los sanitizadores. Usa el modo de diagnóstico solo durante las pruebas, ya que no se abortará en los desbordamientos, 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_BLOCKLIST
te permite especificar un archivo BLOCKLIST para evitar que se limpien las funciones y los archivos de origen. Consulta Solución de problemas para obtener más detalles.
Si deseas un control más detallado, habilita los validadores 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
Compatibilidad con IntSan en archivos de esquemas
Para habilitar la limpieza de desbordamiento de enteros en un archivo de blueprint, 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 make, la propiedad integer_overflow
es un conjunto preempaquetado de opciones para los validadores de desbordamiento de números enteros firmados y sin firmar individuales con una lista de bloqueo predeterminada.
El conjunto de propiedades 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 cancela en los desbordamientos, lo que anula por completo la ventaja de seguridad de la mitigación en las compilaciones de los usuarios. Consulta Solución de problemas para obtener más detalles.
La propiedad BLOCKLIST
permite especificar un archivo BLOCKLIST que permite a los desarrolladores evitar que se limpien las funciones y los archivos de origen. Consulta Solución de problemas para obtener más detalles.
Para habilitar los validadores 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 limpieza de desbordamientos de números enteros en componentes nuevos o dependes de bibliotecas de la plataforma que tuvieron limpieza de desbordamientos de números enteros, es posible que encuentres algunos problemas con desbordamientos de números enteros benignos que causan abortos. Debes probar los componentes con la limpieza habilitada para asegurarte de que se puedan mostrar desbordamientos benignos.
Para encontrar las interrupciones causadas por la limpieza en las compilaciones de los usuarios, busca fallas de SIGABRT
con mensajes de aborto 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 seguimiento de pila debe incluir la función que causa la interrupción. Sin embargo, es posible que los desbordamientos que se producen en las funciones intercaladas no sean evidentes en el seguimiento de pila.
Para determinar la causa raíz con mayor facilidad, habilita los diagnósticos en la biblioteca que activan la interrupción y, luego, intenta reproducir el error. Si los diagnósticos están habilitados, el proceso no se abortará y, en su lugar, seguirá ejecutándose. No abortar ayuda a maximizar la cantidad de desbordamientos benignos en una ruta de ejecución en 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 causa la interrupció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 encuentre la operación aritmética problemática, asegúrate de que el desbordamiento sea benigno y deseado (p.ej., que no tenga implicaciones de seguridad). Puedes abordar la cancelación del limpiador de la siguiente manera:
- Refactorización del código para evitar el desbordamiento (ejemplo)
- Genera un desbordamiento de forma explícita a través de las funciones __builtin_*_overflow de Clang (ejemplo)
- Inhabilitar la limpieza en la función especificando el atributo
no_sanitize
(ejemplo) - Inhabilitar la limpieza de una función o un archivo de origen 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 debe refactorizar la operación única en lugar de incluir toda la función en la lista de bloqueo.
Entre los patrones comunes que pueden generar desbordamientos benignos, se incluyen los siguientes:
- Conversión implícita en la que se produce un desbordamiento sin firmar antes de convertirlo a un tipo firmado (ejemplo)
- Eliminaciones de listas vinculadas que disminuyen el índice del bucle en la eliminación (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 firmar en la condición (ejemplo, ejemplo)
Se recomienda que los desarrolladores se aseguren de que los casos en los que el validador detecta un desbordamiento sean benignos, sin efectos secundarios no deseados ni implicaciones de seguridad, antes de inhabilitar la limpieza.
Inhabilita IntSan
Puedes inhabilitar IntSan con listas de bloqueo o atributos de función. Inhabilita con moderación y solo cuando la refactorización del código no sea razonable 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. La lista de entidades bloqueadas debe tener el alcance del sanitizador en particular mediante el uso de nombres de secciones que especifiquen el sanitizador de destino para evitar afectar a otros sanitizadores.
Validación
Actualmente, no hay pruebas de CTS específicas para la limpieza de desbordamientos de números enteros. En su lugar, asegúrate de que las pruebas de CTS se aprueben con o sin IntSan habilitado para verificar que no afecte el dispositivo.
Limpieza de límites
BoundsSanitizer (BoundSan) agrega instrumentación a los objetos binarios para insertar verificaciones de límites alrededor de los accesos a los arrays. Estas verificaciones se agregan si el compilador no puede probar en el momento de la compilación que el acceso será seguro y si se conocerá el tamaño del array en el tiempo de ejecución para que se pueda verificar. Android 10 implementa BoundSan en Bluetooth y códecs. El compilador proporciona BoundSan y está habilitado de forma predeterminada en varios componentes de la plataforma.
Implementación
BoundSan usa la limpieza de límites de UBSan. Esta mitigación está habilitada a nivel de cada 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 con privilegios 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 que no se pueden probar como seguros. Espera un porcentaje de sobrecarga pequeño en promedio y prueba si el rendimiento es un problema.
Habilita BoundSan en los archivos de Blueprint
Para habilitar BoundSan en los archivos de Blueprint, agrega "bounds"
a la propiedad de limpieza misc_undefined
para los módulos binarios y de bibliotecas:
sanitize: { misc_undefined: ["bounds"], diag: { misc_undefined: ["bounds"], }, BLOCKLIST: "modulename_BLOCKLIST.txt",
diag
La propiedad diag
habilita el modo de diagnóstico para los validadores.
Usa el modo de diagnóstico solo durante las pruebas. El modo de diagnóstico no aborta en los desbordamientos, lo que anula la ventaja de seguridad de la mitigación y tiene una sobrecarga de rendimiento más alta, por lo que no se recomienda para compilaciones de producción.
LISTA DE ENTIDADES BLOQUEADAS
La propiedad BLOCKLIST
permite especificar un archivo BLOCKLIST que los desarrolladores pueden usar para evitar que se limpien las funciones y los archivos de origen. Usa esta propiedad solo si el rendimiento es un problema y los archivos o las funciones objetivo contribuyen de forma 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
Para habilitar BoundSan en los archivos makefile, agrega "bounds"
a la variable LOCAL_SANITIZE
para los módulos binarios y de bibliotecas:
LOCAL_SANITIZE := bounds # Optional features LOCAL_SANITIZE_DIAG := bounds LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
LOCAL_SANITIZE
acepta una lista de validadores separados por una coma.
LOCAL_SANITIZE_DIAG
activa el modo de diagnóstico. Usa el modo de diagnóstico solo durante las pruebas. El modo de diagnóstico no aborta en los desbordamientos, lo que anula la ventaja de seguridad de la mitigación y tiene una sobrecarga de rendimiento más alta, por lo que no se recomienda para compilaciones de producción.
LOCAL_SANITIZE_BLOCKLIST
permite especificar un archivo BLOCKLIST que permite a los desarrolladores evitar que se limpien las funciones y los archivos de origen. Usa esta propiedad solo si el rendimiento es un problema y los archivos o las funciones objetivo contribuyen de forma 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 de origen con listas de bloqueo o atributos de función. Es mejor mantener BoundSan habilitado, así que solo inhabilita esta función si la función o el archivo crean 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 formato de archivo BLOCKLIST, consulta la documentación de Clang LLVM. Usa nombres de secciones que especifiquen el sanitizador de destino para evitar que la BLOCKLIST afecte a otros sanitizadores.
Validación
No hay pruebas de CTS específicas para BoundSan. En su lugar, asegúrate de que las pruebas de CTS aprueben con o sin BoundSan habilitado para verificar que no afecte al dispositivo.
Solución de problemas
Prueba los componentes en detalle después de habilitar BoundSan para asegurarte de que se aborden los accesos fuera de los límites que no se detectaron anteriormente.
Los errores de BoundSan se pueden identificar fácilmente, ya que incluyen el siguiente mensaje de aborto 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 aborto. Revisa logcat
para comprobar si hay errores.
external/foo/bar.c:293:13: runtime error: index -1 out of bounds for type 'int [24]'