जीडब्ल्यूपी-असैन

GWP-ASan, नेटिव मेमोरी ऐलोकेटर की एक सुविधा है. इससे, फ़्री की गई मेमोरी को इस्तेमाल करने और हैप-बफ़र-ओवरफ़्लो वाली गड़बड़ियों का पता लगाने में मदद मिलती है. इसका अनौपचारिक नाम, बार-बार इस्तेमाल होने वाला एक छोटा शब्द है,"GWP-ASan Will Provide Allocation SANity". HWASan या Malloc Debug के मुकाबले, GWP-ASan को सोर्स या फिर से कंपाइल करने की ज़रूरत नहीं होती. इसका मतलब है कि यह पहले से बने प्रोग्राम के साथ काम करता है. साथ ही, यह 32- और 64-बिट, दोनों प्रोसेस पर काम करता है. हालांकि, 32-बिट क्रैश में डीबग करने से जुड़ी कम जानकारी होती है. इस विषय में उन कार्रवाइयों के बारे में बताया गया है जिन्हें अपने ऐप्लिकेशन में चालू करने के लिए करना होगा. GWP-ASan, उन ऐप्लिकेशन के लिए उपलब्ध है जो Android 11 (एपीआई लेवल 30) या उसके बाद के वर्शन को टारगेट करते हैं.

खास जानकारी

GWP-ASan को, रैंडम तरीके से चुने गए कुछ सिस्टम ऐप्लिकेशन और प्लैटफ़ॉर्म पर प्रोसेस शुरू होने पर (या जब ज़िगोट फ़ोर्क होता है) चालू होता है. अपने ऐप्लिकेशन में GWP-ASan को चालू करें. इससे आपको मेमोरी से जुड़ी गड़बड़ियां ढूंढने में मदद मिलेगी. साथ ही, इससे आपके ऐप्लिकेशन को ARM Memory Tag एक्सटेंशन (MTE) से जुड़ी सहायता के लिए तैयार किया जा सकता है. ऐलोकेशन सैंपलिंग के तरीके, मौत के मामलों के ख़िलाफ़ भी भरोसा करते हैं.

चालू होने के बाद, GWP-ASan, हेप के ऐलोकेशन के किसी सबसेट को रैंडम तौर पर चुनकर, उसे एक खास क्षेत्र में डाल देता है. इस क्षेत्र में, हेप मेमोरी में होने वाली गड़बड़ियों के ऐसे बग का पता चलता है जिन्हें ढूंढना मुश्किल होता है. ज़रूरत के मुताबिक उपयोगकर्ता होने पर, सैंपलिंग की इस कम दर से भी उन हीप मेमोरी सुरक्षा गड़बड़ियों का पता चल जाएगा जो नियमित टेस्ट में नहीं मिलतीं. उदाहरण के लिए, GWP-ASan को Chrome ब्राउज़र में बड़ी संख्या में गड़बड़ियां मिली हैं. इनमें से कई गड़बड़ियां अब भी प्रतिबंधित व्यू में हैं.

GWP-ASan, उन सभी ऐलोकेशन के बारे में ज़्यादा जानकारी इकट्ठा करता है जिन्हें वह इंटरसेप्ट करता है. यह जानकारी तब उपलब्ध होती है, जब GWP-ASan को मेमोरी की सुरक्षा के उल्लंघन का पता चलता है और इसे अपने-आप नेटिव क्रैश रिपोर्ट में डाल दिया जाता है. इससे, डीबग करने में काफ़ी मदद मिल सकती है (उदाहरण देखें).

GWP-ASan को इस तरह से डिज़ाइन किया गया है कि वह सीपीयू से जुड़े ज़्यादा ओवरहेड न लगे. GWP-ASan को चालू करने पर, रैम का इस्तेमाल थोड़ा बढ़ जाता है. यह ओवरहेड, Android सिस्टम तय करता है. फ़िलहाल, इस पर असर होने वाली हर प्रोसेस के लिए यह करीब 70 किबीबाइट (KiB) है.

अपने ऐप्लिकेशन के लिए ऑप्ट-इन करें

ऐप्लिकेशन, GWP-ASan को हर प्रोसेस के लेवल पर चालू कर सकते हैं. इसके लिए, ऐप्लिकेशन मेनिफ़ेस्ट में android:gwpAsanMode टैग का इस्तेमाल करें. ये विकल्प काम करते हैं:

  • हमेशा बंद रहे (android:gwpAsanMode="never"): यह सेटिंग आपके ऐप्लिकेशन में GWP-ASan को पूरी तरह से बंद कर देती है. यह सेटिंग, सिस्टम ऐप्लिकेशन के लिए डिफ़ॉल्ट रूप से लागू होती है.

  • डिफ़ॉल्ट (android:gwpAsanMode="default" या तय नहीं किया गया): Android 13 (एपीआई लेवल 33) और इससे पहले के वर्शन - GWP-ASan को बंद किया गया है. Android 14 (एपीआई लेवल 34) और उसके बाद के वर्शन के लिए, वापस पाने लायक GWP-ASan की सुविधा चालू है.

  • हमेशा चालू रहने वाला (android:gwpAsanMode="always"): यह सेटिंग आपके ऐप्लिकेशन में GWP-ASan को चालू करती है. इसमें, ये शामिल हैं:

    1. ऑपरेटिंग सिस्टम, GWP-ASan के ऑपरेशन के लिए तय रैम रिज़र्व करता है. जिन प्रोसेस पर असर पड़ा है उनके लिए, करीब 70 केबी रैम रिज़र्व की जाती है. (अगर मेमोरी का इस्तेमाल बढ़ाने में आपका ऐप्लिकेशन ज़्यादा संवेदनशील नहीं है, तो GWP-ASan को चालू करें.)

    2. GWP-ASan, हीप ऐलोकेशन के रैंडम तरीके से चुने गए सबसेट को बीच में रोकता है और उन्हें एक ऐसे खास इलाके में रख देता है जहां मेमोरी की सुरक्षा से जुड़े उल्लंघनों का भरोसेमंद तरीके से पता लगाया जा सकता है.

    3. जब किसी खास इलाके में मेमोरी की सुरक्षा से जुड़ा उल्लंघन होता है, तो GWP-ASan ने प्रोसेस को खत्म कर दिया है.

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

