تذكرة GWP-ASan

أداة 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 في لمساعدتك في العثور على الأخطاء المتعلقة بالذاكرة، وإعداد التطبيق إتاحة إضافة وضع علامات الذاكرة (MTE). توفّر آليات أخذ العينات أيضًا الموثوقية مقارنةً طلبات البحث عن الموت.

وبعد تفعيل هذه الميزة، يعترض GWP-ASan مجموعة فرعية يتم اختيارها عشوائيًا من تخصيصات لعناصر متعدّدة، ويضعها في منطقة خاصة ترصد أكوامًا يصعب اكتشافها تلف الذاكرة. ومع توفر عدد كافٍ من المستخدمين، حتى إذا كان معدل أخذ العينات المنخفض هذا العثور على أخطاء متعلقة بأمان الذاكرة لا يتم رصدها عبر الاختبارات المنتظمة. على سبيل المثال، عثر GWP-ASan على كثيرًا من الأخطاء في متصفّح Chrome (لا يزال العديد منها ضمن العرض المشروط).

يجمع برنامج GWP-ASan معلومات إضافية عن جميع التخصيصات التي الاعتراضات. تتوفّر هذه المعلومات عندما يرصد برنامج GWP-ASan أمان الذاكرة الانتهاك تلقائيًا، ويتمّ إدراجه تلقائيًا في تقرير الأعطال المدمج مع المحتوى، ما قد يؤدي إلى تساعد بشكل كبير في تصحيح الأخطاء (راجِع المثال).

تم تصميم أداة GWP-ASan بدون تحمّل أي أعباء كبيرة على وحدة المعالجة المركزية (CPU). أداة GWP-ASan تؤدي إلى حمل ذاكرة وصول عشوائي صغيرة وثابتة عند تفعيلها. يتم تحديد هذا النفقات العامة من خلال نظام Android ويبلغ حاليًا حجم 70 كيبيبايت تقريبًا لكل منها المتأثرة.

تفعيل تطبيقك

يمكن أن تفعّل التطبيقات أداة 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 القابلة للاسترداد، ما يساعد يجد المطورون أخطاءً متعلقة بتجاوز سعة التخزين المؤقت والأخطاء في استخدام الإنتاج دون التأثير سلبًا على تجربة المستخدم. عندما يكون android:gwpAsanMode غير محدد في AndroidManifest.xml، يستخدم التطبيق "قابل للاسترداد" GWP-ASan.

يختلف برنامج GWP-ASan القابل للاسترداد عن GWP-ASan الأساسي في النواحي التالية:

  1. لا يتم تفعيل ميزة GWP-ASan القابلة للاسترداد إلا في حوالي 1% من عمليات إطلاق التطبيقات، بدلاً من كل عملية تشغيل للتطبيق.
  2. عند رصد خطأ أثناء استخدام لقطات لأجزاء من الذاكرة أو تجاوزها لأجزاء من الذاكرة المؤقتة، يعرض هذا الخطأ يظهر في تقرير الأعطال (علامة Tombstone). تقرير الأعطال هذا متاح من خلال ActivityManager#getHistoricalProcessExitReasons واجهة برمجة التطبيقات هي نفسها واجهة GWP-ASan الأصلية.
  3. بدلاً من الخروج بعد تفريغ تقرير الأعطال، يمكن استخدام GWP-ASan القابل للاسترداد إلى حدوث تلف في الذاكرة مع استمرار تشغيل التطبيق. على الرغم من أنّ العملية قد تستمر كالمعتاد، لم يعُد سلوك التطبيق المحددة. قد يتعطّل التطبيق أحيانًا بسبب تلف الذاكرة أو قد تستمر بدون أي تأثير مرئي للمستخدم.
  4. تم إيقاف GWP-ASan القابل للاسترداد بعد إرسال تقرير الأعطال. ولذلك، يمكن لأي تطبيق الحصول على تقرير واحد فقط من GWP-ASan قابل للاسترداد لكل عملية تشغيل للتطبيق.
  5. إذا تم تثبيت معالج إشارات مخصّصة في التطبيق، لن يتم طلبه أبدًا إشارة SIGSEGV التي تدل على خطأ GWP-ASan القابل للاسترداد.

لأنّ أعطال GWP-ASan القابلة للاسترداد تشير إلى المثيلات الحقيقية للذاكرة الفساد على أجهزة المستخدمين النهائيين، فإننا ننصح بشدة بفرز الأخطاء وإصلاحها التي تم تحديدها بواسطة 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 الأصلية، راجع تشخيص الأعطال الأصلية: