Igienizzante indirizzi

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

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

  • Overflow/underflow dello stack e del buffer heap
  • Utilizzo heap dopo la disponibilità
  • Utilizzo dello stack al di fuori dell'ambito
  • Doppio 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 di 2 volte).

App di esempio

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

Crea

Per creare il codice nativo dell'app (JNI) con Address Sanitizer:

build-ndk

Nel file 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 nel tuo file Android.mk:

LOCAL_ARM_MODE := arm

Marca

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 nel file CMakeLists.txt:

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

Corsa

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 di cui è possibile eseguire il debug di personalizzare l'avvio dell'applicazione, il che consente l'utilizzo di ASan sui dispositivi di produzione.

  1. Aggiungi android:debuggable al manifest dell'applicazione.
  2. Imposta useLegacyPackaging su true nel file build.gradle della tua app. Per ulteriori informazioni, consulta la guida relativa allo script shell di wrapping.
  3. Aggiungi la libreria di runtime ASan al 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 progetto sia denominato app, la struttura della directory finale dovrebbe 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 sbloccare lo stack a ogni chiamata malloc/realloc/free. Le opzioni disponibili sono due:

  1. Uno svolgimento "veloce" basato sul puntatore a frame. Questa è la sezione che viene utilizzata seguendo le istruzioni nella sezione sugli edifici.

  2. Una "lenta" sessione di relax della CFI. In questa modalità, ASan utilizza _Unwind_Backtrace. Richiede solo -funwind-tables, che in genere è abilitato per impostazione predefinita.

Per Malloc/realloc/free viene impostato lo sblocco rapido. Lo sviluppatore lento è l'impostazione predefinita per le analisi dello stack irreversibili. Lo svolgitore lento può essere abilitato per tutte le analisi dello stack aggiungendo fast_unwind_on_malloc=0 alla variabile ASAN_OPTIONS nel tuo wrap.sh.