AdresSanitizer

AddressSanitizer (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
  • Użyj sterty po darmowym
  • Użycie stosu poza zakresem
  • Podwójnie wolny/dziki wolny

ASan działa zarówno na 32-bitowym, jak i 64-bitowym ARM, a także na x86 i x86-64. Narzut procesora ASana wynosi około 2x, narzut rozmiaru kodu wynosi od 50% do 2x, a duży narzut pamięci (w zależności od wzorców alokacji, ale rzędu 2x).

Android 10 i główna gałąź AOSP na AArch64 obsługują sprzętowo przyspieszany ASan (HWASan) , podobne narzędzie z mniejszym obciążeniem pamięci RAM i większym zakresem wykrytych błędów. HWASan wykrywa użycie stosu po powrocie, oprócz błędów wykrytych przez ASan.

HWASan ma podobne obciążenie procesora i rozmiaru kodu, ale znacznie mniejsze obciążenie pamięci RAM (15%). HWASan jest niedeterministyczny. Istnieje tylko 256 możliwych wartości znaczników, więc prawdopodobieństwo pominięcia dowolnego błędu wynosi 0,4%. HWASan nie ma czerwonych stref ASan o ograniczonym rozmiarze do wykrywania przepełnień i kwarantanny o ograniczonej pojemności do wykrywania użycia po zwolnieniu, więc dla HWASan nie ma znaczenia, jak duże jest przepełnienie ani jak dawno temu pamięć została zwolniona. To sprawia, że ​​HWASan jest lepszy niż ASan. Możesz przeczytać więcej o projektowaniu HWASan lub o wykorzystaniu HWASan na Androidzie .

Oprócz przepełnienia sterty ASan wykrywa przepełnienia stosu/globalne i jest szybki przy minimalnym obciążeniu pamięci.

W tym dokumencie opisano, jak budować i uruchamiać części/całość systemu Android za pomocą ASan. Jeśli tworzysz aplikację SDK/NDK za pomocą ASan, zobacz zamiast tego zobacz Address Sanitizer .

Oczyszczanie poszczególnych plików wykonywalnych za pomocą ASan

Dodaj LOCAL_SANITIZE:=address lub sanitize: { address: true } do reguły kompilacji pliku wykonywalnego. Możesz przeszukać kod pod kątem istniejących przykładów lub znaleźć inne dostępne środki dezynfekujące.

Po wykryciu błędu ASan drukuje szczegółowy raport zarówno na standardowe wyjście, jak i do logcat , a następnie zawiesza proces.

Odkażanie bibliotek współdzielonych za pomocą ASan

Ze względu na sposób działania ASana, biblioteka zbudowana za pomocą ASana może być używana tylko przez plik wykonywalny zbudowany za pomocą ASana.

Aby oczyścić bibliotekę współdzieloną używaną w wielu plikach wykonywalnych, z których nie wszystkie są zbudowane za pomocą ASana, potrzebujesz dwóch kopii biblioteki. Zalecanym sposobem jest dodanie do Android.mk następującego polecenia dla danego modułu:

LOCAL_SANITIZE:=address
LOCAL_MODULE_RELATIVE_PATH := asan

Spowoduje to umieszczenie biblioteki w /system/lib/asan zamiast /system/lib . Następnie uruchom plik wykonywalny za pomocą:

LD_LIBRARY_PATH=/system/lib/asan

W przypadku demonów systemowych dodaj następujący wpis do odpowiedniej sekcji /init.rc lub /init.$device$.rc .

setenv LD_LIBRARY_PATH /system/lib/asan

Sprawdź, czy proces korzysta z bibliotek z /system/lib/asan , jeśli są obecne, czytając /proc/$PID/maps . Jeśli tak nie jest, może być konieczne wyłączenie SELinux:

adb root
adb shell setenforce 0
# restart the process with adb shell kill $PID
# if it is a system service, or may be adb shell stop; adb shell start.

Lepsze ślady stosu

ASan używa szybkiego narzędzia odwijającego opartego na wskaźnikach klatek do rejestrowania śledzenia stosu dla każdego zdarzenia alokacji i zwolnienia pamięci w programie. Większość Androida jest zbudowana bez wskaźników ramek. W rezultacie często otrzymujesz tylko jedną lub dwie znaczące klatki. Aby to naprawić, przebuduj bibliotekę za pomocą ASan (zalecane!) lub za pomocą:

LOCAL_CFLAGS:=-fno-omit-frame-pointer
LOCAL_ARM_MODE:=arm

Lub ustaw ASAN_OPTIONS=fast_unwind_on_malloc=0 w środowisku procesu. To ostatnie może bardzo obciążać procesor, w zależności od obciążenia.

Symbolizowanie

Początkowo raporty ASan zawierają odniesienia do przesunięć w plikach binarnych i bibliotekach współdzielonych. Istnieją dwa sposoby uzyskania informacji o pliku źródłowym i wierszu:

  • Upewnij się, że plik binarny llvm-symbolizer jest obecny w /system/bin . llvm-symbolizer jest zbudowany ze źródeł w third_party/llvm/tools/llvm-symbolizer .
  • Przefiltruj raport za pomocą skryptu external/compiler-rt/lib/asan/scripts/symbolize.py .

Drugie podejście może dostarczyć więcej danych (tj. lokalizacji file:line ) ze względu na dostępność bibliotek symbolizowanych na hoście.

ASan w aplikacjach

ASan nie widzi kodu Java, ale może wykryć błędy w bibliotekach JNI. W tym celu musisz zbudować plik wykonywalny za pomocą ASan, który w tym przypadku to /system/bin/app_process( 32|64 ) . Umożliwia to ASan we wszystkich aplikacjach na urządzeniu jednocześnie, co jest dużym obciążeniem, ale urządzenie z 2 GB RAM powinno sobie z tym poradzić.

Dodaj LOCAL_SANITIZE:=address do reguły kompilacji app_process w frameworks/base/cmds/app_process . Na razie zignoruj ​​cel app_process__asan w tym samym pliku (jeśli nadal tam jest, kiedy to czytasz).

Edytuj sekcję service zygote odpowiedniego pliku system/core/rootdir/init.zygote( 32|64 ).rc , aby dodać następujące linie do bloku wciętych linii zawierających class main , również z wcięciem o tę samą wielkość:

    setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib
    setenv ASAN_OPTIONS allow_user_segv_handler=true

Kompiluj, synchronizuj adb, uruchamiaj flash w trybie fastboot i uruchamiaj ponownie.

Korzystanie z właściwości wrap

Podejście opisane w poprzedniej sekcji umieszcza ASan w każdej aplikacji w systemie (właściwie w każdym potomku procesu Zygota). Możliwe jest uruchomienie tylko jednej (lub kilku) aplikacji za pomocą ASan, zamieniając część obciążenia pamięci na wolniejsze uruchamianie aplikacji.

Można to zrobić, uruchamiając aplikację od wrap. nieruchomość. Poniższy przykład uruchamia aplikację Gmail w ASan:

adb root
adb shell setenforce 0  # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"

W tym kontekście asanwrapper przepisuje /system/bin/app_process na /system/bin/asan/app_process , który jest zbudowany przy użyciu ASan. Dodaje także /system/lib/asan na początku ścieżki wyszukiwania bibliotek dynamicznych. W ten sposób biblioteki ASan z /system/lib/asan są preferowane od normalnych bibliotek w /system/lib , gdy działają z asanwrapper .

Jeśli zostanie znaleziony błąd, aplikacja ulegnie awarii, a raport zostanie wydrukowany w dzienniku.

SANITIZE_TARGET

Android 7.0 i nowsze wersje umożliwiają jednoczesne budowanie całej platformy Android za pomocą ASan. (Jeśli tworzysz wersję wyższą niż Android 9, lepszym wyborem będzie HWASan).

Uruchom następujące polecenia w tym samym drzewie kompilacji.

make -j42
SANITIZE_TARGET=address make -j42

W tym trybie userdata.img zawiera dodatkowe biblioteki i należy go również sflashować na urządzenie. Użyj następującego wiersza poleceń:

fastboot flash userdata && fastboot flashall

Tworzy to dwa zestawy bibliotek współdzielonych: normalne w /system/lib (pierwsze wywołanie make) i instrumentowane przez ASan w /data/asan/lib (drugie wywołanie make). Pliki wykonywalne z drugiej kompilacji zastępują pliki wykonywalne z pierwszej kompilacji. Pliki wykonywalne obsługiwane przez ASan otrzymują inną ścieżkę wyszukiwania bibliotek, która obejmuje /data/asan/lib przed /system/lib poprzez użycie /system/bin/linker_asan w PT_INTERP .

System kompilacji blokuje katalogi obiektów pośrednich, gdy zmieni się wartość $SANITIZE_TARGET . Wymusza to przebudowę wszystkich obiektów docelowych przy jednoczesnym zachowaniu zainstalowanych plików binarnych w katalogu /system/lib .

Niektórych celów nie można zbudować za pomocą ASan:

  • Statycznie połączone pliki wykonywalne
  • LOCAL_CLANG:=false cele
  • LOCAL_SANITIZE:=false nie są wysyłane dla SANITIZE_TARGET=address

Pliki wykonywalne tego typu są pomijane w kompilacji SANITIZE_TARGET , a wersja z pierwszego wywołania make pozostaje w /system/bin .

Biblioteki takie jak ta są budowane bez ASan. Mogą zawierać kod ASan z bibliotek statycznych, od których zależą.

Dokumentacja pomocnicza