Cómo depurar la corrupción de la memoria con Address Sanitizer

En este documento, se muestra cómo habilitar herramientas especiales de depuración cuando se usa AGDE. Estas herramientas pueden ayudar con la corrupción de la memoria difìcil de diagnosticar y reemplazar errores.

HWAddress Sanitizer y Address Sanitizer

HWAddress Sanitizer (HWASan) y Address Sanitizer (ASan) son herramientas para la corrupción de la memoria que ayudan a depurarla y reemplazar errores, por ejemplo:

  • Desbordamiento y subdesbordamiento del búfer de pila
  • Desbordamiento y desbordamiento del búfer del montón
  • Uso de pila fuera de su alcance
  • Errores de liberación doble y liberación salvaje
  • Uso de pila después de la devolución (solo HWASan)

Te recomendamos que habilites HWASan o ASan solo cuando depures un problema o como parte de las pruebas automatizadas. Si bien estas herramientas tienen un buen rendimiento, su uso genera una sanción.

Comportamiento del tiempo de ejecución

Cuando se habilitan, HWASan y ASan verifican automáticamente la corrupción de la memoria en tu app.

Si se detecta un error de memoria, la app falla con un error SIGBART (anulación de la señal) y, luego, imprime un mensaje detallado para logcat. También se escribe una copia del mensaje en un archivo dentro de /data/tombstones.

El mensaje de error es similar al siguiente:

ERROR: HWAddressSanitizer: tag-mismatch on address 0x0042a0826510 at pc 0x007b24d90a0c
WRITE of size 1 at 0x0042a0826510 tags: 32/3d (ptr/mem) in thread T0
    #0 0x7b24d90a08  (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x2a08)
    #1 0x7b8f1e4ccc  (/apex/com.android.art/lib64/libart.so+0x198ccc)
    #2 0x7b8f1db364  (/apex/com.android.art/lib64/libart.so+0x18f364)
    #3 0x7b8f2ad8d4  (/apex/com.android.art/lib64/libart.so+0x2618d4)

0x0042a0826510 is located 0 bytes to the right of 16-byte region [0x0042a0826500,0x0042a0826510)
allocated here:
    #0 0x7b92a322bc  (/apex/com.android.runtime/lib64/bionic/libclang_rt.hwasan-aarch64-android.so+0x212bc)
    #1 0x7b24d909e0  (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x29e0)
    #2 0x7b8f1e4ccc  (/apex/com.android.art/lib64/libart.so+0x198ccc)

Requisitos previos

Requisitos de HWASan

Para usar HWASan, haz lo siguiente:

  • Debes usar AGDE 24.1.99 o una versión posterior.
  • La app debe compilarse con el NDK 26 o una versión posterior.
  • La app debe compilarse con el SDK de destino 34 o una versión posterior.
  • El objetivo debe ser un dispositivo arm64-v8a que ejecute Android 14 (nivel de API 34) o una versión posterior.

Usa la biblioteca C++ estándar compartida en tu proyecto

Debido a un problema conocido, ASan no es compatible con el control de excepciones de C++ cuando se usa libc++_static. Este problema no se observa cuando se utiliza libc++_shared.

HWASan tiene su propia implementación de los operadores new y delete, que no se pueden usar si la biblioteca estándar está vinculada de forma estática al proyecto.

Para cambiar este parámetro, consulta la sección Vincula la biblioteca C++ estándar de este documento.

Cómo habilitar la generación de punteros de marco

HWASan y ASan usan un desenredador rápido basado en punteros de marco para generar información de seguimiento de pila para los eventos de asignación y desasignación de memoria. Esto significa que debes habilitar la generación de punteros de marco en la configuración de tu compilador de C++ para usar estas funciones. Es decir, debes inhabilitar la optimización de la omisión de punteros de marco.

Para cambiar este parámetro, consulta la sección Habilita la generación de punteros de marco de este documento.

Configura tu proyecto de Visual Studio para usar HWASan o ASan

Habilita HWASan o ASan

Para habilitar HWASan o ASan, ve a Configuration Properties > General en Property Pages de tu proyecto.

Menú de propiedades de Solution Explorer de Visual Studio para el proyecto actual.

Figura 1: La opción Properties del proyecto en la ventana Solution Explorer de Visual Studio.

El diálogo Project Pages del proyecto que muestra las propiedades General y destaca la configuración Address Sanitizer.

Figura 2: El parámetro Address Sanitizer (ASan) en las propiedades generales del proyecto.

Para habilitar HWASan para tu proyecto, cambia el parámetro Address Sanitizer (ASan) a Hardware ASan Enabled (fsanitize=hwaddress).

Para habilitar ASan para tu proyecto, cambia el parámetro Address Sanitizer (ASan) a ASan Enabled (fsanitize=address).

Habilita la generación de punteros de marco

La generación de punteros de marco está controlada por el parámetro del compilador de C/C++ Omit Frame Pointer de que se encuentra en Property Pages de tu proyecto dentro de Configuration Properties > C/C++ > Optimization.

