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
  • Wykorzystanie sterty po zwolnieniu
  • Użycie stosu poza zakresem
  • Podwójnie za darmo / dziko za darmo

ASan działa zarówno na 32-bitowym, jak i 64-bitowym ARM, a także na x86 i x86-64. Narzut procesora ASan wynosi około 2x, narzut rozmiaru kodu wynosi od 50% do 2x, a także 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ą przyspieszany sprzętowo 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 podobny narzut na procesor i rozmiar kodu, ale znacznie mniejszy narzut na pamięć RAM (15%). HWASan jest niedeterministyczny. Istnieje tylko 256 możliwych wartości znaczników, więc prawdopodobieństwo pominięcia jakiegokolwiek 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 lub jak dawno zwolniono pamięć. To sprawia, że ​​HWASan jest lepszy niż ASan. Możesz przeczytać więcej o projekcie HWASan lub o korzystaniu z HWASan na Androidzie .

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

W tym dokumencie opisano sposób kompilowania i uruchamiania części/całego systemu Android za pomocą ASan. Jeśli tworzysz aplikację zestawu SDK/NDK za pomocą ASan, zobacz zamiast tego narzędzie do oczyszczania adresów .

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

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

Gdy zostanie wykryty błąd, ASan drukuje pełny raport zarówno na standardowe wyjście, jak i do logcat , a następnie zawiesza proces.

Oczyszczanie bibliotek współdzielonych za pomocą ASan

Ze względu na sposób działania ASan biblioteka zbudowana z ASan może być używana tylko przez plik wykonywalny zbudowany z ASan.

Aby oczyścić udostępnioną bibliotekę, która jest używana w wielu plikach wykonywalnych, z których nie wszystkie są zbudowane przy użyciu ASan, potrzebujesz dwóch kopii biblioteki. Zalecanym sposobem na to jest dodanie następującego elementu do Android.mk 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ące elementy do odpowiedniej sekcji pliku /init.rc lub /init.$device$.rc .

setenv LD_LIBRARY_PATH /system/lib/asan

Sprawdź, czy proces używa bibliotek z /system/lib/asan , jeśli są obecne, czytając /proc/$PID/maps . Jeśli nie, 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 mechanizmu odwijania opartego na wskaźnikach ramek do rejestrowania śladu stosu dla każdego zdarzenia alokacji i zwalniania 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. Te ostatnie mogą 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 uzyskiwania 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 (czyli lokalizacji file:line ) ze względu na dostępność symbolizowanych bibliotek na hoście.

ASan w aplikacjach

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

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

Zmodyfikuj sekcję service zygote odpowiedniego pliku system/core/rootdir/init.zygote( 32|64 ).rc aby dodać następujące wiersze do bloku wierszy z wcięciami zawierającymi class main , również z wcięciem o taką samą wartość:

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

Kompilacja, synchronizacja adb, rozruch flash fastboot i ponowne uruchomienie.

Korzystanie z właściwości zawijania

Podejście z poprzedniej sekcji umieszcza ASan w każdej aplikacji w systemie (właściwie w każdym potomku procesu Zygote). Możliwe jest uruchamianie tylko jednej (lub kilku) aplikacji z ASan, wymieniając trochę narzutu pamięci na wolniejsze uruchamianie aplikacji.

Można to zrobić, uruchamiając aplikację z wrap. nieruchomość. Poniższy przykład uruchamia aplikację Gmail w trybie 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 do /system/bin/asan/app_process , który jest zbudowany z ASan. Dodaje również /system/lib/asan na początku ścieżki wyszukiwania biblioteki dynamicznej. W ten sposób biblioteki z instrumentami ASan z /system/lib/asan są preferowane w stosunku do normalnych bibliotek w /system/lib podczas uruchamiania z asanwrapper .

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

SANITIZE_TARGET

Android 7.0 i nowsze obejmują wsparcie dla jednoczesnego budowania całej platformy Android z ASan. (Jeśli budujesz wersję wyższą niż Android 9, HWASan jest lepszym wyborem).

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 również musi zostać przesłany do urządzenia. 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 z instrumentami ASan w /data/asan/lib (drugie wywołanie make). Pliki wykonywalne z drugiej kompilacji zastępują te z pierwszej kompilacji. Pliki wykonywalne z instrumentami ASan uzyskują inną ścieżkę wyszukiwania biblioteki, która obejmuje /data/asan/lib przed /system/lib za pomocą /system/bin/linker_asan w PT_INTERP .

System kompilacji blokuje pośrednie katalogi obiektów, gdy wartość $SANITIZE_TARGET uległa zmianie. Wymusza to przebudowę wszystkich celów przy jednoczesnym zachowaniu zainstalowanych plików binarnych w /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ą akceptowane dla SANITIZE_TARGET=address

Takie pliki wykonywalne są pomijane w kompilacji SANITIZE_TARGET , a wersja z pierwszego wywołania make jest pozostawiana w /system/bin .

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

Dokumentacja pomocnicza