GWP-ASan

GWP-ASan เป็นฟีเจอร์ตัวจัดสรรหน่วยความจำแบบเนทีฟที่ช่วยค้นหา ใช้หลังใช้ฟรี และ การล้นบัฟเฟอร์ฮีป ข้อบกพร่อง ชื่อไม่เป็นทางการของตัวย่อชื่อ "GWP-ASan Will ระบุสถานที่ตั้ง SAN" เลิกชอบ HWASan หรือ แก้ไขข้อบกพร่อง Malloc GWP-ASan ไม่ต้องมีแหล่งที่มาหรือการรวบรวมใหม่ (กล่าวคือ ทำงานร่วมกับ ที่สร้างไว้ล่วงหน้า) และทำงานได้ทั้งในกระบวนการ 32 บิตและ 64 บิต (แต่การขัดข้อง 32 บิต มีข้อมูลการแก้ไขข้อบกพร่องน้อยลง) หัวข้อนี้สรุปสิ่งที่คุณต้องทำเพื่อเปิดใช้ฟีเจอร์นี้ใน แอป GWP-ASan มีให้บริการในแอปที่กำหนดเป้าหมายเป็น Android 11 (API ระดับ 30) ขึ้นไป

ภาพรวม

GWP-ASan เปิดใช้อยู่ในแอปพลิเคชันระบบและแพลตฟอร์มที่เลือกแบบสุ่มบางรายการ ไฟล์ปฏิบัติการเมื่อเริ่มต้นกระบวนการ (หรือเมื่อไซโกตแยก) เปิดใช้ GWP-ASan ใน แอปของคุณเองเพื่อช่วยคุณค้นหาข้อบกพร่องที่เกี่ยวกับหน่วยความจำ และเพื่อเตรียมแอปของคุณให้พร้อม การรองรับของ ARM Memory gtag Extension (MTE) กลไกการสุ่มตัวอย่างการจัดสรรยังให้ความน่าเชื่อถือกับ การแกล้งตาย

เมื่อเปิดใช้แล้ว GWP-ASan จะสกัดกั้นการจัดสรรฮีปเพียงบางส่วนที่เลือกแบบสุ่ม แล้วนำไปวางลงในพื้นที่พิเศษที่จะดักจับฮีปที่ตรวจจับได้ยาก ข้อบกพร่องของหน่วยความจำ หากมีผู้ใช้จำนวนมากพอ อัตราการสุ่มตัวอย่างต่ำนี้จะ ค้นหาข้อบกพร่องด้านความปลอดภัยของหน่วยความจำฮีปที่ไม่ได้พบผ่านการทดสอบทั่วไป ตัวอย่างเช่น GWP-ASan พบว่า ข้อบกพร่องจำนวนมาก ในเบราว์เซอร์ Chrome (ซึ่งส่วนใหญ่ยังอยู่ในมุมมองที่จำกัด)

GWP-ASan รวบรวมข้อมูลเพิ่มเติมเกี่ยวกับการจัดสรรทั้งหมดที่ โดยตรง ข้อมูลนี้จะพร้อมใช้งานเมื่อ GWP-ASan ตรวจพบความปลอดภัยของหน่วยความจํา ของคุณ และถูกนำไปไว้ในรายงานข้อขัดข้องของระบบโดยอัตโนมัติ ซึ่งสามารถ ช่วยในการแก้ไขข้อบกพร่องอย่างมาก (ดูตัวอย่าง)

GWP-ASan ออกแบบมาเพื่อให้ไม่มีค่าใช้จ่ายในการดำเนินการ CPU ที่สำคัญ GWP-ASan มีการใช้โอเวอร์เฮดของ RAM แบบคงที่ซึ่งมีขนาดเล็กเมื่อเปิดใช้งาน ค่าใช้จ่ายในการดำเนินการดังกล่าวจะกำหนดโดย ระบบ Android และปัจจุบันมีขนาดประมาณ 70 กิบิไบต์ (KiB) สำหรับแต่ละ ที่ได้รับผลกระทบ

