GWP-ASan

GWP-ASan to natywna funkcja przydzielająca pamięć, która ułatwia znajdowanie używaj po bezpłatnym oraz heap-buffer-overflow robaki. Jego nieformalna nazwa jest rekurencyjnym akronimem”GWP-ASanill. Przedawość przydzielenia SAN. Nie podoba mi się HWASan lub Debugowanie Malloc, GWP-ASan nie wymaga źródła ani ponownej kompilacji (czyli współpracuje z gotowy) i działa zarówno w procesach 32-, jak i 64-bitowych (chociaż w przypadku awarii 32-bitowych mają mniej informacji na potrzeby debugowania). W tym temacie opisujemy działania, które należy wykonać, aby włączyć tę funkcję na swoim . Narzędzie GWP-ASan jest dostępne w aplikacjach kierowanych na Androida 11 (poziom interfejsu API 30) lub nowszego.

Omówienie

GWP-ASan jest włączony w niektórych losowo wybranych aplikacjach systemowych i platformach pliki wykonywalne podczas uruchamiania procesu (lub gdy zygota rozwija się). Włącz GWP-ASan w własną aplikację, która pomoże Ci znaleźć błędy związane z pamięcią i przygotować Obsługa rozszerzenia ARM Memory Tagging Extension (MTE). Mechanizmy próbkowania alokacji również zapewniają niezawodność zapytań o śmierci.

Po włączeniu GWP-ASan przechwytuje losowo wybrany podzbiór alokacji sterty, i umieszcza je w specjalnym regionie, które wychwytuje trudne do wykrycia sterty lub uszkadzania pamięci. Przy wystarczającej liczbie użytkowników nawet tak niska częstotliwość próbkowania znajdowanie błędów dotyczących bezpieczeństwa pamięci sterty, których nie wykryto podczas regularnych testów. Na przykład GWP-ASan znalazł znaczą liczbę błędów w przeglądarce Chrome, a wiele z nich nadal jest objęte ograniczeniami wyświetlania.

GWP-ASan zbiera dodatkowe informacje o wszystkich przydziałach przechwytuje. Te informacje są dostępne, gdy GWP-ASan wykryje bezpieczeństwo pamięci naruszenia zasad i jest automatycznie umieszczany w raporcie o awariach natywnych, jest bardzo pomocne w debugowaniu (patrz przykład).

Narzędzie GWP-ASan zostało zaprojektowane tak, aby nie powodować znaczącego obciążenia procesora. GWP-ASan wprowadza niewielki, stały narzut pamięci RAM po włączeniu. Te narzuty są określane przez systemu Android i to obecnie około 70 kibibajtów (KiB) danego procesu.

Włącz aplikację

Aplikacje GWP-ASan mogą być włączane na poziomie poszczególnych procesów za pomocą android:gwpAsanMode w manifeście aplikacji. Następujące opcje są obsługiwane:

  • Zawsze wyłączone (android:gwpAsanMode="never"): to ustawienie jest całkowicie wyłączone. wyłącza GWP-ASan w aplikacji i jest domyślnym ustawieniem dla aplikacji innych niż systemowe.

  • Domyślny (android:gwpAsanMode="default" lub nieokreślony): Android 13 (API) poziomu 33) i niższych – narzędzie GWP-ASan jest wyłączone. Android 14 (poziom API 34) i wyżej – włączona jest opcja Recoverable GWP-ASan,

  • Zawsze włączone (android:gwpAsanMode="always"): to ustawienie włącza GWP-ASan w swojej aplikacji, która obejmuje:

    1. System operacyjny rezerwuje stałą ilość pamięci RAM dla GWP-ASan. około 70 KiB dla każdego procesu, którego dotyczy problem. (Włącz GWP-ASan, jeśli aplikacja nie jest krytycznie wrażliwa na wzrost pamięci. ).

    2. GWP-ASan przechwytuje losowo wybrany podzbiór alokacji sterty i umieszcza je w specjalnym regionie, który niezawodnie wykrywa bezpieczeństwo pamięci, naruszenia zasad.

    3. Gdy w specjalnym regionie wystąpi naruszenie bezpieczeństwa pamięci, GWP-ASan kończy proces.

    4. GWP-ASan podaje dodatkowe informacje o błędzie w awarii raport.

