AddressSanitizer (ASan) to szybkie narzędzie oparte na kompilatorze, które wykrywa błędy pamięci w kodzie natywnym.
ASan wykrywa:
- Przepełnienie/niedopełnienie bufora stosu i kupy
- Użycie sterty po zwalnianiu
- Użycie zbioru poza zakresem
- podwójne wolne/bezpieczne
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 najnowsza wersja gałęzi AOSP na architekturze AArch64 obsługują sprzętowy AddressSanitizer (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 ograniczonym rozmiarze, które ASan wykorzystuje do wykrywania przepełnienia, ani kwarantanny o ograniczonej pojemności, która służy do wykrywania użycia po zwolnieniu, więc nie ma znaczenia, jak duże jest przepełnienie ani jak dawno przydzielona pamięć została zwolniona. 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 działa szybko 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ę na podstawie pakietu SDK lub NDK z użyciem ASan, zapoznaj się z artykułem Address Sanitizer.
Sanityzacja poszczególnych plików wykonywalnych za pomocą ASan
Dodaj LOCAL_SANITIZE:=address
lub sanitize: { address: true }
do reguły kompilacji pliku wykonywalnego. Możesz przeszukać kod, aby znaleźć istniejące przykłady lub 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 biblioteka utworzona za pomocą ASan może być używana tylko przez plik wykonywalny utworzony za pomocą ASan.
Aby odizolować wspólną bibliotekę, która jest używana w kilku plikach wykonywalnych, ale nie wszystkie z nich są skompilowane z użyciem ASan, potrzebujesz 2 kopii biblioteki. Zalecane jest dodanie do Android.mk
w przypadku 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 ten tekst do odpowiedniej sekcji w 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ą dostępne), czytając /proc/$PID/maps
. Jeśli nie, być może trzeba 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 wskazniku ramki, aby rejestrować 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 klatek. W rezultacie często otrzymujesz tylko 1 lub 2 ramki, które mają znaczenie. 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 znacznie obciążać procesor.
Symbolizacja
Początkowo raporty ASan zawierają odwołania do przesunięć w plikach binarnych i wspólnych bibliotekach. Informacje o pliku źródłowym i liniach możesz uzyskać na 2 sposoby:
- Upewnij się, że plik binarny
llvm-symbolizer
znajduje się w folderze/system/bin
.llvm-symbolizer
jest tworzony na podstawie źródeł wthird_party/llvm/tools/llvm-symbolizer
. - Filtruj raport za pomocą skryptu
external/compiler-rt/lib/asan/scripts/symbolize.py
JavaScript.
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 powoduje duże obciążenie, 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 pliku frameworks/base/cmds/app_process
. Na razie zignoruj obiekt app_process__asan
w tym samym pliku (jeśli nadal jest obecny w momencie, gdy czytasz ten artykuł).
Zmień sekcję service zygote
w odpowiednim pliku system/core/rootdir/init.zygote(32|64).rc
, aby dodać te wiersze do zablokowanego bloku wierszy zawierającego class main
, który również jest zablokowany o tę samą wartość:
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). Można uruchomić tylko jedną (lub kilka) aplikację z ASan, tracąc trochę pamięci na rzecz wolniejszego uruchamiania aplikacji.
Aby to zrobić, uruchom aplikację z użyciem 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
, które są zinstrumentowane za pomocą ASan, są preferowane w odróżnieniu od zwykłych bibliotek w /system/lib
, gdy są uruchamiane 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, lepiej wybrać 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 tworzyć za pomocą ASan:
- Statycznie połączone 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 make jest pozostawiana w /system/bin
.
Takie biblioteki są tworzone bez ASan. Mogą one zawierać kod ASan z bibliotek statycznych, od których zależą.