Scudo to dynamiczny przydział pamięci w trybie użytkownika, czyli przydział pamięci w strukturze stosu, który ma być odporny na luki związane ze stosem (takie jak przepełnienie bufora w strukturze stosu, użycie po zwalnianiu i podwójne zwalnianie), przy jednoczesnym zachowaniu wydajności. Zawiera ona standardowe prymity alokacji i dealokwacji w języku C (takie jak malloc i free), a także prymity C++ (takie jak new i delete).
Scudo to raczej środek zapobiegawczy niż w pełni funkcjonalny wykrywacz błędów pamięci, taki jak AddressSanitizer (ASan).
Od wersji Androida 11 w przypadku całego kodu natywnego używany jest scudo (z wyjątkiem urządzeń o małej ilości pamięci, na których nadal używany jest jemalloc). Podczas działania wszystkie przydzielenia i zwolnienia pamięci podręcznej w wersji natywnej są obsługiwane przez Scudo we wszystkich plikach wykonywalnych oraz ich bibliotekach zależnych. Proces jest przerywany, jeśli w pamięci podręcznej zostanie wykryte uszkodzenie lub podejrzane zachowanie.
Scudo to oprogramowanie open source, które jest częścią projektu compiler-rt firmy LLVM. Dokumentację znajdziesz na stronie https://llvm.org/docs/ScudoHardenedAllocator.html. Środowisko wykonawcze Scudo jest dostarczane w ramach zestawu narzędzi Androida, a obsługa została dodana do Soong i Make, aby umożliwić łatwe włączenie alokatora w plikach binarnych.
Możesz włączyć lub wyłączyć dodatkowe środki zaradcze w ramach alokatora, korzystając z opcji opisanych poniżej.
Dostosowywanie
Niektóre parametry modułu alokacji można definiować oddzielnie dla każdego procesu na kilka sposobów:
- Statycznie: zdefiniuj w programie funkcję
__scudo_default_options
, która zwraca ciąg opcji do przeanalizowania. Ta funkcja musi mieć ten prototyp:extern "C" const char *__scudo_default_options()
. - Dynamicznie: użyj zmiennej środowiskowej
SCUDO_OPTIONS
zawierającej ciąg znaków opcji, który ma zostać przeanalizowany. Opcje zdefiniowane w ten sposób zastąpią wszystkie definicje utworzone za pomocą__scudo_default_options
.
Dostępne są te opcje.
Option | Domyślna wersja 64-bitowa | Domyślnie 32-bitowy | Opis |
---|---|---|---|
QuarantineSizeKb |
256 |
64 |
Rozmiar (w KB) kwarantanny używanej do opóźnienia faktycznego zwolnienia zasobów z fragmentów. Niższa wartość może zmniejszyć użycie pamięci, ale może też zmniejszyć skuteczność środków zaradczych. Wartość ujemna powoduje zastosowanie domyślnych wartości. Ustawienie wartości obu tych parametrów na 0 pozwala całkowicie wyłączyć kwarantannę.ThreadLocalQuarantineSizeKb |
QuarantineChunksUpToSize |
2048 |
512 |
Rozmiar (w bajtach), do którego można odizolować fragmenty. |
ThreadLocalQuarantineSizeKb |
64 |
16 |
Rozmiar (w KB) pamięci podręcznej na potrzeby poszczególnych wątków, która służy do odciążenia globalnej kwarantanny.
Niższa wartość może zmniejszyć wykorzystanie pamięci, ale może też zwiększyć liczbę konfliktów w globalnej kwarantannie. Ustawienie wartości obu tych parametrów na 0 pozwala całkowicie wyłączyć kwarantannę.QuarantineSizeKb |
DeallocationTypeMismatch |
false |
false |
Umożliwia raportowanie błędów w przypadku malloc/delete, new/free, new/delete[]. |
DeleteSizeMismatch |
true |
true |
Umożliwia raportowanie błędów w przypadku niezgodności między rozmiarami nowych i usuwanych elementów. |
ZeroContents |
false |
false |
Umożliwia przydzielanie i odbieranie udziałów w ramach zerowych bloków treści. |
allocator_may_return_null |
false |
false |
Określa, że alokator może zwrócić wartość null, gdy wystąpił błąd, który można naprawić, zamiast kończyć proces. |
hard_rss_limit_mb |
0 |
0 |
Gdy RSS procesu osiągnie ten limit, proces zostanie zakończony. |
soft_rss_limit_mb |
0 |
0 |
Gdy RSS procesu osiągnie ten limit, kolejne alokacje nie będą się udawać lub będą zwracać wartość null (w zależności od wartości parametru allocator_may_return_null ), dopóki RSS nie spadnie do wartości umożliwiającej nowe alokacje. |
allocator_release_to_os_interval_ms |
5000 |
Nie dotyczy | Dotyczy tylko 64-bitowego przydzielacza. Jeśli jest ustawiona, próbuje zwolnić nieużywaną pamięć na rzecz systemu operacyjnego, ale nie częściej niż w tym interwale (w milisekundach). Jeśli wartość jest ujemna, pamięć nie jest uwalniana do systemu operacyjnego. |
abort_on_error |
true |
true |
Jeśli jest ustawiona, narzędzie wywołuje funkcję abort() zamiast _exit() po wydrukowaniu komunikatu o błędzie. |
Weryfikacja
Obecnie nie ma testów CTS przeznaczonych specjalnie dla Scudo. Zamiast tego sprawdź, czy testy CTS przechodzą z włączonym lub wyłączonym Scudo w przypadku danego pliku binarnego, aby potwierdzić, że nie ma on wpływu na urządzenie.
Rozwiązywanie problemów
Jeśli wykryto problem, którego nie można odzyskać, przydzielający wyświetla komunikat o błędzie w standardowym opisie błędu, a następnie kończy proces.
Ścieżki sterowania prowadzące do zakończenia są dodawane do dziennika systemowego.
Wyjście zwykle zaczyna się od Scudo ERROR:
, a następnie wyświetlane jest krótkie podsumowanie problemu wraz ze wskazówkami.
Oto lista obecnych komunikatów o błędach i ich potencjalnych przyczynach:
corrupted chunk header
: nie udało się zweryfikować sumy kontrolnej nagłówka fragmentu. Przyczyną może być jedno z tych zdarzeń: nagłówek został zastąpiony (częściowo lub całkowicie) lub wskaźnik przekazany do funkcji nie jest fragmentem.race on chunk header
: 2 różne wątki próbują jednocześnie manipulować tym samym nagłówkiem. Jest to zwykle objaw warunku wyścigu lub ogólnego braku blokowania podczas wykonywania operacji na tym fragmencie.invalid chunk state
: fragment nie ma oczekiwanego stanu w przypadku danej operacji, np. nie jest przypisany, gdy próbujesz go zwolnić, lub nie jest odizolowany, gdy próbujesz go wykorzystać. Ten błąd jest zwykle spowodowany dwukrotnym zwolnieniem pamięci.misaligned pointer
: są rygorystycznie egzekwowane podstawowe wymagania dotyczące wyrównania: 8 bajtów na platformach 32-bitowych i 16 bajtów na platformach 64-bitowych. Jeśli wskaźnik przekazany do naszych funkcji nie pasuje do tych, wskaźnik przekazany do jednej z funkcji jest nieprawidłowo wyrównany.allocation type mismatch
: gdy ta opcja jest włączona, funkcja dealokacji wywoływana w przypadku fragmentu musi odpowiadać funkcji, która została wywołana do jego przydzielenia. Takie niezgodności mogą powodować problemy z bezpieczeństwem.invalid sized delete
: gdy używasz operatora usuwania C++14, a opcjonalne sprawdzanie jest włączone, występuje niezgodność między rozmiarem przekazanym podczas dealokacji fragmentu a rozmiarem, który został poproszony podczas alokacji. Jest to zwykle problem z kompilatorem lub pomyłka typu w przypadku dealokacji obiektu.RSS limit exhausted
: przekroczono maksymalną liczbę kanałów RSS (opcjonalnie).
Jeśli debugujesz awarię samego systemu operacyjnego, możesz użyć kompilacji systemu operacyjnego z HWASan. Jeśli debugujesz awarię aplikacji, możesz też użyć kompilacji aplikacji HWASan.