Arm v9 introduce la extensión de etiquetado de memoria (MTE) de Arm, una implementación de hardware de memoria etiquetada.
A alto nivel, la MTE etiqueta cada asignación o desasignación de memoria con metadatos adicionales. Asigna una etiqueta a una ubicación de memoria, que luego se puede asociar con punteros que hacen referencia a esa ubicación de memoria. En el tiempo de ejecución, la CPU verifica que el puntero y las etiquetas de metadatos coincidan en cada carga y almacenamiento.
En Android 12, el asignador de memoria de montón del kernel y del espacio de usuario puede aumentar cada asignación con metadatos. Esto ayuda a detectar errores de uso después de liberación y desbordamiento de búfer, que son la fuente más común de errores de seguridad de la memoria en nuestras bases de código.
Modos de operación de MTE
El MTE tiene tres modos de operación:
- Modo síncrono (SYNC)
- Modo asíncrono (ASYNC)
- Modo asimétrico (ASYMM)
Modo síncrono (SYNC)
Este modo está optimizado para la precisión de la detección de errores por sobre el rendimiento y se puede usar como una herramienta de detección de errores precisa; se debe tener en cuenta que la sobrecarga de rendimiento será mayor. Cuando MTE SYNC está habilitado, también actúa como mitigación de seguridad.
En una discrepancia de etiqueta, el procesador anula la ejecución de inmediato y finaliza el proceso con SIGSEGV
(código SEGV_MTESERR
) y la información completa sobre el acceso a la memoria y la dirección con errores.
Recomendamos usar este modo durante las pruebas como alternativa a HWASan/KASAN o en producción cuando el proceso objetivo representa una superficie vulnerable a ataques. Además, cuando el modo ASYNC indica la presencia de un error, se puede obtener un informe de errores preciso usando las APIs de entorno de ejecución para cambiar la ejecución al modo SYNC.
Cuando se ejecuta en modo SYNC, el asignador de Android registra los seguimientos de pila de todas las asignaciones y desasignaciones, y los usa para proporcionar mejores informes de errores que incluyen una explicación de un error de memoria, como el uso después de la liberación o el desbordamiento de búfer, y los seguimientos de pila de los eventos de memoria relevantes. Estos informes proporcionan más información contextual y hacen que los errores sean más fáciles de rastrear y corregir.
Modo asíncrono (ASYNC)
Este modo está optimizado para el rendimiento por sobre la precisión de los informes de errores y se puede usar para detectar, con una baja sobrecarga, errores de seguridad de memoria.
En una discrepancia de etiqueta, el procesador continúa la ejecución hasta la entrada de kernel más cercana (por ejemplo, una llamada de sistema o interrupción del temporizador), donde finaliza
el proceso con SIGSEGV
(código SEGV_MTEAERR
) sin
registrar la dirección o el acceso a la memoria con errores.
Recomendamos usar este modo en producción en bases de código bien probadas en las que
se sabe que la densidad de errores de seguridad de la memoria es baja, lo que se logra usando
el modo SYNC durante las pruebas.
Modo asimétrico (ASYMM)
El modo MTE asimétrico, una función adicional en Arm v8.7-A, proporciona una verificación síncrona de las lecturas de memoria y una verificación asíncrona de las escrituras de memoria, con un rendimiento similar al del modo ASYNC. En la mayoría de los casos, este modo es una mejora con respecto al modo ASYNC, y recomendamos usarlo en lugar de ASYNC siempre que esté disponible.
Por este motivo, ninguna de las APIs que se describen a continuación menciona el modo asimétrico. En cambio, el SO se puede configurar para que siempre use el modo asimétrico cuando se solicite el modo asíncrono. Consulta la sección "Configura el nivel de MTE preferido específico de la CPU" para obtener más información.
MTE en el espacio del usuario
En las siguientes secciones, se describe cómo se puede habilitar MTE para los procesos y las apps del sistema. MTE está inhabilitada de forma predeterminada, a menos que se establezca una de las opciones que se indican a continuación para un proceso en particular (consulta para qué componentes está habilitada MTE a continuación).
Cómo habilitar MTE con el sistema de compilación
Como propiedad de todo el proceso, MTE se controla con el parámetro de configuración del tiempo de compilación del ejecutable principal. Las siguientes opciones permiten cambiar este parámetro de configuración para ejecutables individuales o para subdirectorios completos en el árbol de origen. El parámetro de configuración se ignora en las bibliotecas o en cualquier destino que no sea ejecutable ni una prueba.
1. Habilita MTE en Android.bp
(ejemplo) para un proyecto en particular:
Modo MTE | Configuración |
---|---|
MTE asíncrono | sanitize: { memtag_heap: true, } |
MTE síncrona | sanitize: { memtag_heap: true, diag: { memtag_heap: true, }, } |
o en Android.mk:
Modo MTE | Configuración |
---|---|
Asynchronous MTE |
LOCAL_SANITIZE := memtag_heap |
Synchronous MTE |
LOCAL_SANITIZE := memtag_heap LOCAL_SANITIZE_DIAG := memtag_heap |
2. Habilitar MTE en un subdirectorio del árbol de fuentes con una variable de producto:
Modo MTE | Lista de inclusión | Lista de exclusión |
---|---|---|
asincrónico | PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS
MEMTAG_HEAP_ASYNC_INCLUDE_PATHS |
PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS
MEMTAG_HEAP_EXCLUDE_PATHS |
sincronizar | PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS
MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
o
Modo MTE | Configuración |
---|---|
MTE asíncrono | MEMTAG_HEAP_ASYNC_INCLUDE_PATHS |
MTE síncrona | MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
o bien especificando la ruta de exclusión de un archivo ejecutable:
Modo MTE | Configuración |
---|---|
MTE asíncrono | PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS
MEMTAG_HEAP_EXCLUDE_PATHS |
MTE síncrona |
Ejemplo (uso similar a PRODUCT_CFI_INCLUDE_PATHS
):
PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS=vendor/$(vendor) PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS=vendor/$(vendor)/projectA \ vendor/$(vendor)/projectB
Cómo habilitar MTE con propiedades del sistema
Puedes anular la configuración de compilación anterior en el tiempo de ejecución configurando la siguiente propiedad del sistema:
arm64.memtag.process.<basename> = (off|sync|async)
Aquí, basename
representa el nombre base del ejecutable.
Por ejemplo, para configurar /system/bin/ping
o /data/local/tmp/ping
para usar MTE asíncrono, usa adb shell setprop arm64.memtag.process.ping async
.
Habilita MTE con una variable de entorno
Otra forma de anular el parámetro de configuración de compilación para los procesos nativos (no aplicaciones) es definir la variable de entorno: MEMTAG_OPTIONS=(off|sync|async)
. Si se definen tanto la variable de entorno como la propiedad del sistema, la variable tiene prioridad.
Cómo habilitar la MTE para apps
Si no se especifica, MTE está inhabilitado de forma predeterminada, pero las apps que quieran usar MTE pueden hacerlo configurando android:memtagMode
en la etiqueta <application>
o <process>
en AndroidManifest.xml
.
android:memtagMode=(off|default|sync|async)
Cuando se establece en la etiqueta <application>
, el atributo afecta a todos los procesos que usa la app y se puede anular en procesos individuales si configuras la etiqueta <process>
.
Para la experimentación, se pueden usar los cambios de compatibilidad para establecer el valor predeterminado del atributo memtagMode
para una app que no especifica ningún valor en el manifiesto (o especifica default
).
Estos se pueden encontrar en System > Advanced > Developer options
> App Compatibility Changes
en el menú de configuración global. La configuración de NATIVE_MEMTAG_ASYNC
o NATIVE_MEMTAG_SYNC
habilita MTE para una app en particular.
Como alternativa, esto se puede configurar usando el comando am
de la siguiente manera:
$ adb shell am compat enable NATIVE_MEMTAG_[A]SYNC my.app.name
Compila una imagen del sistema de MTE
Te recomendamos que habilites MTE en todos los archivos binarios nativos durante el desarrollo y la puesta en marcha. Esto ayuda a detectar errores de seguridad de la memoria de forma anticipada y proporciona una cobertura realista de los usuarios si se habilita en las compilaciones de prueba.
Recomendamos habilitar MTE en modo síncrono en todos los archivos binarios nativos durante el desarrollo.
SANITIZE_TARGET=memtag_heap SANITIZE_TARGET_DIAG=memtag_heap m
Al igual que con cualquier variable del sistema de compilación, SANITIZE_TARGET
se puede usar como una variable de entorno o un parámetro de configuración de make
(por ejemplo, en un archivo product.mk
).
Ten en cuenta que esto habilita la MTE para todos los procesos nativos, pero no para las
apps (que se bifurcan desde zygote64
) para las que se puede habilitar la MTE
siguiendo las instrucciones anteriores.
Configura el nivel de MTE preferido específico de la CPU
En algunas CPU, el rendimiento de MTE en los modos ASYMM o incluso SYNC puede ser similar al de ASYNC. Esto hace que valga la pena habilitar verificaciones más estrictas en esas CPU cuando se solicita un modo de verificación menos estricto, para obtener los beneficios de detección de errores de las verificaciones más estrictas sin las desventajas de rendimiento.
De forma predeterminada, los procesos configurados para ejecutarse en modo ASYNC se ejecutarán en modo ASYNC
en todas las CPU. Para configurar el kernel para que ejecute estos procesos en modo SYNC en CPUs específicas, el valor sync se debe escribir en la entrada sysfs
/sys/devices/system/cpu/cpu<N>/mte_tcf_preferred
en el momento del arranque. Esto se puede hacer con una secuencia de comandos de inicialización. Por ejemplo, para configurar las CPU 0 y 1 para que ejecuten procesos en modo ASYNC en modo SYNC, y las CPU 2 y 3 para que se ejecuten en modo ASYMM, se puede agregar lo siguiente a la cláusula init de una secuencia de comandos init del proveedor:
write /sys/devices/system/cpu/cpu0/mte_tcf_preferred sync write /sys/devices/system/cpu/cpu1/mte_tcf_preferred sync write /sys/devices/system/cpu/cpu2/mte_tcf_preferred asymm write /sys/devices/system/cpu/cpu3/mte_tcf_preferred asymm
Las marcas de eliminación de los procesos del modo ASYNC que se ejecutan en el modo SYNC contendrán un registro de pila preciso de la ubicación del error de memoria. Sin embargo, no incluirán un registro de seguimiento de asignación o desasignación. Estos seguimientos de pila solo están disponibles si el proceso está configurado para ejecutarse en modo SYNC.
int mallopt(M_THREAD_DISABLE_MEM_INIT, level)
donde level
es 0 o 1.
Inhabilita la inicialización de la memoria en malloc y evita cambiar las etiquetas de memoria
a menos que sea necesario para la corrección.
int mallopt(M_MEMTAG_TUNING, level)
donde level
es:
M_MEMTAG_TUNING_BUFFER_OVERFLOW
M_MEMTAG_TUNING_UAF
Selecciona la estrategia de asignación de etiquetas.
- El parámetro de configuración predeterminado es
M_MEMTAG_TUNING_BUFFER_OVERFLOW
. M_MEMTAG_TUNING_BUFFER_OVERFLOW
: Habilita la detección determinística de errores de desbordamiento y subdesbordamiento del búfer lineal asignando valores de etiquetas distintos a las asignaciones adyacentes. Este modo tiene una probabilidad ligeramente menor de detectar errores de uso después de liberar porque solo la mitad de los valores de etiquetas posibles están disponibles para cada ubicación de memoria. Ten en cuenta que MTE no puede detectar desbordamientos dentro del mismo fragmento de la etiqueta (fragmento alineado de 16 bytes) y puede pasar por alto desbordamientos pequeños incluso en este modo. Este desbordamiento no puede ser la causa de la corrupción de la memoria, ya que la memoria dentro de un gránulo nunca se usa para múltiples asignaciones.M_MEMTAG_TUNING_UAF
: Habilita etiquetas aleatorizadas de forma independiente para una probabilidad uniforme de aproximadamente el 93% de detectar errores espaciales (desbordamiento de búfer) y temporales (uso después de liberación).
Además de las APIs descritas anteriormente, los usuarios experimentados pueden tener en cuenta lo siguiente:
- Configurar el registro de hardware
PSTATE.TCO
puede suprimir temporalmente la verificación de etiquetas (ejemplo). Por ejemplo, cuando se copia un rango de memoria con contenido de etiquetas desconocido o cuando se aborda un cuello de botella de rendimiento en un bucle activo. - Cuando se usa
M_HEAP_TAGGING_LEVEL_SYNC
, el controlador de fallas del sistema proporciona información adicional, como seguimientos de pila de asignación y desasignación. Esta funcionalidad requiere acceso a los bits de etiquetas y se habilita pasando la marcaSA_EXPOSE_TAGBITS
cuando se configura el controlador de señales. Se recomienda que cualquier programa que establezca su propio controlador de señales y delegue fallas desconocidas al del sistema haga lo mismo.
MTE en el kernel
Para habilitar KASAN acelerado por MTE para el kernel, configúralo con CONFIG_KASAN=y
, CONFIG_KASAN_HW_TAGS=y
. Estos parámetros de configuración están habilitados de forma predeterminada en los kernels de GKI, a partir de Android
12-5.10
.
Esto se puede controlar en el momento del arranque con los siguientes argumentos de la línea de comandos:
kasan=[on|off]
: Habilita o inhabilita KASAN (el valor predeterminado eson
).kasan.mode=[sync|async]
: Elige entre el modo síncrono y el asíncrono (predeterminado:sync
).kasan.stacktrace=[on|off]
: Indica si se deben recopilar seguimientos de pila (predeterminado:on
).- La recopilación de seguimientos de pila también requiere
stack_depot_disable=off
.
- La recopilación de seguimientos de pila también requiere
kasan.fault=[report|panic]
: Indica si solo se debe imprimir el informe o si también se debe generar un error de kernel (el valor predeterminado esreport
). Independientemente de esta opción, la verificación de etiquetas se inhabilita después del primer error informado.
Uso recomendado
Te recomendamos que uses el modo SYNC durante la puesta en marcha, el desarrollo y las pruebas. Esta opción se debe habilitar de forma global para todos los procesos que usan la variable de entorno o con el sistema de compilación. En este modo, los errores se detectan en una etapa temprana del proceso de desarrollo, la base de código se estabiliza más rápido y se evita el costo de detectar errores más adelante en la producción.
Te recomendamos que uses el modo ASYNC en producción. Esto proporciona una herramienta de baja sobrecarga para detectar la presencia de errores de seguridad de la memoria en un proceso, así como una mayor defensa en profundidad. Una vez que se detecta un error, el desarrollador puede aprovechar las APIs de entorno de ejecución para cambiar al modo SYNC y obtener un seguimiento de pila preciso a partir de un conjunto de usuarios muestreados.
Te recomendamos que configures el nivel de MTE preferido específico de la CPU para el SoC. Por lo general, el modo asimétrico tiene las mismas características de rendimiento que el modo ASÍNCRONO y casi siempre es preferible. Los núcleos pequeños en orden suelen mostrar un rendimiento similar en los tres modos y se pueden configurar para que prefieran SYNC.
Los desarrolladores deben verificar la presencia de fallas con /data/tombstones
, logcat
o supervisando la canalización de DropboxManager
del proveedor para detectar errores del usuario final. Para obtener más información sobre la depuración de código nativo de Android, consulta la información aquí.
Componentes de la plataforma habilitados para MTE
En Android 12, varios componentes críticos del sistema para la seguridad usan MTE ASYNC para detectar fallas del usuario final y actuar como una capa adicional de defensa en profundidad. Estos componentes son los siguientes:
- Daemons y utilidades de redes (con la excepción de
netd
) - Bluetooth, SecureElement, HAL de NFC y apps del sistema
- Daemon de
statsd
system_server
zygote64
(para permitir que las apps habiliten el uso de MTE)
Estos objetivos se seleccionaron según los siguientes criterios:
- Un proceso con privilegios (definido como un proceso que tiene acceso a algo que el dominio SELinux de unprivileged_app no tiene)
- Procesa entradas no confiables (Regla de dos)
- Ralentización aceptable del rendimiento (la ralentización no crea latencia visible para el usuario)
Recomendamos a los proveedores que habiliten MTE en la producción para más componentes, según los criterios mencionados anteriormente. Durante el desarrollo, recomendamos probar estos componentes con el modo SYNC para detectar errores que se puedan corregir fácilmente y evaluar el impacto de ASYNC en su rendimiento.
En el futuro, Android planea expandir la lista de componentes del sistema en los que se habilita el MTE, guiándose por las características de rendimiento de los próximos diseños de hardware.