Pamięć tylko do wykonywania (XOM) dla plików binarnych AArch64

Wykonywalne sekcje kodu dla plików binarnych systemu AArch64 są domyślnie oznaczone jako przeznaczone tylko do wykonania (nieczytelne) w celu zabezpieczenia przed atakami polegającymi na ponownym użyciu kodu just-in-time. Kod, który łączy dane i kod oraz kod, który celowo sprawdza te sekcje (bez uprzedniego ponownego mapowania segmentów pamięci jako czytelne) już nie działa. Na aplikacje z docelowym zestawem SDK 10 (poziom API 29 lub wyższy) wpływa próba odczytania sekcji kodu bibliotek systemowych obsługujących pamięć wykonywalną (XOM) w pamięci bez uprzedniego oznaczenia sekcji jako czytelnej.

Aby w pełni skorzystać z tego ograniczenia, wymagana jest obsługa zarówno sprzętu, jak i jądra. Bez tego wsparcia środki łagodzące mogłyby zostać wdrożone jedynie częściowo. Wspólne jądro Androida 4.9 zawiera odpowiednie poprawki, aby zapewnić pełną obsługę tego na urządzeniach ARMv8.2.

Realizacja

Pliki binarne AArch64 generowane przez kompilator zakładają, że kod i dane nie są wymieszane. Włączenie tej funkcji nie wpływa negatywnie na wydajność urządzenia.

W przypadku kodu, który musi przeprowadzać celową introspekcję pamięci w swoich wykonywalnych segmentach, zaleca się wywołanie mprotect w segmentach kodu wymagających sprawdzenia, aby były czytelne, a następnie usunięcie czytelności po zakończeniu kontroli.
Ta implementacja powoduje, że odczyty do segmentów pamięci oznaczonych jako przeznaczone tylko do wykonania powodują błąd segmentacji ( SEGFAULT ). Może to nastąpić w wyniku błędu, luki w zabezpieczeniach, danych zmieszanych z kodem (dosłowne łączenie) lub celowej introspekcji pamięci.

Wsparcie i wpływ urządzeń

Urządzenia z wcześniejszym sprzętem lub starszymi jądrami (poniżej 4.9) bez wymaganych poprawek mogą nie obsługiwać w pełni tej funkcji lub nie korzystać z niej. Urządzenia bez obsługi jądra mogą nie wymuszać dostępu użytkowników do pamięci przeznaczonej tylko do wykonywania, jednak kod jądra, który jawnie sprawdza, czy strona jest czytelna, może nadal wymuszać tę właściwość, na przykład process_vm_readv() .

Flaga jądra CONFIG_ARM64_UAO musi być ustawiona w jądrze, aby zapewnić, że jądro respektuje strony użytkownika oznaczone jako tylko do wykonania. Wcześniejsze urządzenia ARMv8 lub urządzenia ARMv8.2 z wyłączoną funkcją User Access Override (UAO) mogą nie w pełni korzystać z tej możliwości i nadal mogą być w stanie czytać strony tylko do wykonania przy użyciu wywołań systemowych.

Refaktoryzacja istniejącego kodu

Kod przeniesiony z AArch32 może zawierać wymieszane dane i kod, co może powodować problemy. W wielu przypadkach naprawienie tych problemów jest tak proste, jak przeniesienie stałych do sekcji .data w pliku zestawu.

Może zaistnieć potrzeba refaktoryzacji zestawu napisanego odręcznie w celu oddzielenia stałych łączonych lokalnie.

Przykłady:

Pliki binarne generowane przez kompilator Clang nie powinny powodować problemów z mieszaniem danych w kodzie. Jeśli dołączony jest kod wygenerowany przez kompilator GNU (GCC) (z biblioteki statycznej), sprawdź wyjściowy plik binarny, aby upewnić się, że stałe nie zostały połączone w sekcje kodu.

Jeśli konieczna jest introspekcja kodu w sekcjach kodu wykonywalnego, najpierw wywołaj mprotect aby oznaczyć kod jako czytelny. Następnie po zakończeniu operacji wywołaj ponownie mprotect , aby oznaczyć ją jako nieczytelną.

Włączanie

Opcja Tylko wykonanie jest domyślnie włączona dla wszystkich 64-bitowych plików binarnych w systemie kompilacji.

Wyłączanie

Można wyłączyć opcję „tylko wykonanie” na poziomie modułu, w całym drzewie podkatalogów lub globalnie dla całej kompilacji.

XOM można wyłączyć dla poszczególnych modułów, których nie można poddać refaktoryzacji lub które wymagają odczytania ich kodu wykonywalnego, ustawiając zmienne LOCAL_XOM i xom na false .

// Android.mk
LOCAL_XOM := false

// Android.bp
cc_binary { // or other module types
   ...
   xom: false,
}

Jeśli w bibliotece statycznej wyłączona jest pamięć tylko do wykonywania, system kompilacji stosuje to do wszystkich zależnych modułów tej biblioteki statycznej. Możesz to zastąpić, używając xom: true, .

Aby wyłączyć pamięć tylko do wykonywania w określonym podkatalogu (na przykład foo/bar/), przekaż wartość do XOM_EXCLUDE_PATHS .

make -j XOM_EXCLUDE_PATHS=foo/bar

Alternatywnie możesz ustawić zmienną PRODUCT_XOM_EXCLUDE_PATHS w konfiguracji produktu.

Możesz globalnie wyłączyć pliki binarne tylko do wykonywania, przekazując ENABLE_XOM=false do polecenia make .

make -j ENABLE_XOM=false

Walidacja

Nie ma dostępnych testów CTS ani testów weryfikacyjnych dla pamięci przeznaczonej tylko do wykonywania. Możesz ręcznie zweryfikować pliki binarne za pomocą readelf i sprawdzając flagi segmentów.