Aby włączyć globalnie GWP-ASan w aplikacji, dodaj do Plik AndroidManifest.xml:

<application android:gwpAsanMode="always">
  ...
</application>

Dodatkowo GWP-ASan można włączyć lub wyłączyć w określonych podprocesów aplikacji. Możesz kierować reklamy na działania i usługi za pomocą procesów którzy jednoznacznie wyrazili zgodę na korzystanie z GWP-ASan lub z niej zrezygnowali. Poniżej znajdziesz informacje na temat przykład:

<application>
  <processes>
    <!-- Create the (empty) application process -->
    <process />

    <!-- Create subprocesses with GWP-ASan both explicitly enabled and disabled. -->
    <process android:process=":gwp_asan_enabled"
               android:gwpAsanMode="always" />
    <process android:process=":gwp_asan_disabled"
               android:gwpAsanMode="never" />
  </processes>

  <!-- Target services and activities to be run on either the GWP-ASan enabled or disabled processes. -->
  <activity android:name="android.gwpasan.GwpAsanEnabledActivity"
            android:process=":gwp_asan_enabled" />
  <activity android:name="android.gwpasan.GwpAsanDisabledActivity"
            android:process=":gwp_asan_disabled" />
  <service android:name="android.gwpasan.GwpAsanEnabledService"
           android:process=":gwp_asan_enabled" />
  <service android:name="android.gwpasan.GwpAsanDisabledService"
           android:process=":gwp_asan_disabled" />
</application>

Możliwe do odzyskania GWP-ASan

Android 14 (poziom interfejsu API 34) i nowsze obsługują funkcję Recoverable GWP-ASan, co pomaga często wykrywają błędy „heap-buffer-overflow” i „heap-use-after-free” w środowisku produkcyjnym bez pogarszania wygody użytkowników. Gdy android:gwpAsanMode to nie określono w tagu AndroidManifest.xml, aplikacja używa opcji Recoverable, GWP-ASan.

możliwy do odzyskania GWP-ASan różni się od podstawowego GWP-ASan pod tymi względami:

  1. Możliwość odzyskiwania kodu GWP-ASan jest włączona tylko w przypadku około 1% uruchomień aplikacji. a nie każdego uruchomienia aplikacji.
  2. Błąd ten występuje w przypadku wykrycia błędu „używania po stercie” lub „przepełnienia bufora na stercie” pojawi się w raporcie o awariach (tombstone). Ten raport o awariach jest dostępny przez ActivityManager#getHistoricalProcessExitReasons API taki sam jak oryginalny GWP-ASan.
  3. Zamiast zamykać po zrzucie raportu o awarii, można odzyskać GWP-ASan umożliwia uszkodzenie pamięci, a aplikacja będzie nadal działać. Chociaż ten proces może przebiegać w zwykły sposób, aplikacja nie będzie już działać określone dane. Z powodu uszkodzenia pamięci aplikacja może ulegać awariom może dotyczyć przyszłości, lub może być kontynuowane bez widocznych dla użytkowników wpływu.
  4. Po skopiowaniu raportu o awariach usługa GWP-ASan do przywrócenia jest wyłączona. Dlatego aplikacja może otrzymać tylko 1 możliwy do przywrócenia raport GWP-ASan na uruchomienie aplikacji.
  5. Jeśli w aplikacji jest zainstalowany moduł obsługi niestandardowych sygnałów, nigdy nie jest on wywoływany sygnał SIGSEGV, który wskazuje na możliwe do naprawienia błąd GWP-ASan.

Ponieważ awarie GWP-ASan możliwe do przywrócenia wskazują rzeczywiste instancje pamięci na urządzeniach użytkowników, zdecydowanie zalecamy przeprowadzenie klasyfikacji i naprawienie błędów. są rozpoznawane przez system Recoverable GWP-ASan o wysokim priorytecie.

Pomoc dla programistów

W tych sekcjach omawiamy problemy, które mogą wystąpić podczas korzystania z GWP-ASan, oraz sposoby i je rozwiązać.

Brakuje logów alokacji/alokacji

