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 stosu po darmowym
  • Stosowanie stosu poza zakresem
  • Pokój za darmo/dziki za darmo

ASan działa zarówno na 32-bitowym, jak i 64-bitowym ARM, plus x86 i x86-64. Obciążenie procesora ASan 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 gałąź AOSP master na AArch64 obsługują akcelerowany 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 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 tagów, więc prawdopodobieństwo pominięcia jakiegokolwiek błędu jest równe 0,4%. HWASan nie ma ograniczonych czerwonych stref ASan 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 projekcie HWASan lub o korzystaniu z HWASan w systemie Android .

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

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

Odkażanie poszczególnych plików wykonywalnych za pomocą ASan

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

Po wykryciu błędu ASan drukuje pełny 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 ASan, biblioteka zbudowana za pomocą ASan może być używana tylko przez plik wykonywalny zbudowany za pomocą ASan.

Aby oczyścić udostępnioną bibliotekę, która jest używana w wielu plikach wykonywalnych, z których nie wszystkie są zbudowane za pomocą ASan, potrzebujesz dwóch kopii biblioteki. Zalecanym sposobem, aby to zrobić, jest dodanie następujących elementów 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 /init.rc lub /init.$device$.rc .

setenv LD_LIBRARY_PATH /system/lib/asan

Sprawdź, czy proces używa bibliotek z /system/lib/asan , gdy są obecne, odczytują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 rozwijacza opartego na wskaźniku ramki do rejestrowania śladu stosu dla każdego zdarzenia alokacji pamięci i cofnięcia alokacji 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 procesowym. Ten ostatni 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 znajduje się 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ść symbolizowanych bibliotek na hoście.

ASan w aplikacjach

ASan nie może zajrzeć do kodu Java, ale może wykryć 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 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).

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

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

Kompilacja, synchronizacja adb, szybkie uruchamianie flashowe i ponowne uruchamianie.

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 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. własność. Poniższy przykład uruchamia aplikację Gmail pod 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 za pomocą ASan. Dodaje również /system/lib/asan na początku ścieżki przeszukiwania biblioteki dynamicznej. W ten sposób biblioteki obsługiwane przez ASan z /system/lib/asan są preferowane w porównaniu z normalnymi bibliotekami w /system/lib podczas uruchamiania z asanwrapper .

Jeśli zostanie znaleziony błąd, aplikacja ulega awarii, a raport jest drukowany w dzienniku.

SANITIZE_TARGET

Android 7.0 i nowszy obejmuje obsługę jednoczesnego budowania całej platformy Androida za pomocą ASan. (Jeśli tworzysz 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 musi być również sflashowany na urządzenie. Użyj następującego wiersza poleceń:

fastboot flash userdata && fastboot flashall

To buduje dwa zestawy bibliotek współdzielonych: normalne w /system/lib (pierwsze wywołanie make) i ASan-instrumented w /data/asan/lib (drugie wywołanie make). Pliki wykonywalne z drugiej kompilacji zastępują te z pierwszej kompilacji. Pliki wykonywalne obsługiwane przez ASan uzyskują inną ścieżkę wyszukiwania biblioteki, która zawiera /data/asan/lib przed /system/lib poprzez użycie /system/bin/linker_asan w PT_INTERP .

System budowania blokuje katalogi obiektów pośrednich, gdy zmieni się wartość $SANITIZE_TARGET . Wymusza to odbudowę wszystkich celów przy zachowaniu zainstalowanych plików binarnych w /system/lib .

Niektóre cele nie mogą być zbudowane za pomocą ASan:

  • Pliki wykonywalne połączone statycznie
  • LOCAL_CLANG:=false cele
  • LOCAL_SANITIZE:=false nie są ASi dla SANITIZE_TARGET SANITIZE_TARGET=address

Pliki wykonywalne takie jak te 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 są zależne.

Dokumentacja pomocnicza