Rozszerzenie Arm pamięci

Wersja 9 wprowadza pamięć armatury Tagowanie rozszerzenia (MTE) – implementacja sprzętowa tagu wspomnienie oznaczone tagami.

Ogólnie MTE oznacza każdą alokację pamięci/rozpoczęcie dodatkowych metadanych. Przypisuje tag do lokalizacji pamięci, którą można następnie powiązane ze wskaźnikami, które odwołują się do tej lokalizacji pamięci. CPU w czasie działania sprawdza, czy przy każdym wczytywaniu i przechowywaniu danych wskaźnik i tagi metadanych są zgodne.

W Androidzie 12 jądro i sterowanie pamięci w przestrzeni użytkownika mogą rozszerzać funkcje dla każdej alokacji z metadanymi. Pomaga to w wykrywaniu przypadków użycia po rezygnacji buforów, które są najczęstszym źródłem błędów dotyczących bezpieczeństwa pamięci, w naszych bazach kodu.

Tryby działania MTE

MTE ma 3 tryby działania:

  • Tryb synchroniczny (SYNC)
  • Tryb asynchroniczny (ASYNC)
  • Tryb asymetryczny (ASYMM)

Tryb synchroniczny (SYNC)

Ten tryb jest zoptymalizowany pod kątem poprawności wykrywania błędów nad wydajnością może służyć jako dokładne narzędzie do wykrywania błędów, jest akceptowalna. Gdy ta opcja jest włączona, MTE SYNC działa jako środek łagodzący bezpieczeństwo. W przypadku niezgodności tagów procesor natychmiast przerywa wykonywanie kodu i kończy proces za pomocą SIGSEGV (kod SEGV_MTESERR) i pełne informacje o dostępie do pamięci oraz błędny adres.

Podczas testowania zalecamy korzystanie z tego trybu jako alternatywy dla HWASan/KASAN lub w środowisku produkcyjnym, gdy proces docelowy stanowi lukę w zabezpieczeniach na powierzchni ataku. Dodatkowo, gdy tryb ASYNC wskazuje obecność dokładny raport o błędzie można uzyskać, przełączając się przez interfejs API środowiska wykonawczego w trybie SYNCHRONIZACJA.

W trybie SYNCHRONIZACJA Android allocator rejestruje zrzuty stosu wszystkich alokacje i lokalizacje umów, używa ich do generowania lepszych raportów o błędach, które zawierają wyjaśnienie pamięci takich jak use-after-free lub buffer-overflow, a także zrzuty stosu istotne zdarzenia pamięci. Raporty tego typu dostarczają więcej kontekstowych informacji ułatwiają ich wyśledzenie i naprawienie.

Tryb asynchroniczny (ASYNC)

Ten tryb jest zoptymalizowany pod kątem wydajności, głównie ze względu na dokładność raportów o błędach, i może w celu wykrywania błędów związanych z bezpieczeństwem pamięci.
W przypadku niezgodności tagów procesor będzie kontynuował wykonywanie kodu do momentu wpis jądra (na przykład wywołanie Syscall lub przerwanie licznika czasu), w którym się kończy; proces z SIGSEGV (kod SEGV_MTEAERR) bez rejestrowania błędów dotyczących adresu lub dostępu do pamięci.
Zalecamy używanie tego trybu w środowisku produkcyjnym w dobrze przetestowanych bazach kodu, gdzie występuje mała gęstość błędów dotyczących bezpieczeństwa pamięci, co jest możliwe przy użyciu w trybie SYNCHRONIZACJA.

Tryb asymetryczny (ASYMM)

Dodatkowa funkcja w Arm v8.7-A, asymetryczna MTE, umożliwia sprawdzanie odczytów pamięci i asynchroniczne sprawdzanie zapisów w pamięci, przy wydajności podobnej do trybu ASYNC. W większości przypadków to lepszy tryb niż ASYNC i zalecamy używanie go zamiast ASYNC zawsze, gdy jest dostępna.

Z tego powodu żaden z opisanych poniżej interfejsów API nie wspomina o asymetrycznym i trybu uzyskiwania zgody. Zamiast tego można skonfigurować system operacyjny tak, aby zawsze używał trybu asymetrycznego, gdy Żądanie asynchroniczne. Więcej informacji znajdziesz w sekcji „Konfigurowanie preferowany poziom MTE” .

MTE w przestrzeni użytkownika

W sekcjach poniżej opisano, jak można włączyć MTE w procesach systemowych i aplikacje. MTE jest domyślnie wyłączone, chyba że wybierzesz jedną z poniższych opcji ustawiony dla konkretnego procesu (poniżej dowiesz się, jakie komponenty MTE są włączone).

