Informacje o tym, jak odczytywać raporty o awariach w HWASan, znajdziesz w artykule Omówienie raportów HWASan .
Hardware-assisted AddressSanitizer (HWASan) to narzędzie do wykrywania błędów pamięci podobne do AddressSanitizer. HWASan zużywa znacznie mniej pamięci RAM niż ASan, dzięki czemu nadaje się do czyszczenia całego systemu. HWASan jest dostępny tylko na Androidzie 10 i nowszym oraz tylko na sprzęcie AArch64.
HWASan jest przydatny przede wszystkim w przypadku kodu C/C++, ale może też pomóc w debugowaniu kodu Java, który powoduje awarie w C/C++ używanym do implementowania interfejsów Java. Jest to przydatne, ponieważ wykrywa błędy pamięci w momencie ich wystąpienia, kierując Cię bezpośrednio do kodu, który je spowodował.
W porównaniu z klasycznym ASan HWASan ma:
- podobny narzut na procesor (~2x),
- podobny narzut na rozmiar kodu (40–50%),
- znacznie mniejszy narzut na pamięć RAM (10–35%).
HWASan wykrywa ten sam zestaw błędów co ASan:
- przepełnienie/niedopełnienie bufora stosu i sterty,
- użycie sterty po zwolnieniu,
- użycie stosu poza zakresem,
- podwójne zwolnienie/zwolnienie nieprawidłowej pamięci.
Dodatkowo HWASan wykrywa użycie stosu po powrocie.
HWASan (podobnie jak ASan) jest zgodny z UBSan, oba te narzędzia można włączyć jednocześnie.
Szczegóły implementacji i ograniczenia
HWASan opiera się na podejściu polegającym na oznaczaniu pamięci, w którym mała losowa wartość tagu jest powiązana zarówno ze wskaźnikami, jak i z zakresami adresów pamięci. Aby dostęp do pamięci był prawidłowy, wskaźnik i tagi pamięci muszą się zgadzać. HWASan korzysta z funkcji ARMv8 top byte ignore (TBI), zwanej też tagowaniem adresów wirtualnych, aby przechowywać tag wskaźnika w najwyższych bitach adresu.
Więcej informacji o projekcie HWASan znajdziesz w dokumentacji Clang.
HWASan nie ma z założenia czerwonych stref o ograniczonej wielkości, które służą do wykrywania przepełnień, ani kwarantanny o ograniczonej pojemności, która służy do wykrywania użycia po zwolnieniu. Dlatego HWASan może wykryć błąd niezależnie od tego, jak duże jest przepełnienie i jak dawno pamięć została zwolniona. Daje to HWASan dużą przewagę nad ASan.
HWASan ma jednak ograniczoną liczbę możliwych wartości tagów (256), co oznacza, że podczas jednego wykonania programu istnieje 0,4% prawdopodobieństwa pominięcia błędu.
Wymagania
Najnowsze wersje (4.14+) wspólnego jądra Androida obsługują HWASan od razu. Gałęzie specyficzne dla Androida 10 nie obsługują HWASan.
Obsługa HWASan w przestrzeni użytkownika jest dostępna od Androida 11.
Jeśli pracujesz z innym jądrem, HWASan wymaga, aby jądro systemu Linux akceptowało oznaczone wskaźniki w argumentach wywołania systemowego. Obsługa tej funkcji została zaimplementowana w tych zestawach poprawek:
- arm64 tagged address ABI
- arm64: untag user pointers passed to the kernel
- mm: Avoid creating virtual address aliases in brk()/mmap()/mremap()
- arm64: Validate tagged addresses in access_ok() called from kernel threads
Jeśli tworzysz kompilację za pomocą niestandardowego łańcucha narzędzi, upewnij się, że zawiera on wszystkie elementy aż do commitu c336557f w LLVM.
Korzystanie z HWASan
Aby utworzyć kompilację całej platformy za pomocą HWASan, użyj tych poleceń:
lunch aosp_walleye-userdebug # (or any other product)export SANITIZE_TARGET=hwaddressm -j
Dla wygody możesz dodać ustawienie SANITIZE_TARGET do definicji produktu, podobnie jak w przypadku aosp_coral_hwasan.
Użytkownicy znający AddressSanitizer nie muszą się już martwić o wiele złożonych aspektów kompilacji:
- Nie trzeba uruchamiać polecenia make 2 razy.
- Kompilacje przyrostowe działają od razu.
- Nie trzeba flashować danych użytkownika.
Niektóre ograniczenia AddressSanitizer też już nie obowiązują:
- Obsługiwane są statyczne pliki wykonywalne.
- Możesz pominąć czyszczenie dowolnego celu innego niż libc. W przeciwieństwie do ASan nie ma wymogu, aby w przypadku czyszczenia biblioteki czyszczony był też każdy plik wykonywalny, który się z nią łączy.
Możesz swobodnie przełączać się między obrazami HWASan a zwykłymi obrazami o tym samym (lub wyższym) numerze kompilacji. Nie musisz czyścić urządzenia.
Aby pominąć czyszczenie modułu, użyj
LOCAL_NOSANITIZE := hwaddress (Android.mk) lub
sanitize: { hwaddress: false } (Android.bp).
Czyszczenie poszczególnych celów
HWASan można włączyć dla poszczególnych celów w zwykłej (nieczyszczonej) kompilacji, o ile libc.so jest też
czyszczona. Dodaj hwaddress: true do bloku czyszczenia w "libc_defaults"
w bionic/libc/Android.bp. Następnie zrób to samo w celu, nad którym pracujesz.
Pamiętaj, że czyszczenie biblioteki libc umożliwia oznaczanie alokacji pamięci sterty w całym systemie, a także sprawdzanie tagów w przypadku operacji na pamięci wewnątrz libc.so. Może to wykryć błędy nawet w plikach binarnych
w których nie włączono HWASan, jeśli nieprawidłowy dostęp do pamięci znajduje się w libc.so
(np. pthread_mutex_unlock() w przypadku delete() muteksu).
Jeśli cała platforma jest tworzona za pomocą HWASan, nie trzeba zmieniać żadnych plików kompilacji.
Lepsze zrzuty stosu
HWASan używa szybkiego, opartego na wskaźniku ramki unwinder do rejestrowania zrzutu stosu dla każdego zdarzenia alokacji pamięci i zwalniania pamięci w programie. Android domyślnie włącza wskaźniki ramki w kodzie AArch64,
więc w praktyce działa to świetnie. Jeśli musisz cofnąć się do
kodu zarządzanego, ustaw HWASAN_OPTIONS=fast_unwind_on_malloc=0
w środowisku procesu. Pamiętaj, że zrzuty stosu nieprawidłowego dostępu do pamięci domyślnie używają „wolnego” unwinder. To ustawienie wpływa tylko na zrzuty alokacji i zwalniania. W zależności od obciążenia ta opcja może bardzo
obciążać procesor.
Symbolizacja
Więcej informacji znajdziesz w sekcji Symbolizacja w artykule "Omówienie raportów HWASan".
HWASan w aplikacjach
Podobnie jak AddressSanitizer, HWASan nie widzi kodu Java, ale może wykrywać błędy w bibliotekach JNI. Do Androida 14 uruchamianie aplikacji HWASan na urządzeniu bez HWASan było nie obsługiwane.
Na urządzeniu z HWASan aplikacje można sprawdzać za pomocą HWASan, tworząc ich
kod za pomocą SANITIZE_TARGET:=hwaddress w
Make lub -fsanitize=hwaddress we flagach kompilatora.
Na urządzeniu bez HWASan (z Androidem 14 lub nowszym) trzeba dodać plik wrap.sh z ustawieniem
LD_HWASAN=1
Więcej informacji znajdziesz w
dokumentacji dla deweloperów aplikacji.