GWP-ASan

GWP-ASan, boş olduktan sonra kullanım ve yığın arabellek taşması hatalarını bulmaya yardımcı olan yerel bir bellek ayırıcı özelliğidir. Resmi olmayan adı yinelemeli bir kısaltmadır: "GWP-AS Will Provide Allocation SANity". HWASan veya Malloc Debug'ın aksine GWP-ASan kaynak veya yeniden derleme gerektirmez (yani önceden oluşturulmuş ile çalışır) ve hem 32 hem 64 bit işlemlerde çalışır (32 bit kilitlenmelerin daha az hata ayıklama bilgisi olsa da). Bu konuda, uygulamanızda bu özelliği etkinleştirmek için yapmanız gereken işlemler özetlenmektedir. GWP-ASan, Android 11 (API düzeyi 30) veya sonraki sürümleri hedefleyen uygulamalarda kullanılabilir.

Genel bakış

GWP-ASan, işlemin başlatılmasından sonra (veya zigot çatallaştığında) bazı rastgele seçilen sistem uygulamalarında ve yürütülebilir platformlarda etkinleştirilir. Bellekle ilgili hataları bulmanıza ve uygulamanızı ARM Memory Tagging Extension (MTE) desteğine hazırlamanıza yardımcı olması için kendi uygulamanızda GWP-ASan'ı etkinleştirin. Tahsis örnekleme mekanizmaları, ölüm sorgularına karşı da güvenilirlik sağlar.

Etkinleştirildiğinde GWP-ASan, rastgele seçilen bir yığın ayırma alt kümesini engeller ve bunları tespit edilmesi zor yığın bellek bozulması hatalarını yakalayan özel bir bölgeye yerleştirir. Yeterli sayıda kullanıcı göz önünde bulundurulduğunda, bu düşük örnekleme hızı bile düzenli testlerle bulunamayan yığın bellek güvenliği hatalarını bulur. Örneğin, GWP-ASan, Chrome tarayıcıda önemli sayıda hata buldu (çoğu hâlâ kısıtlı görünümdedir).

GWP-ASan, müdahale ettiği tüm ayırmalar hakkında ek bilgi toplar. Bu bilgiler, GWP-ASan bir bellek güvenliği ihlali algıladığında kullanıma sunulur ve hata ayıklamada önemli ölçüde yardımcı olabilecek yerel kilitlenme raporuna otomatik olarak yerleştirilir (Örneğe bakın).

GWP-ASan, önemli düzeyde CPU ek yüküne yol açmayacak şekilde tasarlanmıştır. GWP-ASan, etkinleştirildiğinde küçük, sabit bir RAM ek yükü uygular. Bu ek yük, Android sistemi tarafından belirlenir ve etkilenen her işlem için şu anda yaklaşık 70 kibibayt (KiB) boyutundadır.

Uygulamanızı kaydetme

GWP-ASan, uygulama manifestindeki android:gwpAsanMode etiketi kullanılarak uygulamalar tarafından işlem düzeyinde etkinleştirilebilir. Aşağıdaki seçenekler desteklenir:

  • Her zaman devre dışı (android:gwpAsanMode="never"): Bu ayar, GWP-ASan'ı uygulamanızda tamamen devre dışı bırakır ve sistem dışı uygulamalar için varsayılan ayardır.

  • Varsayılan (android:gwpAsanMode="default" veya belirtilmemiş): Android 13 (API düzeyi 33) ve altı - GWP-ASan devre dışıdır. Android 14 (API düzeyi 34) ve üstü - Kurtarılabilir GWP-ASan etkindir.

  • Her zaman etkin (android:gwpAsanMode="always"): Bu ayar, uygulamanızda GWP-ASan'ı etkinleştirir. Bu ayarlar şunları içerir:

    1. İşletim sistemi, GWP-ASan işlemleri için sabit miktarda RAM ayırır. Bu, etkilenen her işlem için yaklaşık 70 KiB'dir. (Uygulamanız bellek kullanımındaki artışlara karşı kritik derecede hassas değilse GWP-ASan'ı etkinleştirin.)

    2. GWP-ASan, yığın ayırmaların rastgele seçilen bir alt kümesini engeller ve bellek güvenliği ihlallerini güvenilir şekilde tespit eden özel bir bölgeye yerleştirir.

    3. Özel bölgede bellek güvenliği ihlali meydana geldiğinde GWP-ASan süreci sonlandırır.

    4. GWP-ASan, kilitlenme raporundaki hatayla ilgili ek bilgi sağlar.

GWP-ASan'ı uygulamanızda global olarak etkinleştirmek için aşağıdaki kodu AndroidManifest.xml dosyanıza ekleyin:

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

Ayrıca, GWP-ASan, uygulamanızın belirli alt işlemleri için açıkça etkinleştirilebilir veya devre dışı bırakılabilir. GWP-ASan'ın açıkça etkinleştirilmiş veya devre dışı bırakıldığı işlemleri ve hizmetleri kullanarak etkinlikleri ve hizmetleri hedefleyebilirsiniz. Bir örnek görmek için aşağıdaki bilgilere bakın:

<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>

Kurtarılabilir GWP-ASan

Android 14 (API düzeyi 34) ve sonraki sürümler, geliştiricilerin kullanıcı deneyimini olumsuz yönde etkilemeden, üretimde yığın arabellek taşması ve serbest kaldıktan sonra yığın kullanımı hatalarını bulmalarına yardımcı olan Kurtarılabilir GWP-ASan'ı destekler. AndroidManifest.xml öğesinde android:gwpAsanMode belirtilmemişse uygulama, Kurtarılabilir GWP-ASan'ı kullanır.

