Igienizzante indirizzi

L'NDK di Android supporta il disinfettante per indirizzi (noto anche come ASan) che inizia con il livello API 27 (Android O MR 1).

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

  • overflow e heap del buffer/overflow
  • Utilizzo dell'heap dopo il giorno
  • Utilizzo dello stack al di fuori dell'ambito
  • Doppia libera/selvatta

L'overhead della CPU di ASan è circa 2 volte, il sovraccarico della dimensione del codice è compreso tra il 50% e il 2x e l'overhead della memoria è grande (a seconda dei pattern di allocazione, ma dell'ordine di 2x).

App di esempio

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

Build

Per creare il codice nativo (JNI) dell'app con il disinfettante per indirizzi, segui questi passaggi:

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

LOCAL_ARM_MODE := arm

C-Make

Nel file build.gradle del tuo 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 destinazione nel tuo file 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 avvolgente che può includere 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 di utilizzare 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 dello script shell wrapper.
  3. Aggiungi la libreria di runtime ASan a jniLibs del modulo dell'app.
  4. Aggiungi wrap.sh file 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 di directory finale 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

Il Disinfettante indirizzo deve annullare lo stack di ogni chiamata malloc/realloc/free. Le opzioni disponibili sono due:

  1. Uno strumento di scorrimento basato sul puntatore del frame "rapido". Viene utilizzato seguendo le istruzioni riportate nella sezione relativa alla creazione.

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

La velocità di sblocco è l'impostazione predefinita per Malloc/realloc/free. L'opzione Slowwinder è l'impostazione predefinita per le analisi dello stack irreversibile. Lo sblocco lento può essere abilitato per tutte le tracce dello stack aggiungendo fast_unwind_on_malloc=0 alla variabile ASAN_OPTIONS nel tuo wrap.sh.