Preparat do dezynfekcji adresu

Pakiet Android NDK na Androida obsługuje usługę Address Sanitizer (inaczej ASan) na początku z interfejsem API poziomu 27 (Android O MR 1).

ASan to szybkie narzędzie oparte na kompilatorze do wykrywania błędów pamięci w kodzie natywnym. ASan wykrywa:

  • Przepełnienie/niedopełnienie bufora stosu i sterty
  • Wykorzystanie sterty po bezpłatnym
  • Użycie stosu poza zakresem
  • Podwójny wolny/szlachetny

Narzut procesora ASan jest około 2-krotnie większy, rozmiar kodu od 50% do 2-krotnie, i zużycie pamięci jest duże (zależnie od wzorców alokacji, ale w kolejności 2x).

Przykładowa aplikacja

Przykładowa aplikacja pokazuje, jak skonfigurować wariant kompilacji dla pliku Asan.

Budowanie

Aby utworzyć natywny kod aplikacji (JNI) za pomocą narzędzia Address Sanitizer, wykonaj :

ndk-build

W pliku Application.mk:

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

W przypadku każdego modułu w pliku Android.mk:

LOCAL_ARM_MODE := arm

CMake

W pliku build.gradle modułu:

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

W przypadku każdego elementu docelowego w pliku CMakeLists.txt:

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

Uruchom

Począwszy od Androida O MR1 (poziom interfejsu API 27) aplikacja może udostępniać skrypt powłoki, który może opakować lub zastąpić proces aplikacji. Dzięki temu aplikacji z możliwością debugowania, aby dostosować uruchamianie aplikacji, i używania ASan na urządzeniach produkcyjnych.

  1. Dodaj android:debuggable do pliku manifestu aplikacji.
  2. Ustaw useLegacyPackaging do true w pliku build.gradle aplikacji. Zapoznaj się z przewodnikiem po zawijaniu skryptu powłoki .
  3. Dodaj bibliotekę środowiska wykonawczego ASan do sekcji jniLibs modułu aplikacji.
  4. Dodaj pliki wrap.sh o podanej zawartości do każdego katalogu w swoim katalogu 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
    "$@"
    
.

Zakładając, że moduł aplikacji Twojego projektu nazywa się app, powinna zawierać:

<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

Śledzenie stosu

Usługa Address Sanitizer musi rozwijać stos co malloc/realloc/free . Dostępne są 2 opcje:

  1. Szybkość elementu rozwijania opartego na wskaźniku ramki. Jest to metoda stosowana przez w sekcji dotyczącej budynków.

  2. „Wolno” Narzędzie do rozwijania interfejsu CFI. W tym trybie ASan używa parametru _Unwind_Backtrace. it wymaga tylko funkcji -funwind-tables, która jest normalnie włączona domyślnie.

Szybkie rozwijanie jest domyślnym ustawieniem dla Malloc/realloc/free. Wolne przewijanie to domyślna dla krytycznych zrzutów stosu. Wolne rozwijanie można włączyć dla wszystkich zrzuty stosu, dodając fast_unwind_on_malloc=0 do zmiennej ASAN_OPTIONS w Twoim pliku wrap.sh.