เลือกใช้แอปของคุณ

แอปในระดับต่อกระบวนการสามารถเปิดใช้ GWP-ASan ได้โดยใช้ android:gwpAsanMode ในไฟล์ Manifest ของแอป ตัวเลือกต่อไปนี้ รองรับ

  • ปิดใช้เสมอ (android:gwpAsanMode="never"): การตั้งค่านี้อย่างสมบูรณ์ ปิดใช้ GWP-ASan ในแอปและเป็นค่าเริ่มต้นสำหรับแอปที่ไม่ใช่ระบบ

  • ค่าเริ่มต้น (android:gwpAsanMode="default" หรือไม่ได้ระบุ): Android 13 (API ระดับ 33) และต่ำกว่า - GWP-ASan ถูกปิดใช้ Android 14 (API ระดับ 34) และ ที่สูงกว่า - เปิดใช้ GWP-ASan ที่กู้คืนได้อยู่

  • เปิดใช้เสมอ (android:gwpAsanMode="always"): การตั้งค่านี้จะเปิดใช้ GWP-ASan ในแอปของคุณ ซึ่งมีสิ่งต่อไปนี้

    1. ระบบปฏิบัติการจะจอง RAM ในปริมาณคงที่สำหรับ GWP-ASan ประมาณ 70KiB สำหรับแต่ละกระบวนการที่ได้รับผลกระทบ (เปิดใช้ 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 (API ระดับ 34) ขึ้นไปรองรับ GWP-ASan ที่กู้คืนได้ ซึ่งช่วยให้ พบข้อบกพร่องใน เวอร์ชันที่ใช้งานจริงโดยไม่ทำให้ประสบการณ์ของผู้ใช้แย่ลง เมื่อandroid:gwpAsanModeคือ ไม่ได้ระบุใน AndroidManifest.xml แอปใช้การกู้คืนได้ GWP-ASan

GWP-ASan ที่กู้คืนได้จะแตกต่างจาก GWP-ASan พื้นฐานดังนี้

  1. GWP-ASan ที่กู้คืนได้จะเปิดใช้กับการเปิดตัวแอปประมาณ 1% เท่านั้น มากกว่าการเรียกใช้แอปพลิเคชันทุกครั้ง
  2. เมื่อตรวจพบข้อบกพร่องการใช้ฮีปหลังใช้ฟรีหรือฮีป-บัฟเฟอร์ล้นเกิน ข้อบกพร่องนี้ จะปรากฏในรายงานข้อขัดข้อง (Tombstone) รายงานข้อขัดข้องนี้พร้อมใช้งาน ผ่านทาง ActivityManager#getHistoricalProcessExitReasons API แบบเดียวกับ GWP-ASan เดิม
  3. GWP-ASan กู้คืนได้ แทนที่จะออกหลังจากบันทึกข้อมูลข้อขัดข้อง จึงอาจทำให้เกิดความเสียหายของหน่วยความจำ และแอปจะยังคงทำงานต่อไป แม้ว่ากระบวนการอาจดำเนินต่อไปตามปกติ แต่ลักษณะการทำงานของแอปไม่ได้แล้ว ที่ระบุ เนื่องจากหน่วยความจำเสียหาย แอปอาจขัดข้องในบางกรณี ในอนาคต ไม่เช่นนั้นอาจส่งผลกระทบต่อเนื่องโดยไม่มีผลกระทบต่อผู้ใช้
  4. GWP-ASan ที่กู้คืนได้ถูกปิดใช้หลังจากถ่ายโอนรายงานข้อขัดข้อง ดังนั้น แอปจะได้รับรายงาน GWP-ASan ที่กู้คืนได้เพียงรายการเดียวต่อการเปิดแอป 1 ครั้ง
  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 ไปใช้งานได้ที่ เอกสารประกอบเกี่ยวกับ LLC เพื่อเรียนรู้ ข้อมูลเพิ่มเติมเกี่ยวกับรายงานข้อขัดข้องของระบบ Android โปรดดู การวิเคราะห์ข้อขัดข้องของระบบ