Scudo es un asignador de memoria dinámico en modo de usuario, o asignador de montón, diseñado para ser resistente a las vulnerabilidades relacionadas con el montón (como el desbordamiento de búfer basado en montón, el uso después de la liberación, y la liberación doble) y, al mismo tiempo, mantener el rendimiento. Proporciona las primitivas de asignación y desasignación estándar 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, en los que todavía se usa jemalloc). En el tiempo de ejecución, Scudo atiende todas las asignaciones y desasignaciones de montón nativas para todos los ejecutables y sus dependencias de biblioteca, y el proceso se anula si se detecta corrupción o comportamiento sospechoso en el montón.
Scudo es de código abierto y forma parte del proyecto compiler-rt de LLVM. La documentación está disponible en https://llvm.org/docs/ScudoHardenedAllocator.html. El tiempo de ejecución de Scudo se incluye como parte de la cadena de herramientas de Android, y se agregó compatibilidad con Soong y Make para permitir la habilitación sencilla del asignador en un objeto binario.
Puedes habilitar o inhabilitar la mitigación adicional dentro del asignador con las opciones que se describen a continuación.
Personalización
Algunos parámetros del asignador se pueden definir por proceso de varias maneras:
- De forma estática: Define una función
__scudo_default_optionsen el programa que muestre la cadena de opciones que se analizará. Esta función debe tener el siguiente prototipo:extern "C" const char *__scudo_default_options(). - De forma dinámica: Usa la variable de entorno
SCUDO_OPTIONSque contiene la cadena de opciones que se analizará. Las opciones definidas de esta manera anulan cualquier definición realizada a través de__scudo_default_options.
Las siguientes opciones están disponibles.
| Opción | Valor predeterminado de 64 bits | Valor predeterminado de 32 bits | Descripción |
|---|---|---|---|
QuarantineSizeKb |
256 |
64 |
Es el tamaño (en KB) de la cuarentena que se usa para retrasar la desasignación real de fragmentos. Un valor más bajo puede reducir el uso de memoria, pero disminuye la eficacia de la mitigación. Un valor negativo vuelve a los valores predeterminados. Si se establece este valor y ThreadLocalQuarantineSizeKb en cero, se inhabilita la cuarentena por completo. |
QuarantineChunksUpToSize |
2048 |
512 |
Es el tamaño (en bytes) hasta el que se pueden poner en cuarentena los fragmentos. |
ThreadLocalQuarantineSizeKb |
64 |
16 |
Es el tamaño (en KB) del uso de caché por subproceso para descargar la cuarentena global.
Un valor más bajo puede reducir el uso de memoria, pero podría aumentar la contención en la cuarentena global. Si se establece este valor y QuarantineSizeKb en cero, se inhabilita la cuarentena por completo. |
DeallocationTypeMismatch |
false |
false |
Habilita la generación de informes de errores en malloc/delete, new/free, new/delete[] |
DeleteSizeMismatch |
true |
true |
Habilita la generación de informes de errores en la falta de coincidencia entre los tamaños de new y delete. |
ZeroContents |
false |
false |
Habilita el contenido de fragmentos cero en la asignación y la desasignación. |
allocator_may_return_null |
false |
false |
Especifica que el asignador puede mostrar 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 posteriores fallan o
muestran 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 |
5000 |
N/A | Solo afecta a un asignador de 64 bits. Si se establece, intenta liberar la memoria sin usar en el SO, pero no con más frecuencia que este intervalo (en milisegundos). Si el valor es negativo, la memoria no se libera en el SO. |
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 de CTS específicas para Scudo. En su lugar, asegúrate de que las pruebas de CTS pasen con o sin Scudo habilitado para un objeto binario determinado para verificar que no afecte al 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 finalización se agregan al registro del sistema.
Por lo general, el resultado comienza con Scudo ERROR: seguido de un breve resumen del problema junto con cualquier puntero.
Esta es una lista de los mensajes de error actuales y sus posibles causas:
corrupted chunk header: Falló la verificación de suma de verificación del encabezado del fragmento. Es probable que se deba a una de estas dos cosas: se reemplazó el encabezado (parcial o totalmente) o el puntero que se pasó a la función no es un fragmento.race on chunk header: Dos subprocesos diferentes intentan manipular el mismo encabezado al mismo tiempo. Por lo general, esto es un síntoma de una condición de carrera o una falta general de bloqueo cuando se realizan operaciones en ese fragmento.invalid chunk state: El fragmento no está en el estado esperado para una operación determinada, por ejemplo, no se asigna cuando se intenta liberarlo o no se pone en cuarentena cuando se intenta reciclarlo. Una liberación doble es el motivo típico 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 que se pasa a nuestras funciones no se ajusta a esos requisitos, el puntero que se pasa a una de las funciones está desalineado.allocation type mismatch: Cuando se habilita esta opción, una función de desasignación llamada en un fragmento debe coincidir con el tipo de función que se llamó para asignarlo. Este tipo de falta de coincidencia puede generar problemas de seguridad.invalid sized delete: Cuando se usa el operador de eliminación con tamaño de C++14 y se habilita la verificación opcional, hay una falta de coincidencia entre el tamaño que se pasó cuando se desasignó un fragmento y el tamaño que se solicitó cuando se asignó. Por lo general, se trata de un problema del compilador o una confusión de tipos en el objeto que se desasigna.RSS limit exhausted: Se superó el RSS máximo especificado de forma opcional.
Si estás depurando una falla en el SO, puedes usar una compilación del SO de HWASan. Si estás depurando una falla en una app, también es posible usar una compilación de la app de HWASan también.