Jeśli diagnozujesz awarię natywną, której prawdopodobnie brakuje ramek alokacji/alokacji, prawdopodobnie brakuje aplikacji wskaźniki klatek. GWP-ASan używa wskaźników klatek do rejestrowania logów alokacji i transakcji ze względu na wydajność i nie jest w stanie rozwinąć zrzutu stosu, jeśli nie jest konieczne obecnie.

Wskaźniki klatek są domyślnie włączone w przypadku urządzeń z AI64 i domyślnie wyłączone w przypadku arm32 urządzenia. Aplikacje nie mają kontroli nad biblioteką libc, więc nie można GWP-ASan zbierać logów czasu alokacji/deallokacji w wersji 32-bitowej pliki wykonywalne i aplikacje. Aplikacje 64-bitowe nie powinny być korzysta z platformy -fomit-frame-pointer, dzięki czemu GWP-ASan może zbierać przydziały akceptacja lokalizacji.

Odtwarzanie przypadków naruszenia bezpieczeństwa

Narzędzie GWP-ASan ma wykrywać naruszenia bezpieczeństwa pamięci sterty na urządzeniach użytkowników. GWP-ASan podaje jak najwięcej informacji o awarii (zrzut dostępu naruszenie, ciąg tekstowy powodujący i ślady alokacji/transakcji), ale może wciąż trudno jest wywnioskować, jak doszło do naruszenia. Niestety, jest prawdopodobna, jednak raporty GWP-ASan są często trudne do odtworzenia na urządzeniu lokalnym.

W takich przypadkach, jeśli błąd dotyczy urządzeń 64-bitowych, skorzystaj z metody HWAddressSanitizer (HWASan). HWASan wykrywa bezpieczeństwo pamięci na stos, stercie i globalnie. Uruchamianie aplikacji z: HWASan może z niezawodnością odtworzyć ten sam wynik raportowany przez GWP-ASan.

W przypadku, gdy uruchomienie aplikacji w środowisku HWASan nie wystarcza do który powoduje błąd, spróbuj usuwaj kod w związku z naruszeniem naszych przepisów. Możesz kierować swoje działania na podstawie informacji w raport GWP-ASan, który potrafi z niezawodną pewnością wykrywać i ujawniać podstawowy stan kodu. .

Przykład

W tym przykładowym kodzie natywnym występuje błąd typu „używanie po bezpłonności” na stercie:

#include <jni.h>
#include <string>
#include <string_view>

jstring native_get_string(JNIEnv* env) {
   std::string s = "Hellooooooooooooooo ";
   std::string_view sv = s + "World\n";

   // BUG: Use-after-free. `sv` holds a dangling reference to the ephemeral
   // string created by `s + "World\n"`. Accessing the data here is a
   // use-after-free.
   return env->NewStringUTF(sv.data());
}

extern "C" JNIEXPORT jstring JNICALL
Java_android11_test_gwpasan_MainActivity_nativeGetString(
    JNIEnv* env, jobject /* this */) {
  // Repeat the buggy code a few thousand times. GWP-ASan has a small chance
  // of detecting the use-after-free every time it happens. A single user who
  // triggers the use-after-free thousands of times will catch the bug once.
  // Alternatively, if a few thousand users each trigger the bug a single time,
  // you'll also get one report (this is the assumed model).
  jstring return_string;
  for (unsigned i = 0; i < 0x10000; ++i) {
    return_string = native_get_string(env);
  }

  return reinterpret_cast<jstring>(env->NewGlobalRef(return_string));
}

W przypadku uruchomienia testowego z użyciem powyższego przykładowego kodu GWP-ASan wykrył nielegalnego użycia i uruchomił poniższy raport o awariach. GWP-ASan automatycznie rozszerzył raport o informacje o typie awarii, metadane alokacji oraz powiązany stos alokacji i transakcji ślady czasu.

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/sargo/sargo:10/RPP3.200320.009/6360804:userdebug/dev-keys'
Revision: 'PVT1.0'
ABI: 'arm64'
Timestamp: 2020-04-06 18:27:08-0700
pid: 16227, tid: 16227, name: 11.test.gwpasan  >>> android11.test.gwpasan <<<
uid: 10238
signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x736ad4afe0
Cause: [GWP-ASan]: Use After Free on a 32-byte allocation at 0x736ad4afe0

