Extensión de etiquetado de memoria de brazo

Arm v9 presenta Arm Memory Tagging Extension (MTE), una implementación de hardware de memoria etiquetada.

En un alto nivel, MTE etiqueta cada asignación/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 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 en 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 liberar y de 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 funcionamiento MTE

MTE tiene tres modos de funcionamiento:

  • Modo síncrono (SYNC)
  • Modo asíncrono (ASYNC)
  • Modo asimétrico (ASYMM)

Modo síncrono (SYNC)

Este modo está optimizado para la corrección de la detección de errores sobre el rendimiento y se puede utilizar como una herramienta precisa de detección de errores, cuando es aceptable una mayor sobrecarga de rendimiento. Cuando está habilitado, MTE SYNC actúa como una mitigación de seguridad. Si la etiqueta no coincide, el procesador aborta la ejecución inmediatamente y finaliza el proceso con SIGSEGV (código SEGV_MTESERR ) e información completa sobre el acceso a la memoria y la dirección que falla.

Recomendamos utilizar este modo durante las pruebas como alternativa a HWASan/KASAN o en producción cuando el proceso de destino representa una superficie de ataque vulnerable. Además, cuando el modo ASYNC ha indicado la presencia de un error, se puede obtener un informe de error preciso utilizando las API de tiempo de ejecución para cambiar la ejecución al modo SYNC.

Cuando se ejecuta en modo SYNC, el asignador de Android registra seguimientos de pila para todas las asignaciones y desasignaciones y los usa para proporcionar mejores informes de error que incluyen una explicación de un error de memoria, como uso después de liberar o desbordamiento de búfer, y la pila rastros de los eventos de memoria relevantes. Dichos informes brindan más información contextual y facilitan el seguimiento y la reparación de errores.

Modo asíncrono (ASYNC)

Este modo está optimizado para el rendimiento sobre la precisión de los informes de errores y se puede utilizar como detección de sobrecarga baja para errores de seguridad de la memoria.
En caso de discrepancia de etiquetas, el procesador continúa la ejecución hasta la entrada del kernel más cercana (por ejemplo, una llamada al sistema o una interrupción del temporizador), donde finaliza el proceso con SIGSEGV (código SEGV_MTEAERR ) sin registrar la dirección que falla o el acceso a la memoria.
Recomendamos usar este modo en producción en bases de código bien probadas donde se sabe que la densidad de errores de seguridad de la memoria es baja, lo que se logra usando el modo SYNC durante la prueba.

Modo asimétrico (ASYMM)

Una característica adicional en Arm v8.7-A, el modo MTE asimétrico proporciona verificación síncrona de lecturas de memoria y verificación asíncrona de escrituras de memoria, con un rendimiento similar al del modo ASYNC. En la mayoría de las situaciones, este modo es una mejora sobre el modo ASYNC y recomendamos usarlo en lugar de ASYNC siempre que esté disponible.

Por este motivo, ninguna de las API descritas a continuación menciona el modo asimétrico. En su lugar, el sistema operativo se puede configurar para usar siempre el modo asimétrico cuando se solicita asíncrono. Consulte la sección "Configuración del nivel de MTE preferido específico de la CPU" para obtener más información.

MTE en el espacio de usuario

Las siguientes secciones describen cómo se puede habilitar MTE para los procesos y aplicaciones del sistema. MTE está deshabilitado de forma predeterminada, a menos que una de las siguientes opciones esté configurada para un proceso en particular (vea para qué componentes MTE está habilitado a continuación ).

Habilitación de MTE mediante el sistema de compilación

Como propiedad de todo el proceso, MTE está controlado por la configuración de tiempo de compilación del ejecutable principal. Las siguientes opciones permiten cambiar esta configuración para ejecutables individuales o para subdirectorios completos en el árbol fuente. La configuración se ignora en las bibliotecas o en cualquier destino que no sea ni ejecutable ni de prueba.

1. Habilitación de MTE en Android.bp ( ejemplo ), para un proyecto en particular:

Modo MTE Ajuste
MTE asíncrono
  sanitize: {
  memtag_heap: true,
  }
MTE síncrono
  sanitize: {
  memtag_heap: true,
  diag: {
  memtag_heap: true,
  },
  }

o en Android.mk:

Modo MTE Ajuste
Asynchronous MTE LOCAL_SANITIZE := memtag_heap
Synchronous MTE LOCAL_SANITIZE := memtag_heap
LOCAL_SANITIZE_DIAG := memtag_heap

2. Habilitar MTE en un subdirectorio en el árbol de fuentes usando una variable de producto:

modo MTE Incluir lista Lista de exclusión
asíncrono MEMTAG_HEAP_ASYNC_INCLUDE_PATHS PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS
sincronizar MEMTAG_HEAP_SYNC_INCLUDE_PATHS PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS

o

Modo MTE Ajuste
MTE asíncrono MEMTAG_HEAP_ASYNC_INCLUDE_PATHS
MTE síncrono MEMTAG_HEAP_SYNC_INCLUDE_PATHS

o especificando la ruta de exclusión de un ejecutable:

Modo MTE Ajuste
MTE asíncrono MEMTAG_HEAP_EXCLUDE_PATHS PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS
MTE síncrono

Ejemplo, (uso similar a PRODUCT_CFI_INCLUDE_PATHS )

  RODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS=vendor/$(vendor)
  PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS=vendor/$(vendor)/projectA \
                                    vendor/$(vendor)/projectB

Habilitación de MTE usando las propiedades del sistema

La configuración de compilación anterior se puede anular en tiempo de ejecución configurando la siguiente propiedad del sistema:

arm64.memtag.process.<basename> = (off|sync|async)

Donde basename representa el nombre base del ejecutable.

Por ejemplo, para configurar /system/bin/ping o /data/local/tmp/ping para usar MTE asíncrono, use adb shell setprop arm64.memtag.process.ping async .

Habilitación de MTE mediante una variable de entorno

Otra forma de anular la configuración de compilación es definiendo la variable de entorno: MEMTAG_OPTIONS=(off|sync|async) Si tanto la variable de entorno como la propiedad del sistema están definidas, la variable tiene prioridad.

Habilitación de MTE para aplicaciones

Si no se especifica, MTE está deshabilitado de manera predeterminada, pero las aplicaciones que desean 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 utilizados por la aplicación y puede anularse para procesos individuales configurando la etiqueta <process> .

Para la experimentación, los cambios de compatibilidad se pueden usar para establecer el valor predeterminado del atributo memtagMode para una aplicación 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. Establecer NATIVE_MEMTAG_ASYNC o NATIVE_MEMTAG_SYNC habilita MTE para una aplicación en particular.
Alternativamente, esto se puede configurar usando el comando am de la siguiente manera:

$ adb shell am compat enable NATIVE_MEMTAG_[A]SYNC my.app.name

Creación de una imagen del sistema MTE

Recomendamos enfáticamente habilitar MTE en todos los binarios nativos durante el desarrollo y la puesta en marcha. Esto ayuda a detectar temprano errores de seguridad de la memoria y proporciona una cobertura de usuario realista, si está habilitado en las compilaciones de prueba.

Recomendamos encarecidamente habilitar MTE en modo síncrono en todos los binarios nativos durante el desarrollo.

SANITIZE_TARGET=memtag_heap SANITIZE_TARGET_DIAG=memtag_heap m

Al igual que con cualquier variable en el sistema de compilación, SANITIZE_TARGET se puede usar como una variable de entorno o una make de creación (por ejemplo, en un archivo product.mk ).
Tenga en cuenta que esto habilita MTE para todos los procesos nativos, pero no para las aplicaciones (que se bifurcan de zygote64 ) para las que se puede habilitar MTE siguiendo las instrucciones anteriores .

Configuración del nivel de MTE preferido específico de la CPU

En algunas CPU, el rendimiento de MTE en modo 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 ejecutar estos procesos en modo SYNC en CPU específicas, el valor sync debe escribirse en la entrada sysfs /sys/devices/system/cpu/cpu<N>/mte_tcf_preferred en el momento del arranque. Esto se puede hacer con un guión de inicio. Por ejemplo, para configurar las CPU 0-1 para ejecutar procesos en modo ASYNC en modo SYNC, y las CPU 2-3 para usar la ejecución en modo ASYMM, se puede agregar lo siguiente a la cláusula de inicio de un script de inicio 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

Los lápidas de los procesos en modo ASYNC que se ejecutan en modo SYNC contendrán un seguimiento de pila preciso de la ubicación del error de memoria. Sin embargo, no incluirán un seguimiento de pila 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.
Deshabilita la inicialización de 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.

  • La configuración predeterminada es M_MEMTAG_TUNING_BUFFER_OVERFLOW .
  • M_MEMTAG_TUNING_BUFFER_OVERFLOW : permite la detección determinista de errores de desbordamiento y subdesbordamiento del búfer lineal mediante la asignación de valores de etiqueta distintos a las asignaciones adyacentes. Este modo tiene una posibilidad ligeramente reducida de detectar errores de uso después de liberar porque solo la mitad de los posibles valores de etiqueta están disponibles para cada ubicación de memoria. Tenga en cuenta que MTE no puede detectar el desbordamiento dentro del mismo gránulo de etiqueta (fragmento alineado de 16 bytes) y puede pasar por alto pequeños desbordamientos incluso en este modo. Tal desbordamiento no puede ser la causa de la corrupción de la memoria, porque la memoria dentro de un gránulo nunca se usa para asignaciones múltiples.
  • M_MEMTAG_TUNING_UAF : habilita etiquetas aleatorias independientes para una probabilidad uniforme de ~93 % de detectar errores tanto espaciales (desbordamiento de búfer) como temporales (uso después de liberar).