Włącz MTE za pomocą systemu kompilacji

W ramach całego procesu MTE zależy od ustawienia czasu kompilacji głównego pliku wykonywalnego. Następujące opcje umożliwiają zmianę tego ustawienia w przypadku poszczególnych plików wykonywalnych lub całych podkatalogów w drzewie źródłowym. jest ignorowane w bibliotekach ani w żadnym celu, który nie jest wykonywalny ani test.

1. Włączam MTE w Android.bp (przykład), dla danego projektu:

Tryb MTE Ustawienie
Asynchroniczne MTE
  sanitize: {
  memtag_heap: true,
  }
Synchroniczny MTE
  sanitize: {
  memtag_heap: true,
  diag: {
  memtag_heap: true,
  },
  }

lub za Android.mk:

Tryb MTE Ustawienie
Asynchronous MTE LOCAL_SANITIZE := memtag_heap
Synchronous MTE LOCAL_SANITIZE := memtag_heap
LOCAL_SANITIZE_DIAG := memtag_heap

2. Włączanie MTE w podkatalogu w drzewie źródłowym za pomocą usługi :

Tryb MTE Uwzględnij listę Wyklucz listę
asynchroniczne PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS MEMTAG_HEAP_ASYNC_INCLUDE_PATHS PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS
synchronizacja PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS MEMTAG_HEAP_SYNC_INCLUDE_PATHS

lub

Tryb MTE Ustawienie
Asynchroniczne MTE MEMTAG_HEAP_ASYNC_INCLUDE_PATHS
Synchroniczny MTE MEMTAG_HEAP_SYNC_INCLUDE_PATHS

lub przez określenie ścieżki wykluczania pliku wykonywalnego:

Tryb MTE Ustawienie
Asynchroniczne MTE PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS
Synchroniczny MTE

Przykład (użycie podobne do PRODUCT_CFI_INCLUDE_PATHS)

  PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS=vendor/$(vendor)
  PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS=vendor/$(vendor)/projectA \
                                    vendor/$(vendor)/projectB

Włącz MTE za pomocą właściwości systemowych

Powyższe ustawienia kompilacji można zastąpić w czasie działania przez ustawienie tę właściwość systemową:

arm64.memtag.process.<basename> = (off|sync|async)

Gdzie basename to nazwa bazowa pliku wykonywalnego.

Aby na przykład ustawić /system/bin/ping lub /data/local/tmp/ping aby użyć asynchronicznego MTE, użyj adb shell setprop arm64.memtag.process.ping async.

Włączanie MTE przy użyciu zmiennej środowiskowej

Innym sposobem zastąpienia ustawienia kompilacji jest zdefiniowanie środowiska zmienna: MEMTAG_OPTIONS=(off|sync|async) Jeśli zdefiniowano zarówno zmienną środowiskową, jak i właściwość systemową, ma pierwszeństwo.

Włącz MTE dla aplikacji

Jeśli nie określono MTE, jest ono domyślnie wyłączone, ale aplikacje, które chcą używać MTE, mogą to zrobić, ustawiając android:memtagMode w ramach licencji <application> lub <process> w AndroidManifest.xml

android:memtagMode=(off|default|sync|async)

Gdy ustawisz ten tag w tagu <application>, ma wpływ na wszystkie procesy używane przez aplikację i może zostać zastąpiony dla poszczególnych procesów, ustawiając <process>.

Na potrzeby eksperymentowania zgodność zmiany mogą służyć do ustawiania wartości domyślnej memtagMode dla aplikacji, która nie określa żadnej wartości w pliku manifestu (lub określa default).
Znajdziesz je w sekcji System > Advanced > Developer options > App Compatibility Changes w globalnym menu ustawień. Ustawienie NATIVE_MEMTAG_ASYNC lub NATIVE_MEMTAG_SYNC włącza MTE dla konkretnej aplikacji.
Można też ustawić tę wartość za pomocą funkcji am w następujący sposób:

$ adb shell am compat enable NATIVE_MEMTAG_[A]SYNC my.app.name

Tworzenie obrazu systemu MTE

Zdecydowanie zalecamy włączenie MTE we wszystkich natywnych plikach binarnych podczas programowania i wywoływać. Pomaga to wcześnie wykryć błędy dotyczące bezpieczeństwa pamięci i zapewnia realistyczne zasięg wśród użytkowników, jeśli włączono je w testowaniu kompilacji.

Zdecydowanie zalecamy włączenie MTE w trybie synchronicznym na wszystkich natywnych plikach binarnych podczas programowania

SANITIZE_TARGET=memtag_heap SANITIZE_TARGET_DIAG=memtag_heap m

