Scudo

Scudo to dynamiczny alokator pamięci w trybie użytkownika, czyli alokator sterty, zaprojektowany tak, aby był odporny na luki w zabezpieczeniach związane ze stertą (takie jak przepełnienie bufora opartego na stercie, użycie po zwolnieniupodwójne zwolnienie), przy jednoczesnym zachowaniu wydajności. Zawiera standardowe funkcje alokacji i dealokacji pamięci w języku C (takie jak malloc i free) oraz funkcje języka C++ (takie jak new i delete).

Scudo to raczej środek zapobiegawczy niż pełnoprawny detektor błędów pamięci, taki jak AddressSanitizer (ASan).

Od czasu wprowadzenia Androida 11 scudo jest używane w przypadku całego kodu natywnego (z wyjątkiem urządzeń z małą ilością pamięci, na których nadal używany jest jemalloc). W czasie działania wszystkie alokacje i dealokacje sterty natywnej są obsługiwane przez Scudo we wszystkich plikach wykonywalnych i ich zależnościach bibliotecznych. Proces jest przerywany, jeśli na stercie zostanie wykryte uszkodzenie lub podejrzane zachowanie.

Scudo to oprogramowanie typu open source, które jest częścią projektu LLVM compiler-rt. Dokumentacja jest dostępna na stronie https://llvm.org/docs/ScudoHardenedAllocator.html. Środowisko wykonawcze Scudo jest dostarczane jako część łańcucha narzędzi Androida, a obsługa została dodana do Soong i Make, aby umożliwić łatwe włączanie alokatora w pliku binarnym.

Dodatkowe środki zaradcze możesz włączyć lub wyłączyć w przydzielającym za pomocą opcji opisanych poniżej.

Dostosowywanie

Niektóre parametry alokatora można zdefiniować dla każdego procesu na kilka sposobów:

  • Statycznie: zdefiniuj w programie __scudo_default_options funkcję, która zwraca ciąg znaków 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ępują wszystkie definicje utworzone za pomocą __scudo_default_options.

Dostępne są te opcje:

Opcja Domyślna wersja 64-bitowa Domyślna wersja 32-bitowa Opis
QuarantineSizeKb 256 64 Rozmiar (w KB) kwarantanny używanej do opóźniania rzeczywistego zwalniania bloków. Niższa wartość może zmniejszyć zużycie pamięci, ale obniżyć skuteczność środka zaradczego. Wartość ujemna powoduje powrót do wartości domyślnych. Ustawienie zarówno tego parametru, jak i parametru ThreadLocalQuarantineSizeKb na zero całkowicie wyłącza kwarantannę.
QuarantineChunksUpToSize 2048 512 Rozmiar (w bajtach), do którego można poddać kwarantannie fragmenty.
ThreadLocalQuarantineSizeKb 64 16 Rozmiar (w KB) pamięci podręcznej wątku używanej do odciążenia globalnej kwarantanny. Niższa wartość może zmniejszyć wykorzystanie pamięci, ale może zwiększyć rywalizację w globalnej kwarantannie. Ustawienie zarówno tego parametru, jak i parametru QuarantineSizeKb na zero całkowicie wyłącza kwarantannę.
DeallocationTypeMismatch false false Włącza raportowanie błędów w przypadku funkcji malloc/delete, new/free i new/delete[]
DeleteSizeMismatch true true Umożliwia raportowanie błędów związanych z niezgodnością rozmiarów nowych i usuwanych elementów.
ZeroContents false false Umożliwia zerowe fragmenty treści podczas przydzielania i zwalniania miejsca.
allocator_may_return_null false false Określa, że w przypadku wystąpienia błędu, który można naprawić, alokator może zwrócić wartość null 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, dalsze przydziały nie powiodą się lub zwrócą wartość null (w zależności od wartości allocator_may_return_null), dopóki RSS nie spadnie z powrotem, aby umożliwić nowe przydziały.
allocator_release_to_os_interval_ms 5000 Nie dotyczy Dotyczy tylko alokatora 64-bitowego. Jeśli ta opcja jest ustawiona, próbuje zwolnić nieużywaną pamięć na rzecz systemu operacyjnego, ale nie częściej niż w tym przedziale czasu (w milisekundach). Jeśli wartość jest ujemna, pamięć nie jest zwalniana do systemu operacyjnego.
abort_on_error true true Jeśli ta opcja jest ustawiona, po wydrukowaniu komunikatu o błędzie narzędzie wywołuje funkcję abort() zamiast _exit().

Weryfikacja

Obecnie nie ma testów CTS przeznaczonych specjalnie dla Scudo. Zamiast tego upewnij się, że testy CTS przechodzą z włączonym lub wyłączonym Scudo dla danego pliku binarnego, aby sprawdzić, czy nie ma to wpływu na urządzenie.

Rozwiązywanie problemów

Jeśli wykryty zostanie problem, którego nie można rozwiązać, alokator wyświetli komunikat o błędzie w standardowym deskryptorze błędu, a następnie zakończy proces. Ślady stosu, które prowadzą do zakończenia działania, są dodawane do dziennika systemowego. Wynik zwykle zaczyna się od Scudo ERROR:, a następnie zawiera krótki opis problemu wraz z wskazówkami.

Oto lista aktualnych komunikatów o błędach i ich potencjalnych przyczyn:

  • corrupted chunk header: weryfikacja sumy kontrolnej nagłówka fragmentu nie powiodła się. Prawdopodobnie wynika to z jednej z dwóch przyczyn: nagłówek został nadpisany (częściowo lub całkowicie) albo wskaźnik przekazany do funkcji nie jest fragmentem.
  • race on chunk header: dwa różne wątki próbują jednocześnie manipulować tym samym nagłówkiem. Zwykle jest to objaw wyścigu lub ogólnego braku blokowania podczas wykonywania operacji na tym bloku.
  • invalid chunk state: fragment nie jest w oczekiwanym stanie dla danej operacji, np. nie jest przydzielony podczas próby jego zwolnienia lub nie jest poddany kwarantannie podczas próby jego ponownego wykorzystania. Typową przyczyną tego błędu jest podwójne zwolnienie.
  • misaligned pointer: podstawowe wymagania dotyczące wyrównania są ściśle egzekwowane: 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 warunków, wskaźnik przekazany do jednej z funkcji jest nieprawidłowo wyrównany.
  • allocation type mismatch: gdy ta opcja jest włączona, funkcja zwalniania pamięci wywoływana w przypadku bloku musi być zgodna z rodzajem funkcji, która została wywołana w celu przydzielenia tego bloku. Tego typu niezgodność może powodować problemy z bezpieczeństwem.
  • invalid sized delete: gdy używany jest operator delete o rozmiarze C++14 i włączone jest opcjonalne sprawdzanie, występuje niezgodność między rozmiarem przekazanym podczas zwalniania bloku a rozmiarem żądanym podczas jego przydzielania. Zwykle jest to problem z kompilatorem lub błąd typu w przypadku zwalnianego obiektu.
  • RSS limit exhausted: Przekroczono opcjonalnie określony maksymalny RSS.

Jeśli debugujesz awarię w samym systemie operacyjnym, możesz użyć kompilacji systemu operacyjnego HWASan. Jeśli debugujesz awarię w aplikacji, możesz też użyć kompilacji aplikacji HWASan.