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ł wthird_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_TARGETSANITIZE_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.