Tak jak w przypadku każdej zmiennej w systemie kompilacji, właściwość SANITIZE_TARGET może zostać używany jako zmienna środowiskowa lub ustawienie make (na przykład w product.mk plik).
Pamiętaj, że to włącza MTE dla wszystkich procesów natywnych, ale nie dla (rozgałęzionych z zygote64), dla których można określić MTE włączono, wykonując instrukcje opisane powyżej.

Skonfiguruj preferowany poziom MTE dla danego procesora

Na niektórych procesorach wydajność MTE w trybie ASYMM, a nawet w trybach SYNCHRONIZACJA może być podobna do ASYNC. Dlatego warto włączyć bardziej rygorystyczne kontrole tych procesorów po zażądaniu mniej rygorystycznego trybu sprawdzania, w aby zyskać korzyści związane z wykrywaniem błędów dzięki bardziej rygorystycznym kontrolom bez obniżenia wydajności.
Domyślnie procesy skonfigurowane do uruchomienia w trybie ASYNC będą uruchamiane w trybie ASYNC na wszystkich procesorach. Aby skonfigurować ją do uruchamiania tych procesów w trybie SYNC dla konkretnych procesorów, synchronizacja wartości musi być zapisywana w sysfs wpis /sys/devices/system/cpu/cpu<N>/mte_tcf_preferred podczas uruchamiania obecnie się znajdujesz. Możesz to zrobić za pomocą skryptu init. Aby na przykład skonfigurować procesory 0–1 w celu uruchomienia procesów w trybie ASYNC w trybie SYNCHRONIZACJA, a dla procesorów 2-3 w trybie ASYMM Do klauzuli init w skrypcie inicjującym dostawcy można dodać następujący tekst:

  write /sys/devices/system/cpu/cpu0/mte_tcf_preferred sync
  write /sys/devices/system/cpu/cpu1/mte_tcf_preferred sync
  write /sys/devices/system/cpu/cpu2/mte_tcf_preferred asymm
  write /sys/devices/system/cpu/cpu3/mte_tcf_preferred asymm

Elementy tombstone z procesów w trybie ASYNC uruchomionych w trybie SYNC zawierają dokładny zrzut stosu lokalizacji błędu pamięci. Nie będą jednak zawierać zrzut stosu alokacji lub lokalizacji. Te zrzuty stosu mają tylko dostępny, jeśli proces jest skonfigurowany do uruchamiania w trybie SYNC.

int mallopt(M_THREAD_DISABLE_MEM_INIT, level)

gdzie level to 0 lub 1.
Wyłącza inicjowanie pamięci w Malloc i unika zmiany tagów pamięci chyba że jest to konieczne do poprawności.

int mallopt(M_MEMTAG_TUNING, level)

gdzie level to:

  • M_MEMTAG_TUNING_BUFFER_OVERFLOW
  • M_MEMTAG_TUNING_UAF

Wybiera strategię alokacji tagów.

  • Domyślne ustawienie to M_MEMTAG_TUNING_BUFFER_OVERFLOW.
  • M_MEMTAG_TUNING_BUFFER_OVERFLOW – umożliwia deterministyczną wykrywanie błędów linearnego przepełnienia bufora i błędów niedopełnienia za pomocą przypisywania osobnych tagów. do sąsiednich przydziałów. Ten tryb ma nieco mniejsze szanse na wykrywają błędy „Użycie po wolnym”, ponieważ tylko połowa możliwych wartości tagów dostępnych dla każdej lokalizacji pamięci. Pamiętaj, że MTE nie może wykryć pojawią się w tym samym granucie tagu (w 16-bajtowym fragmencie wyrównanym do 16 bajtów) i mogą pominąć nie wychodzi nawet w tym trybie. Takie przepełnienie nie może być przyczyną zapamiętywania pamięci. uszkodzenie, ponieważ pamięć w jednym granucie nigdy nie jest wykorzystywana przez wiele alokacje.
  • M_MEMTAG_TUNING_UAF – włącza niezależnie losowe tagi. dla jednolitego ok. 93% prawdopodobieństwa wykrycia zarówno przestrzennego (przepełnienia bufora), jak i błędy czasowe (do wykorzystania po bezpłatnym).

Oprócz opisanych powyżej interfejsów API doświadczeni użytkownicy mogą chcieć musisz wiedzieć, że:

  • Skonfigurowanie rejestru sprzętowego PSTATE.TCO może tymczasowo pomijania sprawdzania tagów (przykład). Na przykład podczas kopiowania zakresu pamięci z nieznaną zawartością tagów lub i rozwiązania problemu wąskiego gardła wydajności w ramach pętli.
  • Jeśli używasz M_HEAP_TAGGING_LEVEL_SYNC, moduł obsługi awarii systemu zawiera dodatkowe informacje, takie jak zrzuty stosu dotyczące alokacji i atrybucji. Ta funkcja wymaga dostępu do bitów tagu i jest włączona przez przekazanie SA_EXPOSE_TAGBITS podczas ustawiania modułu obsługi sygnału. Każdy program, który ustawia własny sygnał i przekazuje nieznane awarie do systemu, tak samo.