El diálogo Project Pages del proyecto que muestra las propiedades de Optimization de C/C++ y destaca la configuración Omit Frame Pointer.

Figura 3: Dónde encontrar el parámetro Omit Frame Pointer.

Cuando uses HWASan o ASan, establece el parámetro Omit Frame Pointer como No (-fno-omit-frame-pointer).

Vincula la biblioteca C++ estándar en el modo de biblioteca compartida

El parámetro de configuración del modo vinculador para la biblioteca C++ estándar se puede encontrar en Property Pages del proyecto dentro de Configuration Properties > General, en la sección Project Defaults.

El diálogo Project Pages del proyecto en el que se selecciona la categoría General y se destaca el parámetro Use of STL.

Figura 4: Dónde encontrar el parámetro de configuración del modo vinculador para la biblioteca C++ estándar.

Cuando uses HWASan o ASan, configura Use of STL como Use C++ Standard Libraries (.so). Este valor vincula la biblioteca C++ estándar al proyecto como una biblioteca compartida, que es necesaria para que HWASan y ASan funcionen de forma correcta.

Crea una configuración de compilación para usar Address Sanitizer

Si prefieres usar HWASan o ASan de manera transitoria, te recomendamos que no crees una nueva configuración de compilación solo para su uso. Este podría ser el caso si tu proyecto es pequeño, exploras la función o buscas la respuesta a un problema que descubres durante la prueba.

Sin embargo, si te resulta útil y planeas usarlo con regularidad, te recomendamos que crees una configuración de compilación nueva para HWASan o ASan, como se observa en la muestra Teapot. Puedes hacerlo si, por ejemplo, ejecutas Address Sanitizer con regularidad como parte de tus pruebas de unidades o durante las pruebas nocturnas de humo del juego.

Crear una configuración de compilación independiente puede ser especialmente útil si tienes un proyecto grande que consume una gran cantidad de bibliotecas de terceros diferentes que sueles vincular, de forma estática, con la biblioteca C++ estándar. La configuración de compilación dedicadas puede ayudar a garantizar que la configuración de tu proyecto siga siendo precisa en todo momento.

Para crear una configuración de compilación, desde Property Pages del proyecto, haz clic en el botón Configuration Manager… y, luego, abre el menú desplegable Active solution configuration. Luego, selecciona y crea una configuración de compilación nueva con un nombre adecuado (por ejemplo, HWASan habilitado).

Cómo usar HWASan con asignadores de memoria personalizados

HWASan intercepta automáticamente la memoria asignada a través de malloc (o new) para poder insertar etiquetas en punteros y verificar si hay discrepancias.

Sin embargo, cuando se usa un asignador de memoria personalizado, HWASan no puede interceptar automáticamente tus métodos de asignación de memoria personalizados. Por lo tanto, si quieres usar HWASan con tu asignador de memoria personalizado, instrumenta tu asignador de memoria para llamar a HWASan de forma explícita. Esto se puede hacer con solo unas pocas líneas de código.

Requisitos previos

Los métodos de HWASan a los que debes llamar se definen en este encabezado:

#include "sanitizer/hwasan_interface.h"

Instrumenta tu método de asignación de memoria

  1. Asignar objetos con alineación y nivel de detalle de bloque de 16 bytes Por ejemplo, si tienes un asignador de grupos que entrega objetos de tamaño fijo de 24 bytes, redondea tus asignaciones hasta 32 bytes y alinéalas a 16 bytes.

  2. Genera una etiqueta de 8 bits. Tu etiqueta no debe usar valores del 0 al 16, ya que esos valores están reservados para uso interno.

  3. Habilita HWASan para que comience a hacer un seguimiento de la región de memoria con esa etiqueta:

    __hwasan_tag_memory((void*) address, tag, size);
    
  4. Inyecta la etiqueta en los 8 bits superiores de tu puntero:

    address = __hwasan_tag_pointer((void*) address, tag);
    

Instrumenta tu método de desasignación de memoria

  1. Restablece la etiqueta de la región de memoria para que los accesos adicionales a través de los punteros etiquetados existentes fallen:

    __hwasan_tag_memory(__hwasan_tag_pointer(ptr, 0), 0, size);
    

Cómo trabajar con un grupo de objetos preasignados

Si tu asignador de memoria asigna objetos de antemano en un grupo y los muestra nuevamente en el grupo en lugar de liberarlos, tu método de desasignación puede reemplazar directamente la etiqueta de la memoria y el puntero con un valor nuevo:

```
__hwasan_tag_memory(__hwasan_tag_pointer(ptr, 0), tag, size);
ptr = __hwasan_tag_pointer((void*)ptr, tag);
```

Si usas esta técnica, tus métodos de asignación no necesitan etiquetar punteros ni bloques de memoria, pero sí etiquetar los punteros y los bloques de memoria cuando asignas previamente los objetos en tu grupo. Consulta el ejemplo de PoolAllocator para ver un ejemplo que usa este estilo.