backtrace:
      #00 pc 000000000037a090  /apex/com.android.art/lib64/libart.so (art::(anonymous namespace)::ScopedCheck::CheckNonHeapValue(char, art::(anonymous namespace)::JniValueType)+448)
      #01 pc 0000000000378440  /apex/com.android.art/lib64/libart.so (art::(anonymous namespace)::ScopedCheck::CheckPossibleHeapValue(art::ScopedObjectAccess&, char, art::(anonymous namespace)::JniValueType)+204)
      #02 pc 0000000000377bec  /apex/com.android.art/lib64/libart.so (art::(anonymous namespace)::ScopedCheck::Check(art::ScopedObjectAccess&, bool, char const*, art::(anonymous namespace)::JniValueType*)+612)
      #03 pc 000000000036dcf4  /apex/com.android.art/lib64/libart.so (art::(anonymous namespace)::CheckJNI::NewStringUTF(_JNIEnv*, char const*)+708)
      #04 pc 000000000000eda4  /data/app/android11.test.gwpasan/lib/arm64/libmy-test.so (_JNIEnv::NewStringUTF(char const*)+40)
      #05 pc 000000000000eab8  /data/app/android11.test.gwpasan/lib/arm64/libmy-test.so (native_get_string(_JNIEnv*)+144)
      #06 pc 000000000000edf8  /data/app/android11.test.gwpasan/lib/arm64/libmy-test.so (Java_android11_test_gwpasan_MainActivity_nativeGetString+44)
      ...

deallocated by thread 16227:
      #00 pc 0000000000048970  /apex/com.android.runtime/lib64/bionic/libc.so (gwp_asan::AllocationMetadata::CallSiteInfo::RecordBacktrace(unsigned long (*)(unsigned long*, unsigned long))+80)
      #01 pc 0000000000048f30  /apex/com.android.runtime/lib64/bionic/libc.so (gwp_asan::GuardedPoolAllocator::deallocate(void*)+184)
      #02 pc 000000000000f130  /data/app/android11.test.gwpasan/lib/arm64/libmy-test.so (std::__ndk1::_DeallocateCaller::__do_call(void*)+20)
      ...
      #08 pc 000000000000ed6c  /data/app/android11.test.gwpasan/lib/arm64/libmy-test.so (std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> >::~basic_string()+100)
      #09 pc 000000000000ea90  /data/app/android11.test.gwpasan/lib/arm64/libmy-test.so (native_get_string(_JNIEnv*)+104)
      #10 pc 000000000000edf8  /data/app/android11.test.gwpasan/lib/arm64/libmy-test.so (Java_android11_test_gwpasan_MainActivity_nativeGetString+44)
      ...

allocated by thread 16227:
      #00 pc 0000000000048970  /apex/com.android.runtime/lib64/bionic/libc.so (gwp_asan::AllocationMetadata::CallSiteInfo::RecordBacktrace(unsigned long (*)(unsigned long*, unsigned long))+80)
      #01 pc 0000000000048e4c  /apex/com.android.runtime/lib64/bionic/libc.so (gwp_asan::GuardedPoolAllocator::allocate(unsigned long)+368)
      #02 pc 000000000003b258  /apex/com.android.runtime/lib64/bionic/libc.so (gwp_asan_malloc(unsigned long)+132)
      #03 pc 000000000003bbec  /apex/com.android.runtime/lib64/bionic/libc.so (malloc+76)
      #04 pc 0000000000010414  /data/app/android11.test.gwpasan/lib/arm64/libmy-test.so (operator new(unsigned long)+24)
      ...
      #10 pc 000000000000ea6c  /data/app/android11.test.gwpasan/lib/arm64/libmy-test.so (native_get_string(_JNIEnv*)+68)
      #11 pc 000000000000edf8  /data/app/android11.test.gwpasan/lib/arm64/libmy-test.so (Java_android11_test_gwpasan_MainActivity_nativeGetString+44)
      ...

Więcej informacji

Więcej informacji o wdrażaniu GWP-ASan znajdziesz w Dokumentacja LLVM. Aby się uczyć więcej informacji o raportach o awariach natywnych Androida, zobacz Diagnozowanie awarii natywnych