GWP-ASan, boş kaldıktan 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-ASan Will Provide Allocation SANity". HWASan veya Malloc Debug'ın aksine GWP-ASan kaynak veya yeniden derleme gerektirmez (yani, önceden oluşturulmuş cihazlarla çalışır) ve hem 32 hem de 64 bit işlemlerde çalışır (32 bit kilitlenmelerde daha az hata ayıklama bilgisi olsa da). Bu konuda, uygulamanızda bu özelliği etkinleştirmek için yapmanız gereken işlemler açıklanmaktadır. 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ının ardından (veya zigot çatallandığında) rastgele seçilen bazı 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. Ayırma ö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ı algılanması zor yığın bellek bozulması hatalarını yakalayan özel bir bölgeye yerleştirir. Yeterli sayıda kullanıcıya sahip olunduğunda, bu düşük örnekleme hızı bile düzenli testlerde bulunmayan 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ılabilir ve otomatik olarak yerel kilitlenme raporuna yerleştirilir. Bu, hata ayıklama konusunda önemli ölçüde yardımcı olabilir (Örneğe bakın).
GWP-ASan, önemli bir CPU ek yüküne yol açmayacak şekilde tasarlanmıştır. GWP-ASan etkinleştirildiğinde küçük ve sabit bir RAM ek yükü uygular. Bu ek yük Android sistemi tarafından belirlenir ve şu anda etkilenen her işlem için yaklaşık 70 kibibayt (KiB) değerindedir.
Uygulamanızı kaydetme
GWP-ASan, uygulamalar tarafından uygulama manifest dosyasında android:gwpAsanMode
etiketi kullanılarak işlem başına 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.Her zaman etkin (
android:gwpAsanMode="always"
): Bu ayar, uygulamanızda GWP-ASan'ı etkinleştirir. Buna aşağıdakiler dahildir:İşletim sistemi, GWP-ASan işlemleri için sabit bir RAM miktarı ayırır. Bu, etkilenen her işlem için yaklaşık 70 KiB'dir. (Uygulamanız bellek kullanımındaki artış konusunda kritik derecede hassas değilse GWP-ASan'ı etkinleştirin.)
GWP-ASan, rastgele seçilen bir yığın ayırma alt kümesini müdahale ederek bellek güvenliği ihlallerini güvenilir bir şekilde tespit eden özel bir bölgeye yerleştirir.
Özel bölgede bellek güvenliği ihlali meydana geldiğinde GWP-ASan süreci sonlandırır.
GWP-ASan, kilitlenme raporunda hata hakkında ek bilgi sağlar.
GWP-ASan'ı uygulamanızda genel olarak etkinleştirmek için AndroidManifest.xml
dosyanıza aşağıdakileri ekleyin:
<application android:gwpAsanMode="always"> ... </application>
Ayrıca, GWP-ASan, uygulamanızın belirli alt işlemleri için açık bir şekilde etkinleştirilebilir veya devre dışı bırakılabilir. Etkinlikleri ve hizmetleri, GWP-ASan'ın açıkça etkinleştirdiği veya devre dışı bırakıldığı işlemleri kullanarak hedefleyebilirsiniz. Örnek için aşağıdaki bölüme 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>
Geliştirici desteği
Bu bölümlerde, GWP-ASan kullanılırken ortaya çıkabilecek sorunlar ve bunların nasıl ele alınacağı özetlenmektedir.
Ayırma/ayırma izleri eksik
Ayırma/ayırma çerçeveleri eksik görünen yerel bir kilitlenme teşhis ediyorsanız uygulamanızda çerçeve işaretçileri eksik olabilir. GWP-ASan, ayırma ve anlaşma izlerini performans nedeniyle kaydetmek için çerçeve işaretçilerini kullanır ve yığın izleme yoksa stack trace'i çözemez.
Çerçeve işaretçileri arm64 cihazlar için varsayılan olarak, arm32 cihazlar için ise varsayılan olarak devre dışıdır. Uygulamaların libc üzerinde kontrolü olmadığından, GWP-ASan'ın (genel olarak) 32 bit yürütülebilir dosyalar veya uygulamalar için ayırma/ayırma izlerini toplaması mümkün değildir. GWP-ASan'ın ayırma ve ayırma yığın izlemelerini toplayabilmesi için 64 bit uygulamaların -fomit-frame-pointer
ile oluşturulmadığından emin olması gerekir.
Güvenlik ihlallerini tekrar oluşturma
GWP-ASan, kullanıcı cihazlarındaki yığın bellek güvenliği ihlallerini yakalamak için tasarlanmıştır. GWP-ASan, kilitlenme (ihlalin erişim izi, neden dizesi ve ayırma/ayırma izleri) hakkında mümkün olduğunca fazla bilgi sağlar, ancak ihlalin nasıl oluştuğunu belirlemek yine de zor olabilir. Maalesef hata tespiti olasılıksal olduğundan, GWP-ASan raporlarının yerel bir cihazda yeniden oluşturulması genellikle yanıltıcıdır.
Bu durumlarda, hata 64 bit cihazları etkiliyorsa HWAddressSanitizer'ı (HWASan) kullanmanız gerekir. HWASan; yığın, yığın ve genel gruplarda 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 yol açmak için yeterli olmadığı durumlarda, söz konusu kodu fuzz'a koymanız gerekir. Temel kod sağlığı sorunlarını güvenilir bir şekilde tespit edip ortaya çıkaran GWP-ASan raporundaki bilgilere dayanarak fuzz kaldırma ç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 dokümanlarına bakın. Android yerel kilitlenme raporları hakkında daha fazla bilgi edinmek için Yerel Kilitlenmeleri Teşhis Etme bölümüne bakın.