Scudo

Scudo to dynamiczny mechanizm alokacji pamięci w trybie użytkownika, czyli alokator stosu, który ma być odporny na luki związane ze stosem (takie jak przepełnienie bufora na stosie, użycie po zwolnieniupodwójne zwolnienie), przy jednoczesnym zachowaniu wydajności. Zawiera on standardowe prymitywy alokacji i dealokwacji języka C (takie jak malloc i free), a także prymitywy 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, a 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 opcji do przeanalizowania. 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 oddzielenia 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 0 dla obu tych parametrów całkowicie wyłącza 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 poszczególne wątki, która jest używana 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 zgłaszanie 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 treści bez dzielenia na częś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, co umożliwi nowe alokacje.
allocator_release_to_os_interval_ms Nie dotyczy 5000 Dotyczy tylko 64-bitowego przydzielacza. Jeśli jest ustawiona, próbuje zwolnić niewykorzystaną 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 upewnić się, że nie ma to 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 deskryptorze błędów, 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 częściowego pliku. 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 wywoływanej w celu alokacji. Takie niezgodności mogą powodować problemy z bezpieczeństwem.
  • invalid sized delete: gdy używasz operatora usuwania C++14, a opcjonalna kontrola jest włączona, 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 pomylenie typów 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.