NDK na Androida obsługuje narzędzie Address Sanitizer (znane też jako ASan) od poziomu API 27 (Android O MR 1).
ASan to szybkie narzędzie oparte na kompilatorach, które wykrywa błędy pamięci w kodzie natywnym. ASan wykrywa:
- Przepełnienie lub niedostateczna ilość bufora stosu i buforu sterty
- Wykorzystanie sterty po bezpłatnym
- Użycie stosu poza zakresem
- Podwójne bez ograniczeń
Obciążenie procesora w systemie ASan jest około 2 razy większe, narzut związany z rozmiarem kodu wynosi pomiędzy 50% a 2 razy. Ilość pamięci jest znaczna (zależy od wzorców alokacji, ale na poziomie 2x).
Przykładowa aplikacja
Przykładowa aplikacja pokazuje, jak skonfigurować wariant kompilacji dla Asana.
Budowanie
Aby utworzyć kod natywny (JNI) aplikacji za pomocą narzędzia Address Sanitizer, wykonaj te czynności:
NK 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 każdym module 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"
}
}
}
}
Dla każdego miejsca 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 O MR1 (poziom interfejsu API 27) aplikacja może udostępniać skrypt powłoki zawijania, który może spakować lub zastąpić proces aplikacji. Umożliwia to aplikacji z możliwością debugowania dostosowanie uruchamiania, co umożliwia używanie ASan na urządzeniach produkcyjnych.
- Dodaj
android:debuggable
do pliku manifestu aplikacji. - Ustaw wartość
useLegacyPackaging
natrue
w plikubuild.gradle
aplikacji. Więcej informacji znajdziesz w przewodniku po skrypcie powłoki wrapowej. - Dodaj bibliotekę środowiska wykonawczego ASan do pliku
jniLibs
modułu aplikacji. Do każdego katalogu w katalogu
src/main/resources/lib
dodaj plikiwrap.sh
o poniższej zawartości.#!/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
, końcowa struktura katalogów powinna zawierać taką nazwę:
<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
Narzędzie Address Sanitizer musi wyłączać stos przy każdym wywołaniu malloc
/realloc
/free
. Dostępne są 2 opcje:
„Szybki” moduł rozwijania oparty na wskaźniku ramki. Jest on potrzebny, postępując zgodnie z instrukcjami w sekcji tworzenia.
„Powolne” działanie CFI. W tym trybie ASan używa
_Unwind_Backtrace
. Wymaga jedynie uprawnienia-funwind-tables
, które jest zwykle domyślnie włączone.
Szybkie Relaks to domyślna opcja w przypadku Malloc/realloc/free. Powolne rozwijanie to domyślny
w przypadku krytycznych zrzutów stosu. Powolne rozwijanie funkcji można włączyć we wszystkich zrzutach stosu, dodając fast_unwind_on_malloc=0
do zmiennej ASAN_OPTIONS
w pliku wrap.sh.