GWP-ASan की वह गड़बड़ी जिसे ठीक किया जा सकता है

Android 14 (एपीआई लेवल 34) और उसके साथ बेहतर तरीके से काम करने वाली सुविधा रिकवर करने लायक GWP-ASan, जिससे डेवलपर को उपयोगकर्ता अनुभव को खराब किए बिना, प्रोडक्शन में हीप-बफ़र-ओवरफ़्लो और हीप-यूज़-आज़-फ़्री गड़बड़ियां खोजने में मदद मिलती है. जब AndroidManifest.xml में android:gwpAsanMode की जानकारी नहीं दी जाती है, तो ऐप्लिकेशन, Recoverable GWP-ASan का इस्तेमाल करता है.

रिकवर किया जा सकने वाला GWP-ASan, बुनियादी GWP-ASan से इन मामलों में अलग होता है:

  1. वापस पाने लायक GWP-ASan को हर ऐप्लिकेशन लॉन्च के बजाय, करीब 1% ऐप्लिकेशन लॉन्च पर ही चालू किया जाता है.
  2. जब 'खाली करने के बाद हेप का इस्तेमाल' या 'हेप बफ़र ओवरफ़्लो' गड़बड़ी का पता चलता है, तो यह गड़बड़ी क्रैश रिपोर्ट (टॉम्बस्टोन) में दिखती है. यह क्रैश रिपोर्ट, ActivityManager#getHistoricalProcessExitReasons एपीआई के ज़रिए उपलब्ध है और मूल GWP-ASan के ज़रिए देखी जा सकती है.
  3. क्रैश रिपोर्ट को डंप करने के बाद, रिकवर करने लायक GWP-ASan की मदद से, मेमोरी में गड़बड़ी होती है और ऐप्लिकेशन बिना किसी रुकावट के चलता रहता है. यह प्रोसेस पहले की तरह जारी रह सकती है. हालांकि, अब ऐप्लिकेशन के काम करने के तरीके के बारे में नहीं बताया गया है. मेमोरी खराब होने की वजह से, आने वाले समय में यह ऐप्लिकेशन किसी तय जगह पर क्रैश हो सकता है. इसके अलावा, यह लोगों को दिखने वाले असर के बिना भी काम करना जारी रख सकता है.
  4. क्रैश रिपोर्ट को डंप करने के बाद, वापस पाया जा सकने वाला GWP-ASan बंद हो जाता है. इसलिए, ऐप्लिकेशन के हर लॉन्च के लिए, GWP-ASan की सिर्फ़ एक ऐसी रिपोर्ट मिल सकती है जिसे ठीक किया जा सकता है.
  5. अगर ऐप्लिकेशन में कोई कस्टम सिग्नल हैंडलर इंस्टॉल है, तो उसे कभी भी SIGSEGV सिग्नल के लिए नहीं बुलाया जाता. यह सिग्नल, ठीक की जा सकने वाली GWP-ASan गड़बड़ी का संकेत देता है.

रिकवर किए जा सकने वाले GWP-ASan के क्रैश से, असली उपयोगकर्ताओं के डिवाइसों की मेमोरी खराब होने के मामलों का पता चलता है. इसलिए, हमारा सुझाव है कि जिन गड़बड़ियों को ठीक किया जा सकता है उन्हें ठीक करके, सबसे पहले जांच लें और उन्हें ठीक करें.

डेवलपर सहायता

इन सेक्शन में, GWP-ASan का इस्तेमाल करते समय होने वाली समस्याओं और उन्हें हल करने के तरीकों के बारे में बताया गया है.

ऐलोकेशन/डिऐलोकेशन ट्रेस मौजूद नहीं हैं

अगर किसी नेटिव क्रैश का पता लगाने पर, आपको पता चलता है कि ऐलोकेशन/डिलोकेशन फ़्रेम मौजूद नहीं हैं, तो हो सकता है कि आपके ऐप्लिकेशन में फ़्रेम पॉइंटर मौजूद न हों. GWP-ASan, परफ़ॉर्मेंस की वजहों से, ऐलोकेशन और डीललोकेशन ट्रेस को रिकॉर्ड करने के लिए फ़्रेम पॉइंटर का इस्तेमाल करता है. अगर स्टैक ट्रेस मौजूद नहीं है, तो यह उसे खोला नहीं जा सकता.

फ़्रेम पॉइंटर, आर्म64 डिवाइसों के लिए डिफ़ॉल्ट रूप से चालू रहते हैं और आर्म32 डिवाइसों के लिए, डिफ़ॉल्ट रूप से बंद होते हैं. ऐप्लिकेशन के पास 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 नेटिव क्रैश रिपोर्ट के बारे में ज़्यादा जानने के लिए, नेटिव क्रैश की गड़बड़ी का पता लगाना लेख पढ़ें.