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.
- Aggiungi
android:debuggableal manifest dell'applicazione. - Imposta
useLegacyPackagingsutruenel filebuild.gradledell'app. Per ulteriori informazioni, consulta la guida allo script shell di wrapping. - Aggiungi la libreria runtime ASan al file
jniLibsdel modulo dell'app. Aggiungi file
wrap.shcon i seguenti contenuti a ogni directory nella directorysrc/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:
Un unwinder "veloce" basato su puntatori di frame. Questo è ciò che viene utilizzato seguendo le istruzioni riportate nella sezione di creazione.
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.