Adressdesinfektionsmittel

Das Android-NDK unterstützt Address Sanitizer (auch als ASan bezeichnet) ab API-Level 27 (Android O MR 1).

ASan ist ein schnelles Compiler-basiertes Tool zur Erkennung von Arbeitsspeicherfehlern in nativem Code. ASan erkennt:

  • Überlauf/Unterlauf von Stack- und Heap-Zwischenspeicher
  • Heap-Nutzung nach der kostenlosen Nutzung
  • Stacknutzung außerhalb des Bereichs
  • Doppelt kostenlos/wildfrei

Der CPU-Aufwand für ASan ist etwa doppelt so hoch, der Overhead für die Codegröße liegt zwischen 50 und 2x und der Speicheraufwand ist groß (abhängig von Ihren Zuweisungsmustern, aber in der Größenordnung von 2x).

Beispiel-App

Eine Beispiel-App zeigt, wie eine Build-Variante für AS konfiguriert wird.

Eine Community

So erstellen Sie den JNI-Code (nativen Code) Ihrer Anwendung mit Address Sanitizer:

NDK-Build

Führen Sie in Application.mk folgende Schritte aus:

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

Für jedes Modul in Android.mk:

LOCAL_ARM_MODE := arm

CMake

Führen Sie im build.gradle-Modul Ihres Moduls folgende Schritte aus:

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

Führen Sie für jedes Ziel in Ihrer CMakeLists.txt-Datei folgende Schritte aus:

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

Laufen

Ab Android O MR1 (API-Level 27) kann eine Anwendung ein Wrapping-Shell-Script bereitstellen, das den Anwendungsprozess umschließen oder ersetzen kann. Dadurch kann eine debug-fähige Anwendung ihren Anwendungsstart anpassen, was die Verwendung von ASan auf Produktionsgeräten ermöglicht.

  1. Füge dem Anwendungsmanifest android:debuggable hinzu.
  2. Setze useLegacyPackaging in der Datei build.gradle deiner App auf true. Weitere Informationen finden Sie in der Anleitung zum Umfassen von Shell-Skripts.
  3. Füge die ASan-Laufzeitbibliothek zum jniLibs deines App-Moduls hinzu.
  4. Fügen Sie jedem Verzeichnis im src/main/resources/lib-Verzeichnis wrap.sh-Dateien mit den folgenden Inhalten hinzu.

    #!/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
    "$@"
    

Wenn das Anwendungsmodul Ihres Projekts den Namen app hat, sollte die endgültige Verzeichnisstruktur Folgendes enthalten:

<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

Stacktraces

Address Sanitizer muss den Stack bei jedem malloc/realloc/free-Aufruf entladen. Dafür gibt es zwei Optionen:

  1. Ein „schneller“ Framepointer-basierter Entspannungseffekt. Verwenden Sie dazu die Anleitung im Abschnitt „Builds“.

  2. Ein „langsamer“ CFI-Abspann. In diesem Modus verwendet ASan _Unwind_Backtrace. Dafür ist nur -funwind-tables erforderlich, das normalerweise standardmäßig aktiviert ist.

In Malloc/Realloc/free gilt standardmäßig das schnelle Abschalten. Das langsame Entwickeln ist die Standardeinstellung für schwerwiegende Stacktraces. Das langsame Unwinder kann für alle Stacktraces aktiviert werden. Dazu fügen Sie fast_unwind_on_malloc=0 in die Variable ASAN_OPTIONS in der Datei „wrap.sh“ ein.