Además de las API descritas anteriormente, es posible que los usuarios experimentados deseen tener en cuenta lo siguiente:

  • La configuración del registro de hardware PSTATE.TCO puede suprimir temporalmente la verificación de etiquetas ( ejemplo ). Por ejemplo, al copiar un rango de memoria con contenidos de etiquetas desconocidos o abordar 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 la pila de asignación y desasignación. Esta funcionalidad requiere acceso a los bits de la etiqueta y se habilita al pasar el indicador SA_EXPOSE_TAGBITS al configurar el controlador de señal. Se recomienda que cualquier programa que establezca su propio controlador de señales y delegue bloqueos desconocidos al sistema haga lo mismo.

MTE en el núcleo

Para habilitar KASAN acelerado por MTE para el kernel, configure el kernel con CONFIG_KASAN=y , CONFIG_KASAN_HW_TAGS=y . Estas configuraciones están habilitadas de forma predeterminada en los kernels GKI, a partir de Android 12-5.10 .
Esto se puede controlar en el momento del arranque utilizando los siguientes argumentos de la línea de comandos:

  • kasan=[on|off] - activa o desactiva KASAN (predeterminado: on )
  • kasan.mode=[sync |async ] - elige entre modo síncrono y asíncrono (predeterminado: sync )
  • kasan.stacktrace=[on|off] : si recopilar seguimientos de pila (predeterminado: on )
    • la recopilación de seguimiento de pila también requiere stack_depot_disable=off .
  • kasan.fault=[report|panic] - ya sea para imprimir solo el informe, o también generar pánico en el kernel (predeterminado: report ). Independientemente de esta opción, la verificación de etiquetas se deshabilita después del primer error informado.

Recomendamos encarecidamente utilizar el modo SYNC durante la puesta en marcha, el desarrollo y las pruebas. Esta opción debe habilitarse globalmente para todos los procesos que utilicen la variable de entorno o con el sistema de compilación . En este modo, los errores se detectan temprano en el 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.

Recomendamos enfáticamente usar 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 API de tiempo de ejecución para cambiar al modo SYNC y obtener un seguimiento de pila preciso de un conjunto de usuarios de muestra.

Recomendamos encarecidamente configurar el nivel de MTE preferido específico de la CPU para el SoC. El modo Asymm normalmente tiene las mismas características de rendimiento que ASYNC, y casi siempre es preferible a él. Los núcleos pequeños en orden a menudo muestran un rendimiento similar en los tres modos y se pueden configurar para preferir SYNC.

Los desarrolladores deben comprobar la presencia de bloqueos consultando /data/tombstones , logcat o supervisando la canalización de DropboxManager del proveedor en busca de errores del usuario final. Para obtener más información sobre la depuración del código nativo de Android, consulte la información aquí .

Componentes de plataforma habilitados para MTE

En Android 12, una serie de componentes del sistema críticos 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:

  • Demonios y utilidades de red (con la excepción de netd )
  • Bluetooth, SecureElement, NFC HAL y aplicaciones del sistema
  • demonio statsd
  • system_server
  • zygote64 (para permitir que las aplicaciones opten por usar MTE)

Estos objetivos fueron seleccionados en base a los siguientes criterios:

  • Un proceso privilegiado (definido como un proceso que tiene acceso a algo que el dominio SELinux unprivileged_app no ​​tiene)
  • Procesa entradas no confiables ( Regla de dos )
  • Ralentización aceptable del rendimiento (la ralentización no crea una latencia visible para el usuario)

Alentamos a los proveedores a habilitar MTE en producción para más componentes, siguiendo los criterios mencionados anteriormente. Durante el desarrollo, recomendamos probar estos componentes usando el modo SYNC, para detectar errores fáciles de corregir y evaluar el impacto de ASYNC en su rendimiento.
En el futuro, Android planea expandir la lista de componentes del sistema en los que MTE está habilitado, guiado por las características de rendimiento de los próximos diseños de hardware.