Igienizzante indirizzi

L'NDK Android supporta Address Sanitizer (noto anche come ASan) a partire dal livello API 27 (Android O MR 1).

ASan è uno strumento rapido basato sul compilatore per rilevare bug di memoria nel codice nativo. ASan rileva:

  • Overflow/underflow del buffer dello stack e dell'heap
  • Heap use-after-free
  • Utilizzo dello stack al di fuori dell'ambito
  • Double free/wild free

L'overhead della CPU di ASan è circa il doppio, l'overhead delle dimensioni del codice è compreso tra il 50% e il doppio e l'overhead della memoria è elevato (dipende dai pattern di allocazione, ma è dell'ordine del doppio).

App di esempio

Un'app di esempio mostra come configurare una variante di compilazione per asan.

Build

Per creare il codice nativo (JNI) della tua app con Address Sanitizer, procedi nel seguente modo:

ndk-build

In Application.mk:

APP_STL := c++_shared # Or system, or none.
APP_CFLAGS := -fsanitize=address -fno-omit-frame-pointer
APP_LDFLAGS := -fsanitize=address

Per ogni modulo in Android.mk:

LOCAL_ARM_MODE := arm

CMake

Nel file build.gradle del modulo:

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                // Can also use system or none as ANDROID_STL.
                arguments "-DANDROID_ARM_MODE=arm", "-DANDROID_STL=c++_shared"
            }
        }
    }
}

Per ogni target in CMakeLists.txt:

target_compile_options(${TARGET} PUBLIC -fsanitize=address -fno-omit-frame-pointer)
set_target_properties(${TARGET} PROPERTIES LINK_FLAGS -fsanitize=address)

Esegui

A partire da Android O MR1 (livello API 27), un'applicazione può fornire uno script shell di wrapping che può eseguire il wrapping o sostituire il processo dell'applicazione. Ciò consente a un'applicazione sottoponibile a debug di personalizzare l'avvio dell'applicazione, il che consente di utilizzare ASan sui dispositivi di produzione.

  1. Aggiungi android:debuggable al manifest dell'applicazione.
  2. Imposta useLegacyPackaging su true nel file build.gradle dell'app. Per ulteriori informazioni, consulta la guida allo script shell di wrapping.
  3. Aggiungi la libreria runtime ASan al file jniLibs del modulo dell'app.
  4. Aggiungi file wrap.sh con i seguenti contenuti a ogni directory nella directory src/main/resources/lib.

    #!/system/bin/sh
    HERE="$(cd "$(dirname "$0")" && pwd)"
    export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1
    ASAN_LIB=$(ls $HERE/libclang_rt.asan-*-android.so)
    if [ -f "$HERE/libc++_shared.so" ]; then
        # Workaround for https://github.com/android-ndk/ndk/issues/988.
        export LD_PRELOAD="$ASAN_LIB $HERE/libc++_shared.so"
    else
        export LD_PRELOAD="$ASAN_LIB"
    fi
    "$@"
    

Supponendo che il modulo dell'applicazione del tuo progetto si chiami app, la struttura finale della directory deve includere quanto segue:

<project root>
└── app
    └── src
        └── main
            ├── jniLibs
            │   ├── arm64-v8a
            │   │   └── libclang_rt.asan-aarch64-android.so
            │   ├── armeabi-v7a
            │   │   └── libclang_rt.asan-arm-android.so
            │   ├── x86
            │   │   └── libclang_rt.asan-i686-android.so
            │   └── x86_64
            │       └── libclang_rt.asan-x86_64-android.so
            └── resources
                └── lib
                    ├── arm64-v8a
                    │   └── wrap.sh
                    ├── armeabi-v7a
                    │   └── wrap.sh
                    ├── x86
                    │   └── wrap.sh
                    └── x86_64
                        └── wrap.sh

Analisi dello stack

Address Sanitizer deve ripristinare lo stack a ogni chiamata malloc/realloc/free. Sono disponibili due opzioni:

  1. Un unwinder "veloce" basato su puntatori di frame. Questo è ciò che viene utilizzato seguendo le istruzioni riportate nella sezione di creazione.

  2. Un CFI unwinder "lento". In questa modalità ASan utilizza _Unwind_Backtrace. Richiede solo -funwind-tables, che in genere è abilitato per impostazione predefinita.

L'unwinder veloce è quello predefinito per malloc/realloc/free. Lo strumento di analisi dello stack lento è l'impostazione predefinita per le analisi dello stack irreversibili. Il disassemblatore lento può essere abilitato per tutte le analisi dello stack aggiungendo fast_unwind_on_malloc=0 alla variabile ASAN_OPTIONS in wrap.sh.