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ł 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 (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 dlaSANITIZE_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.