MTE w jądrze

Aby włączyć KASAN z akceleracją MTE dla jądra, skonfiguruj ją w CONFIG_KASAN=y, CONFIG_KASAN_HW_TAGS=y. Te konfiguracje są domyślnie włączone w jądrze GKI, począwszy od Android 12-5.10.
Tą funkcją można sterować podczas uruchamiania, używając następujących argumentów wiersza poleceń:

  • kasan=[on|off] – włącz lub wyłącz KASAN (domyślnie: on).
  • kasan.mode=[sync|async] – wybierz tryb synchroniczny lub asynchroniczny (domyślnie sync)
  • kasan.stacktrace=[on|off] – czy zbierać zrzuty stosu (domyślnie: on)
    • zbieranie zrzutów stosu wymaga także stack_depot_disable=off
  • kasan.fault=[report|panic] – czy ma zostać wydrukowany raport, lub też ją panikować (domyślnie: report). Niezależnie od tego sprawdzanie tagu zostaje wyłączone po pierwszym zgłoszonym błędzie.

Zdecydowanie zalecamy korzystanie z trybu SYNCHRONIZACJA podczas uruchamiania, tworzenia i tworzenia kodu i testowania. Tę opcję należy włączyć globalnie dla wszystkich procesów przy użyciu zmiennej środowiskowej lub systemu kompilacji. W tym trybie wykrywane są robaki, na wczesnym etapie rozwoju baza kodu jest stabilniejsza szybciej, eliminuje koszty wykrywania błędów na późniejszym etapie produkcji.

Zdecydowanie zalecamy użycie trybu ASYNC w środowisku produkcyjnym. Zapewnia to niski poziom do wykrywania błędów związanych z bezpieczeństwem pamięci a także na bardziej szczegółową obronę. Po wykryciu błędu deweloper może wykorzystaj interfejsy API środowiska wykonawczego, aby przełączyć się w tryb SYNCHRONIZACJI i uzyskać dokładny zrzut stosu na podstawie próbkowanej grupy użytkowników.

Zdecydowanie zalecamy skonfigurowanie preferowanego poziomu MTE dla danego procesora dla układ scalony. Tryb Asymm ma zazwyczaj takie same parametry wydajności jak ASYNC, i prawie zawsze jest w niej lepsze. Małe rdzenie w kolejności są często podobne wydajności we wszystkich trzech trybach i można ją skonfigurować tak, by preferowała funkcję SYNC.

Deweloperzy powinni sprawdzać, czy nie wystąpiły awarie, /data/tombstones, logcat lub monitorując dostawcę DropboxManager i eliminacji błędów użytkownika. Więcej informacji o debugowaniu kodu natywnego na Androida znajdziesz w artykule więcej informacji znajdziesz tutaj.

Komponenty platformy z obsługą MTE

W Androidzie 12 wiele komponentów systemu o znaczeniu krytycznym dla bezpieczeństwa korzysta z formatu MTE ASYNC wykrywania awarii użytkowników i działania w ramach dodatkowej warstwy z myślą o obronie w głąb. Te komponenty to:

  • demony sieciowe i narzędzia (z wyjątkiem netd).
  • Bluetooth, SecureElement, HAL NFC i aplikacje systemowe
  • demon statsd
  • system_server
  • zygote64 (aby zezwolić aplikacjom na korzystanie z MTE)

Te cele zostały wybrane na podstawie tych kryteriów:

  • Proces z podwyższonymi uprawnieniami (zdefiniowany jako proces mający dostęp do czegoś) których nie ma domena SELinux bez uprawnień)
  • Przetwarza niewiarygodne dane wejściowe (Reguła z dwóch)
  • Dopuszczalne spowolnienie wydajności (spowolnienie powoduje, że użytkownik nie jest widoczny czas oczekiwania)

Zachęcamy dostawców do włączenia MTE w środowisku produkcyjnym w przypadku większej liczby komponentów, zgodnie z podanymi wyżej kryteriami. Podczas programowania zalecamy za pomocą trybu SYNCHRONIZACJA, aby wykrywać łatwo naprawione błędy i ASYNC – wpływ na ich wyniki.
W przyszłości Android planuje rozszerzyć listę komponentów systemu, które MTE jest włączony, kierując się charakterystyką wydajności w kolejnych projektach sprzętu.