Kurtarılabilir GWP-ASan, temel GWP-ASan'dan şu yönleriyle ayrılır:

  1. Kurtarılabilir GWP-ASan, her uygulama lansmanı yerine yalnızca uygulama lansmanlarının yaklaşık% 1'inde etkinleştirilir.
  2. Ayrıldıktan sonra yığın kullanımı veya yığın arabellek taşması hatası algılandığında bu hata, kilitlenme raporunda (mezar taşı) görünür. Bu kilitlenme raporuna, orijinal GWP-ASan ile aynı şekilde ActivityManager#getHistoricalProcessExitReasons API üzerinden erişilebilir.
  3. Kilitlenme raporu dökümünden sonra çıkmak yerine, Kurtarılabilir GWP-ASan bellek bozulmasına izin verir ve uygulama çalışmaya devam eder. Süreç her zamanki gibi devam edebilir, ancak uygulamanın davranışı artık belirtilmedi. Bellek bozulması nedeniyle, uygulama gelecekte rastgele bir noktada kilitlenebilir veya kullanıcılar tarafından görülebilen herhangi bir etki olmadan devam edebilir.
  4. Kilitlenme raporu dökümü yapıldıktan sonra kurtarılabilir GWP-ASan devre dışı bırakılır. Bu nedenle, bir uygulama her uygulama lansmanı için yalnızca tek bir Kurtarılabilir GWP-ASan raporu alabilir.
  5. Uygulamada bir özel sinyal işleyici yüklüyse bu cihaz hiçbir zaman Kurtarılabilir GWP-ASan hatasını belirten SIGSEGV sinyali için çağrılmaz.

Kurtarılabilir GWP-ASan kilitlenmeleri, son kullanıcı cihazlarında gerçek bellek bozulması örneklerini gösterdiğinden, Kurtarılabilir GWP-ASan tarafından tanımlanan yüksek öncelikli hataları önceliklendirip düzeltmenizi önemle tavsiye ederiz.

Geliştirici desteği

Bu bölümlerde, GWP-ASan kullanılırken ortaya çıkabilecek sorunlar ve bunların nasıl ele alınacağı açıklanmaktadır.

Ayırma/ayırma izleri eksik

Ayırma/ayırma çerçeveleri eksik görünen yerel bir kilitlenmeyi teşhis ediyorsanız uygulamanızda kare işaretçileri olmayabilir. GWP-ASan, ayırma ve anlaşma izlerini performans nedeniyle kaydetmek için çerçeve işaretçilerini kullanır. Yığın izleme mevcut değilse bunu çözemez.

Kare işaretçileri varsayılan olarak arm64 cihazlar için açık, arm32 cihazlar için ise kapalıdır. Uygulamaların libc üzerinde kontrolü olmadığından GWP-ASan'ın (genel olarak) 32 bitlik yürütülebilir dosyalar veya uygulamalar için ayırma/ayırma izlerini toplaması mümkün değildir. 64 bit uygulamaların, GWP-ASan'ın ayırma ve ayırma yığın izlemelerini toplayabilmesi için -fomit-frame-pointer ile oluşturulmadığından emin olması gerekir.

Güvenlik ihlallerini çoğaltma

GWP-ASan, kullanıcı cihazlarındaki yığın bellek güvenliği ihlallerini yakalamak için tasarlanmıştır. GWP-ASan, kilitlenme hakkında mümkün olduğunca fazla bağlam (ihlalin erişim izi, neden dizesi ve ayırma/ayırma izleri) sağlar, ancak ihlalin nasıl oluştuğunu anlamak yine de zor olabilir. Maalesef hata tespiti olasılıksal olduğundan, GWP-ASan raporlarının yerel bir cihazda yeniden oluşturulması çoğu zaman yanıltıcıdır.

Böyle durumlarda, hata 64 bit cihazları etkiliyorsa HWAddressSanitizer (HWASan) kullanmanız gerekir. HWASan; yığın, yığın ve genel belleklerde bellek güvenliği ihlallerini güvenilir bir şekilde tespit eder. Uygulamanızı HWASan ile çalıştırmak, GWP-ASan tarafından bildirilen sonucun aynısını güvenilir bir şekilde yeniden oluşturabilir.

Uygulamanızı HWASan altında çalıştırmanın bir hataya neden olması için yeterli olmadığı durumlarda, söz konusu kodu fuzz'a dönüştürmeyi denemeniz gerekir. Temel kod sağlığı sorunlarını güvenilir bir şekilde tespit edip ortaya çıkarabilen GWP-ASan raporundaki bilgilere dayanarak fuzzing çalışmalarınızı hedefleyebilirsiniz.

Örnek

Bu örnek yerel kodda, boşaltıldıktan sonra yığın kullanımı hatası vardır:

#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));
}

Yukarıdaki örnek kodu kullanan bir test çalıştırması için GWP-ASan, yasa dışı kullanımı başarıyla tespit etti ve aşağıdaki kilitlenme raporunu tetikledi. GWP-ASan; kilitlenme türü, ayırma meta verileri, ilişkili ayırma ve anlaşma yığın izleri hakkında bilgi sağlayarak raporu otomatik olarak geliştirmiştir.

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
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)
      ...

Daha fazla bilgi

GWP-ASan'ın uygulama ayrıntıları hakkında daha fazla bilgi edinmek için LLVM belgelerini inceleyin. Android yerel kilitlenme raporları hakkında daha fazla bilgi edinmek için Yerel Kilitlenmeleri Teşhis Etme bölümüne bakın.