GWP-ASan, नेटिव मेमोरी एलोकेटर की एक सुविधा है. इसकी मदद से, फ़्री की गई मेमोरी को इस्तेमाल करने की गड़बड़ी और heap-buffer-overflow वाली गड़बड़ी का पता लगाया जा सकता है. इसका अनौपचारिक नाम, रिकर्सिव एक्रोनम है. यह नाम है,"GWP-ASan Will Provide Allocation SANity". HWASan या Malloc Debug के उलट, GWP-ASan के लिए सोर्स या रीकंपाइलेशन की ज़रूरत नहीं होती. इसका मतलब है कि यह पहले से बने ऐप्लिकेशन के साथ काम करता है. साथ ही, यह 32-बिट और 64-बिट, दोनों तरह की प्रोसेस पर काम करता है. हालांकि, 32-बिट वाले क्रैश की डीबग करने से जुड़ी जानकारी कम होती है. इस विषय में, आपके ऐप्लिकेशन में इस सुविधा को चालू करने के लिए ज़रूरी कार्रवाइयों के बारे में बताया गया है. GWP-ASan, Android 11 (एपीआई लेवल 30) या इसके बाद के वर्शन को टारगेट करने वाले ऐप्लिकेशन पर उपलब्ध है.
खास जानकारी
प्रोसेस शुरू होने पर (या जब zygote फ़ोर्क होता है), कुछ सिस्टम ऐप्लिकेशन और प्लैटफ़ॉर्म एक्ज़ीक्यूटेबल के लिए, GWP-ASan चालू हो जाता है. हालांकि, यह सुविधा रैंडम तरीके से चुने गए ऐप्लिकेशन के लिए ही चालू होती है. अपने ऐप्लिकेशन में GWP-ASan चालू करें, ताकि आपको मेमोरी से जुड़ी गड़बड़ियों का पता लगाने में मदद मिल सके. साथ ही, इससे आपको अपने ऐप्लिकेशन को एआरएम मेमोरी टैगिंग एक्सटेंशन (एमटीई) के साथ काम करने के लिए तैयार करने में भी मदद मिलेगी. एलोकेशन सैंपलिंग के मैकेनिज़्म, क्वेरी ऑफ़ डेथ के ख़िलाफ़ भरोसेमंद तरीके से काम करते हैं.
चालू होने के बाद, GWP-ASan, हीप एलोकेशन के रैंडम तरीके से चुने गए सबसेट को इंटरसेप्ट करता है. इसके बाद, उन्हें एक खास जगह पर रखता है. इस जगह पर, हीप मेमोरी में होने वाली गड़बड़ियों का पता लगाया जा सकता है. इन गड़बड़ियों का पता लगाना मुश्किल होता है. अगर ज़रूरत के मुताबिक उपयोगकर्ता मौजूद हैं, तो सैंपलिंग की कम दर पर भी, हीप मेमोरी की सुरक्षा से जुड़ी उन गड़बड़ियों का पता लगाया जा सकेगा जो सामान्य टेस्टिंग के दौरान नहीं मिल पाती हैं. उदाहरण के लिए, GWP-ASan ने Chrome ब्राउज़र में कई गड़बड़ियों का पता लगाया है. इनमें से कई गड़बड़ियां अब भी प्रतिबंधित व्यू में हैं.
GWP-ASan, इंटरसेप्ट किए गए सभी एलोकेशन के बारे में अतिरिक्त जानकारी इकट्ठा करता है. यह जानकारी तब उपलब्ध होती है, जब GWP-ASan को मेमोरी की सुरक्षा से जुड़े उल्लंघन का पता चलता है. यह जानकारी, नेटिव क्रैश रिपोर्ट में अपने-आप शामिल हो जाती है. इससे डीबग करने में काफ़ी मदद मिलती है. (उदाहरण देखें).
GWP-ASan को इस तरह से डिज़ाइन किया गया है कि इससे सीपीयू पर कोई खास असर न पड़े. GWP-ASan को चालू करने पर, रैम पर थोड़ा असर पड़ता है. हालांकि, यह असर तय होता है. यह असर, Android सिस्टम तय करता है. फ़िलहाल, यह हर प्रभावित प्रोसेस के लिए करीब 70 किबिबाइट (केआईबी) है.
अपने ऐप्लिकेशन के लिए ऑप्ट-इन करना
ऐप्लिकेशन मेनिफ़ेस्ट में android:gwpAsanMode टैग का इस्तेमाल करके, ऐप्लिकेशन के लिए GWP-ASan को हर प्रोसेस के हिसाब से चालू किया जा सकता है. ये विकल्प उपलब्ध हैं:
हमेशा बंद (
android:gwpAsanMode="never"): इस सेटिंग से, आपके ऐप्लिकेशन में GWP-ASan पूरी तरह से बंद हो जाता है. यह सेटिंग, सिस्टम ऐप्लिकेशन के अलावा अन्य ऐप्लिकेशन के लिए डिफ़ॉल्ट रूप से लागू होती है.डिफ़ॉल्ट (
android:gwpAsanMode="default"या कोई सेटिंग तय न करना): Android 13 (एपीआई लेवल 33) और इससे पहले के वर्शन - GWP-ASan बंद रहता है. Android 14 (एपीआई लेवल 34) और इसके बाद के वर्शन - Recoverable GWP-ASan चालू रहता है.हमेशा चालू (
android:gwpAsanMode="always"): इस सेटिंग से, आपके ऐप्लिकेशन में GWP-ASan चालू हो जाता है. इसमें ये शामिल हैं:ऑपरेटिंग सिस्टम, GWP-ASan की कार्रवाइयों के लिए तय मात्रा में रैम रिज़र्व करता है. यह हर प्रभावित प्रोसेस के लिए करीब ~70 केआईबी होती है. (अगर आपके ऐप्लिकेशन को मेमोरी के इस्तेमाल में बढ़ोतरी से कोई खास फ़र्क़ नहीं पड़ता है, तो GWP-ASan चालू करें.)
GWP-ASan, हीप एलोकेशन के रैंडम तरीके से चुने गए सबसेट को इंटरसेप्ट करता है. इसके बाद, उन्हें एक खास जगह पर रखता है. इस जगह पर, मेमोरी की सुरक्षा से जुड़े उल्लंघनों का पता लगाया जा सकता है.
खास जगह पर, मेमोरी की सुरक्षा से जुड़ा उल्लंघन होने पर, GWP-ASan प्रोसेस को बंद कर देता है.
GWP-ASan, क्रैश रिपोर्ट में गड़बड़ी के बारे में अतिरिक्त जानकारी देता है.
अपने ऐप्लिकेशन के लिए, GWP-ASan को दुनिया भर में चालू करने के लिए, अपनी AndroidManifest.xml फ़ाइल में यह कोड जोड़ें:
<application android:gwpAsanMode="always"> ... </application>
इसके अलावा, आपके ऐप्लिकेशन की खास सबप्रोसेस के लिए, GWP-ASan को साफ़ तौर पर चालू या बंद किया जा सकता है. GWP-ASan के लिए, साफ़ तौर पर ऑप्ट-इन या ऑप्ट-आउट करने वाली प्रोसेस का इस्तेमाल करके, गतिविधियों और सेवाओं को टारगेट किया जा सकता है. उदाहरण के लिए, यह देखें:
<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>
Recoverable GWP-ASan
Android 14 (एपीआई लेवल 34) और इसके बाद के वर्शन, Recoverable GWP-ASan के साथ काम करते हैं. इससे डेवलपर, उपयोगकर्ता अनुभव को खराब किए बिना, प्रोडक्शन में heap-buffer-overflow और heap-use-after-free वाली गड़बड़ियों का पता लगा सकते हैं. जब AndroidManifest.xml में android:gwpAsanMode की कोई सेटिंग तय नहीं की जाती है, तो ऐप्लिकेशन, Recoverable GWP-ASan का इस्तेमाल करता है.
Recoverable GWP-ASan, GWP-ASan से इन मामलों में अलग है:
- Recoverable GWP-ASan, हर ऐप्लिकेशन के लॉन्च होने पर नहीं, बल्कि करीब 1% ऐप्लिकेशन के लॉन्च होने पर चालू होता है.
- जब heap-use-after-free या heap-buffer-overflow वाली गड़बड़ी का पता चलता है, तो यह गड़बड़ी क्रैश रिपोर्ट (टॉम्बस्टोन) में दिखती है. यह क्रैश रिपोर्ट,
ActivityManager#getHistoricalProcessExitReasonsएपीआई के ज़रिए उपलब्ध होती है. यह ओरिजनल GWP-ASan की तरह ही काम करता है. - क्रैश रिपोर्ट डंप करने के बाद बंद होने के बजाय, Recoverable GWP-ASan, मेमोरी में गड़बड़ी होने देता है. साथ ही, ऐप्लिकेशन चलता रहता है. हालांकि, प्रोसेस सामान्य तरीके से जारी रह सकती है, लेकिन ऐप्लिकेशन के व्यवहार के बारे में कोई जानकारी नहीं दी जाती है. मेमोरी में गड़बड़ी की वजह से, ऐप्लिकेशन आने वाले समय में किसी भी समय क्रैश हो सकता है. हालांकि, ऐसा भी हो सकता है कि ऐप्लिकेशन बिना किसी समस्या के चलता रहे और उपयोगकर्ता को कोई असर न दिखे.
- क्रैश रिपोर्ट डंप होने के बाद, Recoverable GWP-ASan बंद हो जाता है. इसलिए, कोई ऐप्लिकेशन, हर लॉन्च पर Recoverable GWP-ASan की सिर्फ़ एक रिपोर्ट पा सकता है.
- अगर ऐप्लिकेशन में कोई कस्टम सिग्नल हैंडलर इंस्टॉल किया गया है, तो उसे SIGSEGV सिग्नल के लिए कभी भी कॉल नहीं किया जाता है. यह सिग्नल, Recoverable GWP-ASan की गड़बड़ी का संकेत देता है.
Recoverable GWP-ASan के क्रैश, एंड-यूज़र डिवाइसों पर मेमोरी में होने वाली गड़बड़ियों के असली उदाहरण दिखाते हैं. इसलिए, हमारा सुझाव है कि आप Recoverable GWP-ASan से मिली गड़बड़ियों को प्राथमिकता के आधार पर ठीक करें.
डेवलपर सहायता
इन सेक्शन में, GWP-ASan का इस्तेमाल करते समय होने वाली समस्याओं और उन्हें ठीक करने के तरीकों के बारे में बताया गया है.
एलोकेशन/डीएलोकेशन के ट्रेस नहीं दिख रहे हैं
अगर आपको किसी नेटिव क्रैश की समस्या का पता लगाना है और आपको लगता है कि इसमें एलोकेशन/डीएलोकेशन फ़्रेम नहीं दिख रहे हैं, तो हो सकता है कि आपके ऐप्लिकेशन में फ़्रेम पॉइंटर मौजूद न हों. GWP-ASan, परफ़ॉर्मेंस की वजहों से एलोकेशन और डीएलोकेशन के ट्रेस रिकॉर्ड करने के लिए, फ़्रेम पॉइंटर का इस्तेमाल करता है. अगर ये मौजूद नहीं हैं, तो यह स्टैक ट्रेस को अनवाइंड नहीं कर पाता है.
arm64 डिवाइसों के लिए, फ़्रेम पॉइंटर डिफ़ॉल्ट रूप से चालू होते हैं. वहीं, arm32 डिवाइसों के लिए, ये डिफ़ॉल्ट रूप से बंद होते हैं. ऐप्लिकेशन का libc पर कोई कंट्रोल नहीं होता. इसलिए, आम तौर पर GWP-ASan के लिए, 32-बिट एक्ज़ीक्यूटेबल या ऐप्लिकेशन के लिए, एलोकेशन/डीएलोकेशन के ट्रेस इकट्ठा करना मुमकिन नहीं है. 64-बिट वाले ऐप्लिकेशन को यह पक्का करना चाहिए कि उन्हें -fomit-frame-pointer के साथ न बनाया गया हो, ताकि GWP-ASan, एलोकेशन और डीएलोकेशन स्टैक ट्रेस इकट्ठा कर सके.
सुरक्षा से जुड़े उल्लंघनों को फिर से बनाना
GWP-ASan को उपयोगकर्ता के डिवाइसों पर, हीप मेमोरी की सुरक्षा से जुड़े उल्लंघनों का पता लगाने के लिए डिज़ाइन किया गया है. GWP-ASan, क्रैश के बारे में ज़्यादा से ज़्यादा जानकारी देता है. जैसे, उल्लंघन का ऐक्सेस ट्रेस, वजह की स्ट्रिंग, और एलोकेशन/डीएलोकेशन के ट्रेस. हालांकि, यह पता लगाना मुश्किल हो सकता है कि उल्लंघन कैसे हुआ. दुर्भाग्य से, गड़बड़ी का पता लगाने की संभावना के आधार पर, GWP-ASan की रिपोर्ट को स्थानीय डिवाइस पर फिर से बनाना मुश्किल होता है.
ऐसे मामलों में, अगर गड़बड़ी का असर 64-बिट वाले डिवाइसों पर पड़ता है, तो आपको HWAddressSanitizer (HWASan) का इस्तेमाल करना चाहिए. HWASan, स्टैक, हीप, और ग्लोबल पर, मेमोरी की सुरक्षा से जुड़े उल्लंघनों का पता लगाता है. HWASan के साथ अपना ऐप्लिकेशन चलाने पर, GWP-ASan से रिपोर्ट किए जा रहे नतीजे को भरोसेमंद तरीके से फिर से बनाया जा सकता है.
अगर HWASan के साथ अपना ऐप्लिकेशन चलाने पर भी, गड़बड़ी की वजह का पता नहीं चलता है, तो आपको गड़बड़ी वाले कोड को फ़ज़ करने की कोशिश करनी चाहिए. GWP-ASan की रिपोर्ट में मौजूद जानकारी के आधार पर, फ़ज़िंग की कोशिशें की जा सकती हैं. इससे, कोड की बुनियादी समस्याओं का पता लगाया जा सकता है.
उदाहरण
नेटिव कोड के इस उदाहरण में, फ़्री की गई मेमोरी को इस्तेमाल करने की गड़बड़ी है:
#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));
}
ऊपर दिए गए उदाहरण कोड का इस्तेमाल करके, टेस्ट रन के लिए, GWP-ASan ने गैर-कानूनी इस्तेमाल को पकड़ लिया और नीचे दी गई क्रैश रिपोर्ट को ट्रिगर किया. GWP-ASan ने क्रैश के टाइप, एलोकेशन के मेटाडेटा, और उससे जुड़े एलोकेशन और डीएलोकेशन स्टैक ट्रेस की जानकारी देकर, रिपोर्ट को अपने-आप बेहतर बना दिया है.
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
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)
...
ज़्यादा जानकारी
GWP-ASan को लागू करने से जुड़ी ज़्यादा जानकारी पाने के लिए, LLVM का दस्तावेज़ देखें. Android की नेटिव क्रैश रिपोर्ट के बारे में ज़्यादा जानने के लिए, देखें नेटिव क्रैश की समस्या का पता लगाना.