Scudo to dynamiczny alokator pamięci w trybie użytkownika, czyli alokator sterty, który ma być odporny na luki związane ze stertą (takie jak przepełnienie bufora oparte na stercie, użycie po zwolnieniu, i podwójne zwolnienie) przy zachowaniu wydajności. Udostępnia standardowe prymitywy alokacji i dealokacji w języku C (takie jak malloc i free) oraz prymitywy w języku C++ (takie jak new i delete).
Scudo to raczej środek łagodzący niż pełnoprawny detektor błędów pamięci , taki jak AddressSanitizer (ASan).
Od wersji Androida 11 Scudo jest używany 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 natywne alokacje i dealokacje sterty są obsługiwane przez Scudo w przypadku wszystkich plików wykonywalnych i ich zależności bibliotecznych. Jeśli w stercie zostanie wykryte uszkodzenie lub podejrzane zachowanie, proces zostanie przerwany.
Scudo jest oprogramowaniem typu open source i częścią projektu LLVM's 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 do Soong i Make dodano obsługę, aby umożliwić łatwe włączanie alokatora w pliku binarnym.
Dodatkowe środki łagodzące w alokatorze możesz włączyć lub wyłączyć 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 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_OPTIONSzawierają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 wartość dla wersji 64-bitowej | Domyślna wartość dla wersji 32-bitowej | Opis |
|---|---|---|---|
QuarantineSizeKb |
256 |
64 |
Rozmiar (w KB) kwarantanny używanej do opóźniania rzeczywistej dealokacji fragmentów. Niższa wartość może zmniejszyć zużycie pamięci, ale zmniejszyć skuteczność środków łagodzących. Wartość ujemna powoduje powrót do wartości domyślnych. Ustawienie zarówno tej opcji, jak i ThreadLocalQuarantineSizeKb na zero całkowicie wyłącza kwarantannę. |
QuarantineChunksUpToSize |
2048 |
512 |
Rozmiar (w bajtach), do którego fragmenty mogą być poddawane kwarantannie. |
ThreadLocalQuarantineSizeKb |
64 |
16 |
Rozmiar (w KB) pamięci podręcznej na wątek używanej do odciążania globalnej kwarantanny.
Niższa wartość może zmniejszyć wykorzystanie pamięci, ale może zwiększyć rywalizację w globalnej kwarantannie. Ustawienie zarówno tej opcji, jak i QuarantineSizeKb na zero całkowicie wyłącza kwarantannę. |
DeallocationTypeMismatch |
false |
false |
Włącza raportowanie błędów w przypadku malloc/delete, new/free, new/delete[] |
DeleteSizeMismatch |
true |
true |
Włącza raportowanie błędów w przypadku niezgodności rozmiarów new i delete. |
ZeroContents |
false |
false |
Włącza zerowanie zawartości fragmentów podczas alokacji i dealokacji. |
allocator_may_return_null |
false |
false |
Określa, że alokator może zwracać 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, dalsze alokacje nie powiodą się lub
zwrócą wartość null (w zależności od wartości allocator_may_return_null), dopóki
RSS nie spadnie, aby umożliwić nowe alokacje. |
allocator_release_to_os_interval_ms |
5000 |
Nie dotyczy | Dotyczy tylko alokatora 64-bitowego. Jeśli ta opcja jest ustawiona, narzędzie próbuje zwolnić nieużywaną pamięć do systemu operacyjnego, ale nie częściej niż w tym interwale (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, narzędzie wywołuje abort() zamiast _exit() po wyświetleniu 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 zostanie wykryty problem, którego nie można rozwiązać, alokator wyświetli komunikat o błędzie w standardowym deskryptorze błędów, a następnie zakończy proces.
Ślady stosu prowadzące do zakończenia procesu są dodawane do dziennika systemowego.
Dane wyjściowe zwykle zaczynają się od Scudo ERROR: , po którym następuje krótkie podsumowanie problemu wraz ze wskaźnikami.
Oto lista aktualnych komunikatów o błędach i ich potencjalnych przyczyn:
corrupted chunk header: nie powiodła się weryfikacja sumy kontrolnej nagłówka fragmentu. Prawdopodobnie wynika to z jednej z 2 przyczyn: nagłówek został nadpisany (częściowo lub całkowicie) albo 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. Zwykle jest to objaw warunku wyścigu lub ogólnego braku blokowania podczas wykonywania operacji na tym fragmencie.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 użycia. 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 spełnia tych wymagań, wskaźnik przekazany do jednej z funkcji jest niewyrównany.allocation type mismatch: gdy ta opcja jest włączona, funkcja dealokacji wywoływana na fragmencie musi być zgodna z typem funkcji, która została wywołana w celu jego przydzielenia. Ten typ niezgodności może powodować problemy z bezpieczeństwem.invalid sized delete: gdy używany jest operator delete z rozmiarem C++14 i włączone jest opcjonalne sprawdzanie, występuje niezgodność między rozmiarem przekazanym podczas dealokacji fragmentu a rozmiarem żądanym podczas jego alokacji. Zwykle jest to problem z kompilatorem lub nieprawidłowe rozpoznanie typu obiektu, który jest dealokowany.RSS limit exhausted: przekroczono maksymalny RSS określony opcjonalnie.
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.