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 bufora stosu.
- Użycie sterty po zwalnianiu
- Użycie zasobów z poziomu zewnętrznego
- podwójne wolne/bezpieczne dzikie
ASan działa na procesorach ARM 32- i 64-bitowych oraz x86 i x86-64. Narzut na procesor w ASan wynosi około 2 razy, narzut na rozmiar kodu – od 50% do 2 razy, a narzut na pamięć – około 2 razy (zależy od wzorca alokacji, ale jest to około 2 razy).
Android 10 i główna gałąź AOSP na AArch64 obsługują AddressSanitizer z obsługą sprzętową (HWASan), czyli podobne narzędzie z mniejszym obciążeniem pamięci RAM i większym zakresem wykrywanych błędów. HWASan wykrywa wykorzystanie 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 tagów, więc prawdopodobieństwo przeoczenia błędu wynosi 0,4%. HWASan nie ma stref czerwonych o ograniczonej wielkości, które służą do wykrywania przepełnienia, ani stref kwarantanny o ograniczonej pojemności, które służą do wykrywania użycia po zwolnieniu, więc nie ma znaczenia, jak duże jest przepełnienie ani jak dawno przydzielone było pamięci. Dzięki temu HWASan jest lepszy niż ASan. Więcej informacji o projektowaniu interfejsu HWASan oraz o używaniu HWASan na Androidzie.
ASan wykrywa przepełnienia stosu/globalne oprócz przepełnień stosu i jest szybki przy minimalnym narzutem na pamięć.
Z tego dokumentu dowiesz się, jak kompilować i uruchamiać części lub całość Androida za pomocą ASANA. Jeśli tworzysz aplikację z użyciem SDK lub NDK z użyciem ASan, zapoznaj się z artykułem Address Sanitizer.
Sanitize poszczególnych plików wykonywalnych za pomocą ASan
Dodaj LOCAL_SANITIZE:=address
lub sanitize: { address: true }
do reguły kompilacji pliku wykonywalnego. Możesz wyszukać w kodzie istniejące przykłady lub znaleźć inne dostępne środki do dezynfekcji.
Gdy zostanie wykryty błąd, ASan wypisuje obszerny raport zarówno do standardowego wyjścia, jak i do pliku logcat
, a potem powoduje awarię procesu.
Czyszczenie bibliotek udostępnionych za pomocą ASan
Ze względu na sposób działania ASan biblioteki utworzone za pomocą ASan mogą być używane tylko przez plik wykonywalny utworzony za pomocą ASan.
Aby oczyścić bibliotekę współdzieloną, która jest używana w wielu plikach wykonywalnych, z których nie wszystkie są tworzone przy użyciu ASan, potrzebujesz 2 kopii biblioteki. Najlepszym sposobem na to jest dodanie do Android.mk
dla danego modułu:
LOCAL_SANITIZE:=address LOCAL_MODULE_RELATIVE_PATH := asan
Spowoduje to umieszczenie biblioteki w folderze /system/lib/asan
zamiast w folderze /system/lib
. Następnie uruchom plik wykonywalny:
LD_LIBRARY_PATH=/system/lib/asan
W przypadku demonów systemowych dodaj poniższy kod 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 jest dostępny), odczytując plik /proc/$PID/maps
. Jeśli nie, być może musisz
wyłączyć 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 zrzuty stosu
ASan używa szybkiego odwijacza opartego na wskaźniku ramki, aby zapisywać informacje o ścieżce wywołań dla każdego zdarzenia przydzielenia i zwolnienia pamięci w programie. Większość Androida jest zbudowana bez wskaźników ramki. W rezultacie często otrzymujesz tylko 1 lub 2 znaczące klatki. Aby rozwiązać ten problem, utwórz ponownie bibliotekę za pomocą ASANA (zalecane!) lub za pomocą:
LOCAL_CFLAGS:=-fno-omit-frame-pointer LOCAL_ARM_MODE:=arm
Możesz też ustawić parametr ASAN_OPTIONS=fast_unwind_on_malloc=0
w środowisku procesu. W zależności od obciążenia może ono mocno obciążać procesor.
Symbolizacja
Początkowo raporty ASan zawierają odniesienia do przesunięcia w plikach binarnych i bibliotekach udostępnionych. Informacje o pliku źródłowym i wierszu można uzyskać na 2 sposoby:
- Upewnij się, że plik binarny
llvm-symbolizer
znajduje się w folderze/system/bin
.llvm-symbolizer
zostało utworzone na podstawie ź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 file:line
lokalizacji) ze względu na dostępność bibliotek symboli na hoście.
ASan w aplikacjach
ASan nie może przeglądać kodu Java, ale może wykrywać błędy w bibliotekach JNI. W tym celu musisz skompilować plik wykonywalny za pomocą ASan, który w tym przypadku to /system/bin/app_process(32|64)
. Włącza to ASan we wszystkich aplikacjach na urządzeniu jednocześnie, 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
. Na razie zignoruj obiekt app_process__asan
w tym samym pliku (jeśli jest on nadal obecny w momencie, gdy czytasz ten artykuł).
Edytuj sekcję service zygote
odpowiedniego pliku system/core/rootdir/init.zygote(32|64).rc
, dodając następujące wiersze do bloku z wcięciem zawierającego class main
, także o takie same wcięcia:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib setenv ASAN_OPTIONS allow_user_segv_handler=true
Kompilacja, synchronizacja za pomocą adb, uruchamianie z pamięci flash za pomocą Fastboot i restart.
Używanie właściwości nawiasów
Metoda opisana w poprzedniej sekcji powoduje, że ASan jest dodawana do każdej aplikacji w systemie (a właściwie do każdego potomka procesu Zygote). Za pomocą ASan można uruchomić tylko jedną (lub kilka) aplikację, tracąc w ten sposób trochę pamięci na rzecz wolniejszego uruchamiania aplikacji.
Aby to zrobić, uruchom aplikację za pomocą właściwości wrap.
.
W tym przykładzie aplikacja Gmail jest uruchamiana w ASan:
adb root
adb shell setenforce 0 # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"
W tym kontekście usługa asanwrapper
zastępuje usługę /system/bin/app_process
usługą /system/bin/asan/app_process
, która została utworzona za pomocą ASANA. Dodaje też /system/lib/asan
na początku ścieżki wyszukiwania dynamicznej biblioteki. W ten sposób biblioteki z /system/lib/asan
z instrumentacją ASan są preferowane przed zwykłymi bibliotekami w /system/lib
podczas uruchamiania z asanwrapper
.
Jeśli zostanie znaleziony błąd, aplikacja ulegnie awarii, a raport zostanie wydrukowany do dziennika.
SANITIZE_TARGET
Android 7.0 i nowsze wersje obsługują kompilowanie całej platformy Androida za pomocą ASANA. (jeśli tworzysz wersję nowszą niż Android 9, lepszym wyborem będzie HWASan).
Uruchom te polecenia w tym samym drzewie kompilacji.
make -j42
SANITIZE_TARGET=address make -j42
W tym trybie userdata.img
zawiera dodatkowe biblioteki i musi zostać wgrany na urządzenie. Użyj tego wiersza poleceń:
fastboot flash userdata && fastboot flashall
Spowoduje to utworzenie 2 zestawów bibliotek wspólnych: normalnej w /system/lib
(pierwsze wywołanie make) i z instrumentacją ASan w /data/asan/lib
(drugie wywołanie make). Pliki wykonywalne z drugiej kompilacji zastąpią te z pierwszej. Pliki wykonywalne z instrumentacją ASan mają inną ścieżkę wyszukiwania biblioteki, która zawiera /data/asan/lib
przed /system/lib
dzięki użyciu /system/bin/linker_asan
w PT_INTERP
.
System kompilacji zastępuje pośrednie katalogi obiektów, gdy zmieni się wartość $SANITIZE_TARGET
. Wymusza to odtworzenie wszystkich obiektów, zachowując zainstalowane pliki binarne w folderze /system/lib
.
Niektórych celów nie można utworzyć za pomocą ASan:
- Statyczni pliki wykonywalne
LOCAL_CLANG:=false
celówLOCAL_SANITIZE:=false
nie są dostępne w przypadkuSANITIZE_TARGET=address
Pliki wykonywalne takie jak te są pomijane w kompilacji SANITIZE_TARGET
, a wersja z pierwszego wywołania tworzenia pozostaje w /system/bin
.
Biblioteki takie jak ta są tworzone bez ASan. Mogą zawierać kod ASan z bibliotek statycznych, od których zależą.