Scudo es un asignador de memoria dinámico en modo de usuario, o asignador de montón , diseñado para resistir las vulnerabilidades relacionadas con el montón (como el desbordamiento de búfer basado en montón, el uso después de liberar y el doble libre ) mientras mantiene el rendimiento. Proporciona las primitivas estándar de asignación y desasignación de C (como malloc y free), así como las primitivas de C (como new y delete).
Scudo es más una mitigación que un detector de errores de memoria completo como AddressSanitizer (ASan) .
Desde el lanzamiento de Android 11, scudo se usa para todo el código nativo (excepto en dispositivos con poca memoria, donde todavía se usa jemalloc). En tiempo de ejecución, Scudo atiende todas las asignaciones y desasignaciones de almacenamiento dinámico nativo para todos los ejecutables y sus dependencias de biblioteca, y el proceso se cancela si se detecta corrupción o comportamiento sospechoso en el almacenamiento dinámico.
Scudo es de código abierto y parte del proyecto compilador-rt de LLVM. La documentación está disponible en https://llvm.org/docs/ScudoHardenedAllocator.html . El tiempo de ejecución de Scudo se envía como parte de la cadena de herramientas de Android y se agregó compatibilidad con Soong y Make para permitir una habilitación sencilla del asignador en un binario.
Puede habilitar o deshabilitar la mitigación adicional dentro del asignador mediante las opciones que se describen a continuación.
personalización
Algunos parámetros del asignador se pueden definir por proceso de varias formas:
- Estáticamente: defina una función
__scudo_default_options
en el programa que devuelva la cadena de opciones para analizar. Esta función debe tener el siguiente prototipo:extern "C" const char *__scudo_default_options()
. - Dinámicamente: use la variable de entorno
SCUDO_OPTIONS
que contiene la cadena de opciones que se va a analizar. Las opciones definidas de esta manera anulan cualquier definición realizada a través__scudo_default_options
.
Las siguientes opciones están disponibles.
Opción | predeterminado de 64 bits | predeterminado de 32 bits | Descripción |
---|---|---|---|
QuarantineSizeKb | 256 | 64 | El tamaño (en KB) de la cuarentena utilizada para retrasar la desasignación real de fragmentos. Un valor más bajo puede reducir el uso de memoria pero disminuir la efectividad de la mitigación; un valor negativo vuelve a los valores predeterminados. Establecer tanto esto como ThreadLocalQuarantineSizeKb en cero desactiva la cuarentena por completo. |
QuarantineChunksUpToSize | 2048 | 512 | El tamaño (en bytes) hasta el cual se pueden poner en cuarentena los fragmentos. |
ThreadLocalQuarantineSizeKb | 64 | 16 | El tamaño (en KB) del uso de la memoria caché por subproceso para descargar la cuarentena global. Un valor más bajo puede reducir el uso de la memoria, pero puede aumentar la contención en la cuarentena global. Establecer tanto esto como QuarantineSizeKb en cero desactiva la cuarentena por completo. |
DeallocationTypeMismatch | false | false | Habilita el informe de errores en malloc/delete, new/free, new/delete[] |
DeleteSizeMismatch | true | true | Habilita el informe de errores sobre la falta de coincidencia entre los tamaños de nuevo y eliminado. |
ZeroContents | false | false | Habilita el contenido de cero fragmentos en la asignación y desasignación. |
allocator_may_return_null | false | false | Especifica que el asignador puede devolver un valor nulo cuando se produce un error recuperable, en lugar de finalizar el proceso. |
hard_rss_limit_mb | 0 | 0 | Cuando el RSS del proceso alcanza este límite, el proceso finaliza. |
soft_rss_limit_mb | 0 | 0 | Cuando el RSS del proceso alcanza este límite, las asignaciones adicionales fallan o devuelven un null (según el valor de allocator_may_return_null ), hasta que el RSS vuelve a bajar para permitir nuevas asignaciones. |
allocator_release_to_os_interval_ms | N / A | 5000 | Solo afecta a un asignador de 64 bits. Si está configurado, intenta liberar la memoria no utilizada al sistema operativo, pero no más a menudo que este intervalo (en milisegundos). Si el valor es negativo, la memoria no se libera al sistema operativo. |
abort_on_error | true | true | Si se establece, la herramienta llama a abort() en lugar de _exit() después de imprimir el mensaje de error. |
Validación
Actualmente, no hay pruebas CTS específicas para Scudo. En su lugar, asegúrese de que las pruebas CTS pasen con o sin Scudo habilitado para un binario dado para verificar que no afecte el dispositivo.
Solución de problemas
Si se detecta un problema no recuperable, el asignador muestra un mensaje de error en el descriptor de error estándar y luego finaliza el proceso. Los seguimientos de pila que conducen a la terminación se agregan en el registro del sistema. La salida generalmente comienza con Scudo ERROR:
seguido de un breve resumen del problema junto con cualquier sugerencia.
Aquí hay una lista de los mensajes de error actuales y sus posibles causas:
- Encabezado de
corrupted chunk header
: la verificación de la suma de comprobación del encabezado de fragmento ha fallado. Es probable que esto se deba a una de dos cosas: el encabezado se sobrescribió (parcial o totalmente) o el puntero pasado a la función no es un fragmento. -
race on chunk header
: dos subprocesos diferentes intentan manipular el mismo encabezado al mismo tiempo. Esto suele ser un síntoma de una condición de carrera o una falta general de bloqueo al realizar operaciones en ese fragmento. -
invalid chunk state
: el fragmento no se encuentra en el estado esperado para una operación determinada; por ejemplo, no se asigna al intentar liberarlo o no se pone en cuarentena al intentar reciclarlo. Un doble gratis es la razón típica de este error. -
misaligned pointer
: los requisitos básicos de alineación se aplican estrictamente: 8 bytes en plataformas de 32 bits y 16 bytes en plataformas de 64 bits. Si un puntero pasado a nuestras funciones no se ajusta a ellas, el puntero pasado a una de las funciones está desalineado. -
allocation type mismatch
: cuando esta opción está habilitada, una función de desasignación invocada en un fragmento debe coincidir con el tipo de función que se invocó para asignarlo. Este tipo de desajuste puede presentar problemas de seguridad. -
invalid sized delete
: cuando se usa el operador de eliminación de tamaño C 14 y la verificación opcional está habilitada, hay una discrepancia entre el tamaño que se pasó al desasignar un fragmento y el tamaño que se solicitó al asignarlo. Suele ser un problema del compilador o una confusión de tipos en el objeto que se desasigna. -
RSS limit exhausted
: Se ha excedido el RSS máximo especificado opcionalmente.
Si está depurando un bloqueo en el propio sistema operativo, puede usar una compilación de sistema operativo HWASan . Si está depurando un bloqueo en una aplicación, también es posible usar una compilación de aplicación HWASan .
,Scudo es un asignador de memoria dinámico en modo de usuario, o asignador de montón , diseñado para resistir las vulnerabilidades relacionadas con el montón (como el desbordamiento de búfer basado en montón, el uso después de liberar y el doble libre ) mientras mantiene el rendimiento. Proporciona las primitivas estándar de asignación y desasignación de C (como malloc y free), así como las primitivas de C (como new y delete).
Scudo es más una mitigación que un detector de errores de memoria completo como AddressSanitizer (ASan) .
Desde el lanzamiento de Android 11, scudo se usa para todo el código nativo (excepto en dispositivos con poca memoria, donde todavía se usa jemalloc). En tiempo de ejecución, Scudo atiende todas las asignaciones y desasignaciones de almacenamiento dinámico nativo para todos los ejecutables y sus dependencias de biblioteca, y el proceso se cancela si se detecta corrupción o comportamiento sospechoso en el almacenamiento dinámico.
Scudo es de código abierto y parte del proyecto compilador-rt de LLVM. La documentación está disponible en https://llvm.org/docs/ScudoHardenedAllocator.html . El tiempo de ejecución de Scudo se envía como parte de la cadena de herramientas de Android y se agregó compatibilidad con Soong y Make para permitir una habilitación sencilla del asignador en un binario.
Puede habilitar o deshabilitar la mitigación adicional dentro del asignador mediante las opciones que se describen a continuación.
personalización
Algunos parámetros del asignador se pueden definir por proceso de varias maneras:
- Estáticamente: defina una función
__scudo_default_options
en el programa que devuelva la cadena de opciones para analizar. Esta función debe tener el siguiente prototipo:extern "C" const char *__scudo_default_options()
. - Dinámicamente: use la variable de entorno
SCUDO_OPTIONS
que contiene la cadena de opciones que se va a analizar. Las opciones definidas de esta manera anulan cualquier definición realizada a través__scudo_default_options
.
Las siguientes opciones están disponibles.
Opción | predeterminado de 64 bits | predeterminado de 32 bits | Descripción |
---|---|---|---|
QuarantineSizeKb | 256 | 64 | El tamaño (en KB) de la cuarentena utilizada para retrasar la desasignación real de fragmentos. Un valor más bajo puede reducir el uso de memoria pero disminuir la efectividad de la mitigación; un valor negativo vuelve a los valores predeterminados. Establecer tanto esto como ThreadLocalQuarantineSizeKb en cero desactiva la cuarentena por completo. |
QuarantineChunksUpToSize | 2048 | 512 | El tamaño (en bytes) hasta el cual se pueden poner en cuarentena los fragmentos. |
ThreadLocalQuarantineSizeKb | 64 | 16 | El tamaño (en KB) del uso de la memoria caché por subproceso para descargar la cuarentena global. Un valor más bajo puede reducir el uso de la memoria, pero puede aumentar la contención en la cuarentena global. Establecer tanto esto como QuarantineSizeKb en cero desactiva la cuarentena por completo. |
DeallocationTypeMismatch | false | false | Habilita el informe de errores en malloc/delete, new/free, new/delete[] |
DeleteSizeMismatch | true | true | Habilita el informe de errores sobre la falta de coincidencia entre los tamaños de nuevo y eliminado. |
ZeroContents | false | false | Habilita el contenido de cero fragmentos en la asignación y desasignación. |
allocator_may_return_null | false | false | Especifica que el asignador puede devolver un valor nulo cuando se produce un error recuperable, en lugar de finalizar el proceso. |
hard_rss_limit_mb | 0 | 0 | Cuando el RSS del proceso alcanza este límite, el proceso finaliza. |
soft_rss_limit_mb | 0 | 0 | Cuando el RSS del proceso alcanza este límite, las asignaciones adicionales fallan o devuelven un null (según el valor de allocator_may_return_null ), hasta que el RSS vuelve a bajar para permitir nuevas asignaciones. |
allocator_release_to_os_interval_ms | N / A | 5000 | Solo afecta a un asignador de 64 bits. Si está configurado, intenta liberar la memoria no utilizada al sistema operativo, pero no más a menudo que este intervalo (en milisegundos). Si el valor es negativo, la memoria no se libera al sistema operativo. |
abort_on_error | true | true | Si se establece, la herramienta llama a abort() en lugar de _exit() después de imprimir el mensaje de error. |
Validación
Actualmente, no hay pruebas CTS específicas para Scudo. En su lugar, asegúrese de que las pruebas CTS pasen con o sin Scudo habilitado para un binario dado para verificar que no afecte el dispositivo.
Solución de problemas
Si se detecta un problema no recuperable, el asignador muestra un mensaje de error en el descriptor de error estándar y luego finaliza el proceso. Los seguimientos de pila que conducen a la terminación se agregan en el registro del sistema. La salida generalmente comienza con Scudo ERROR:
seguido de un breve resumen del problema junto con cualquier sugerencia.
Aquí hay una lista de los mensajes de error actuales y sus posibles causas:
- Encabezado de
corrupted chunk header
: la verificación de la suma de comprobación del encabezado de fragmento ha fallado. Es probable que esto se deba a una de dos cosas: el encabezado se sobrescribió (parcial o totalmente) o el puntero pasado a la función no es un fragmento. -
race on chunk header
: dos subprocesos diferentes intentan manipular el mismo encabezado al mismo tiempo. Esto suele ser un síntoma de una condición de carrera o una falta general de bloqueo al realizar operaciones en ese fragmento. -
invalid chunk state
: el fragmento no se encuentra en el estado esperado para una operación determinada; por ejemplo, no se asigna al intentar liberarlo o no se pone en cuarentena al intentar reciclarlo. Un doble gratis es la razón típica de este error. -
misaligned pointer
: los requisitos básicos de alineación se aplican estrictamente: 8 bytes en plataformas de 32 bits y 16 bytes en plataformas de 64 bits. Si un puntero pasado a nuestras funciones no se ajusta a ellas, el puntero pasado a una de las funciones está desalineado. -
allocation type mismatch
: cuando esta opción está habilitada, una función de desasignación invocada en un fragmento debe coincidir con el tipo de función que se invocó para asignarlo. Este tipo de desajuste puede presentar problemas de seguridad. -
invalid sized delete
: cuando se usa el operador de eliminación de tamaño C 14 y la verificación opcional está habilitada, hay una discrepancia entre el tamaño que se pasó al desasignar un fragmento y el tamaño que se solicitó al asignarlo. Suele ser un problema del compilador o una confusión de tipos en el objeto que se desasigna. -
RSS limit exhausted
: Se ha excedido el RSS máximo especificado opcionalmente.
Si está depurando un bloqueo en el propio sistema operativo, puede usar una compilación de sistema operativo HWASan . Si está depurando un bloqueo en una aplicación, también es posible usar una compilación de aplicación HWASan .