แก้ปัญหาหน่วยความจำเสียหายโดยใช้ตัวล้างที่อยู่

เอกสารนี้แสดงวิธีเปิดใช้เครื่องมือแก้ไขข้อบกพร่องพิเศษเมื่อใช้ AGDE เครื่องมือเหล่านี้ช่วยแก้ปัญหาการเสียหายของหน่วยความจำและข้อผิดพลาดในการเขียนทับที่วินิจฉัยได้ยาก

HWAddress Sanitizer และ Address Sanitizer

HWAddress Sanitizer (HWASan) และ Address Sanitizer (ASan) เป็นเครื่องมือแก้ไขข้อบกพร่องการเสียหายของหน่วยความจําที่ช่วยแก้ไขข้อบกพร่องการเสียหายของหน่วยความจําและการเขียนทับ เช่นข้อผิดพลาดต่อไปนี้

  • การล้นและการลบล้างบัฟเฟอร์ของกองซ้อน
  • การล้นและการไม่เพียงพอของบัฟเฟอร์กองขยะ
  • การใช้สแต็กนอกขอบเขต
  • ข้อผิดพลาด Double free และ Wild free
  • การใช้สแต็กหลังจากการกลับมา (HWASan เท่านั้น)

เราขอแนะนำให้เปิดใช้ HWASan หรือ ASan เฉพาะในกรณีที่กำลังแก้ไขข้อบกพร่องหรือเป็นส่วนหนึ่งของการทดสอบอัตโนมัติเท่านั้น แม้ว่าเครื่องมือเหล่านี้จะมีประสิทธิภาพ แต่การใช้เครื่องมือเหล่านี้ก็อาจส่งผลให้มีการหักคะแนน

ลักษณะการทํางานของรันไทม์

เมื่อเปิดใช้ ทั้ง HWASan และ ASan จะตรวจสอบข้อบกพร่องของหน่วยความจำในแอปโดยอัตโนมัติ

หากตรวจพบข้อผิดพลาดเกี่ยวกับหน่วยความจำ แอปจะขัดข้องพร้อมข้อผิดพลาด SIGBART (signal abort) และพิมพ์ข้อความโดยละเอียดไปยัง logcat ระบบจะเขียนสำเนาของข้อความลงในไฟล์ภายใต้ /data/tombstones ด้วย

ข้อความแสดงข้อผิดพลาดจะมีลักษณะคล้ายกับตัวอย่างต่อไปนี้

ERROR: HWAddressSanitizer: tag-mismatch on address 0x0042a0826510 at pc 0x007b24d90a0c
WRITE of size 1 at 0x0042a0826510 tags: 32/3d (ptr/mem) in thread T0
    #0 0x7b24d90a08  (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x2a08)
    #1 0x7b8f1e4ccc  (/apex/com.android.art/lib64/libart.so+0x198ccc)
    #2 0x7b8f1db364  (/apex/com.android.art/lib64/libart.so+0x18f364)
    #3 0x7b8f2ad8d4  (/apex/com.android.art/lib64/libart.so+0x2618d4)

0x0042a0826510 is located 0 bytes to the right of 16-byte region [0x0042a0826500,0x0042a0826510)
allocated here:
    #0 0x7b92a322bc  (/apex/com.android.runtime/lib64/bionic/libclang_rt.hwasan-aarch64-android.so+0x212bc)
    #1 0x7b24d909e0  (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x29e0)
    #2 0x7b8f1e4ccc  (/apex/com.android.art/lib64/libart.so+0x198ccc)

สิ่งที่ต้องมีก่อน

ข้อกำหนดของ HWASan

วิธีใช้ HWASan

  • คุณต้องใช้ AGDE 24.1.99 ขึ้นไป
  • แอปต้องสร้างโดยใช้ NDK 26 ขึ้นไป
  • แอปต้องสร้างด้วย SDK เป้าหมาย 34 ขึ้นไป
  • เป้าหมายต้องเป็นอุปกรณ์ arm64-v8a ที่ใช้ Android 14 (API ระดับ 34) ขึ้นไป

ใช้ไลบรารีมาตรฐาน C++ ที่แชร์ในโปรเจ็กต์

ASan เข้ากันไม่ได้กับการจัดการข้อยกเว้น C++ เมื่อใช้ libc++_static เนื่องจากปัญหาที่ทราบแล้ว ปัญหานี้จะไม่เกิดขึ้นเมื่อใช้ libc++_shared

HWASan มีการใช้งานโอเปอเรเตอร์ new และ delete ของตัวเอง ซึ่งจะใช้ไม่ได้หากมีการลิงก์ไลบรารีมาตรฐานแบบคงที่ไว้ในโปรเจ็กต์

หากต้องการเปลี่ยนการตั้งค่านี้ โปรดดูส่วนการลิงก์คลังมาตรฐาน C++ ของเอกสารนี้

เปิดใช้การสร้างเคอร์เซอร์เฟรม

HWASan และ ASan ใช้โปรแกรมยกเลิกการซ้อนที่ทำงานเร็วซึ่งอิงตามตัวชี้เฟรมเพื่อสร้างข้อมูลการติดตามสแต็กสำหรับเหตุการณ์การจัดสรรและการยกเลิกการจัดสรรหน่วยความจำ ซึ่งหมายความว่าคุณต้องเปิดใช้การสร้างเคอร์เซอร์เฟรมในการตั้งค่าคอมไพเลอร์ C++ เพื่อใช้ฟีเจอร์เหล่านี้ กล่าวคือ คุณต้องปิดใช้การเพิ่มประสิทธิภาพการละเว้นเคอร์เซอร์เฟรม

หากต้องการเปลี่ยนการตั้งค่านี้ โปรดดูส่วนการเปิดใช้การสร้างเคอร์เซอร์เฟรมของเอกสารนี้

การกำหนดค่าโปรเจ็กต์ Visual Studio ให้ใช้ HWASan หรือ ASan

การเปิดใช้ HWASan หรือ ASan

หากต้องการเปิดใช้ HWASan หรือ ASan ให้ไปที่พร็อพเพอร์ตี้การกําหนดค่า > ทั่วไปในหน้าพร็อพเพอร์ตี้ของโปรเจ็กต์

เมนูพร็อพเพอร์ตี้ของ Visual Studio Solution Explorer สำหรับโปรเจ็กต์ปัจจุบัน

รูปที่ 1: ตัวเลือกพร็อพเพอร์ตี้ของโปรเจ็กต์ในหน้าต่าง Visual Studio Solution Explorer

กล่องโต้ตอบหน้าพร็อพเพอร์ตี้ของโปรเจ็กต์ที่แสดงพร็อพเพอร์ตี้ทั่วไป และไฮไลต์การตั้งค่า Address Sanitizer

รูปที่ 2: การตั้งค่า Address Sanitizer (ASan) ในพร็อพเพอร์ตี้ทั่วไปของโปรเจ็กต์

หากต้องการเปิดใช้ HWASan สําหรับโปรเจ็กต์ ให้เปลี่ยนการตั้งค่า Address Sanitizer (ASan) เป็น Hardware ASan Enabled (fsanitize=hwaddress)

หากต้องการเปิดใช้ ASan สําหรับโปรเจ็กต์ ให้เปลี่ยนการตั้งค่า Address Sanitizer (ASan) เป็น ASan Enabled (fsanitize=address)

การเปิดใช้การสร้างเคอร์เซอร์เฟรม

การสร้างตัวชี้เฟรมจะควบคุมโดยการตั้งค่าคอมไพเลอร์ C/C++ ละเว้นตัวชี้เฟรม และสามารถดูได้ในหน้าพร็อพเพอร์ตี้ของโปรเจ็กต์ในส่วนพร็อพเพอร์ตี้การกําหนดค่า > C/C++ > การเพิ่มประสิทธิภาพ

กล่องโต้ตอบหน้าพร็อพเพอร์ตี้ของโปรเจ็กต์ที่แสดงพร็อพเพอร์ตี้การเพิ่มประสิทธิภาพ C/C++ และไฮไลต์การตั้งค่า "ละเว้นเคอร์เซอร์เฟรม"

รูปที่ 3: ตําแหน่งการตั้งค่าละเว้นเคอร์เซอร์เฟรม

เมื่อใช้ HWASan หรือ ASan ให้ตั้งค่าละเว้นตัวชี้เฟรมเป็นไม่ (-fno-omit-frame-pointer)

การลิงก์คลังมาตรฐาน C++ ในโหมดไลบรารีที่ใช้ร่วมกัน

การตั้งค่าโหมด linker สําหรับคลังมาตรฐาน C++ จะอยู่ในหน้าพร็อพเพอร์ตี้ของโปรเจ็กต์ในส่วนพร็อพเพอร์ตี้การกําหนดค่า > ทั่วไป ในส่วนค่าเริ่มต้นของโปรเจ็กต์

กล่องโต้ตอบหน้าพร็อพเพอร์ตี้ของโปรเจ็กต์ที่เลือกหมวดหมู่ทั่วไป และไฮไลต์การตั้งค่าการใช้ STL

รูปที่ 4: ตําแหน่งที่จะพบการตั้งค่าโหมด linker สําหรับคลังมาตรฐาน C++

ขณะใช้ HWASan หรือ ASan ให้ตั้งค่าการใช้ STL เป็นใช้คลังมาตรฐาน C++ (.so) ค่านี้จะลิงก์ไลบรารีมาตรฐาน C++ เข้ากับโปรเจ็กต์ของคุณเป็นไลบรารีที่แชร์ ซึ่งจำเป็นต่อการทำงานที่ถูกต้องของ HWASan และ ASan

การสร้างการกำหนดค่าการสร้างสําหรับการใช้โปรแกรมตรวจสอบที่อยู่ที่ปลอดภัย

หากต้องการใช้ HWASan หรือ ASan ชั่วคราว คุณอาจไม่ต้องการสร้างการกำหนดค่าบิลด์ใหม่เพื่อใช้กับเครื่องมือเหล่านี้เพียงอย่างเดียว กรณีนี้อาจเกิดขึ้นเมื่อโปรเจ็กต์มีขนาดเล็ก คุณกำลังสำรวจฟีเจอร์ หรือกำลังแก้ปัญหาที่คุณพบระหว่างการทดสอบ

อย่างไรก็ตาม หากพบว่ามีประโยชน์และวางแผนที่จะใช้เป็นประจำ คุณอาจพิจารณาสร้างการกำหนดค่าบิลด์ใหม่สำหรับ HWASan หรือ ASan ตามที่แสดงในตัวอย่าง Teapot คุณอาจทำเช่นนี้ในกรณีต่างๆ เช่น เรียกใช้ Address Sanitizer เป็นประจำเป็นส่วนหนึ่งของการทดสอบหน่วย หรือระหว่างการทดสอบการทำงานเบื้องต้นของเกมข้ามคืน

การสร้างการกำหนดค่าบิลด์แยกต่างหากอาจมีประโยชน์อย่างยิ่งหากคุณมีโปรเจ็กต์ขนาดใหญ่ที่ใช้ไลบรารีของบุคคลที่สามจำนวนมาก ซึ่งปกติแล้วคุณจะต้องลิงก์แบบคงที่กับไลบรารีมาตรฐาน C++ การกําหนดค่าบิลด์เฉพาะจะช่วยตรวจสอบว่าการตั้งค่าโปรเจ็กต์ถูกต้องอยู่เสมอ

หากต้องการสร้างการกำหนดค่าบิลด์ ให้คลิกปุ่มเครื่องมือจัดการการกําหนดค่า… จากหน้าพร็อพเพอร์ตี้ของโปรเจ็กต์ แล้วเปิดเมนูแบบเลื่อนลงการกําหนดค่าโซลูชันที่ใช้งานอยู่ จากนั้นเลือก และสร้างการกำหนดค่าบิลด์ใหม่โดยใช้ชื่อที่เหมาะสม (เช่น HWASan enabled)

การใช้ HWASan กับเครื่องมือจัดสรรหน่วยความจำที่กำหนดเอง

HWASan จะสกัดกั้นหน่วยความจำที่จัดสรรผ่าน malloc (หรือ new) โดยอัตโนมัติเพื่อให้สามารถแทรกแท็กลงในพอยน์เตอร์และตรวจสอบการจับคู่แท็กที่ไม่ตรงกัน

อย่างไรก็ตาม เมื่อใช้ตัวจัดสรรหน่วยความจําที่กําหนดเอง HWASan จะไม่สามารถดักรับเมธอดการจัดสรรหน่วยความจําที่กําหนดเองโดยอัตโนมัติ ดังนั้น หากต้องการใช้ HWASan กับตัวจัดสรรหน่วยความจำที่กำหนดเอง ให้เครื่องมือตัวจัดสรรหน่วยความจำเพื่อเรียกใช้ HWASan อย่างชัดแจ้ง ซึ่งทําได้โดยใช้โค้ดเพียงไม่กี่บรรทัด

สิ่งที่ต้องมีก่อน

วิธีการ HWASan ที่คุณจำเป็นต้องเรียกมีการกำหนดไว้ในส่วนหัวนี้

#include "sanitizer/hwasan_interface.h"

เครื่องมือวัดผลวิธีการจัดสรรหน่วยความจํา

  1. จัดสรรออบเจ็กต์ที่ความละเอียดและการจัดแนวบล็อก 16 ไบต์ เช่น หากคุณมีตัวจัดสรรพูลที่ให้บริการออบเจ็กต์ขนาดคงที่ 24 ไบต์ ให้ปัดเศษการจัดสรรขึ้นเป็น 32 ไบต์ และจัดแนวเป็น 16 ไบต์

  2. สร้างแท็ก 8 บิต แท็กต้องไม่ใช้ค่า 0-16 เนื่องจากค่าเหล่านั้นสงวนไว้สําหรับการใช้งานภายใน

  3. เปิดใช้ HWASan เพื่อเริ่มติดตามพื้นที่หน่วยความจำด้วยแท็กนั้น

    __hwasan_tag_memory((void*) address, tag, size);
    
  4. แทรกแท็กไปยัง 8 บิตบนสุดของตัวชี้

    address = __hwasan_tag_pointer((void*) address, tag);
    

เครื่องมือวัดวิธีการเพิ่มพื้นที่หน่วยความจำ

  1. รีเซ็ตแท็กสำหรับภูมิภาคหน่วยความจำเพื่อให้การเข้าถึงเพิ่มเติมผ่านพอยน์เตอร์ที่ติดแท็กที่มีอยู่ไม่สำเร็จ

    __hwasan_tag_memory(__hwasan_tag_pointer(ptr, 0), 0, size);
    

การทำงานกับพูลออบเจ็กต์ที่จัดสรรไว้ล่วงหน้า

หากตัวจัดสรรหน่วยความจำจัดสรรพื้นที่หน่วยความจำล่วงหน้าสำหรับออบเจ็กต์ในพูลและส่งคืนออบเจ็กต์กลับไปยังพูลแทนที่จะยกเลิกการจัดสรรหน่วยความจำจริง วิธีการยกเลิกการจัดสรรหน่วยความจำจะเขียนทับแท็กสำหรับหน่วยความจำและพอยน์เตอร์ด้วยค่าใหม่ได้โดยตรง ดังนี้

```
__hwasan_tag_memory(__hwasan_tag_pointer(ptr, 0), tag, size);
ptr = __hwasan_tag_pointer((void*)ptr, tag);
```

หากใช้เทคนิคนี้ วิธีการจัดสรรของคุณไม่จําเป็นต้องติดแท็กพอยน์เตอร์หรือบล็อกหน่วยความจํา แต่ให้ติดแท็กพอยน์เตอร์และบล็อกหน่วยความจําเมื่อคุณจัดสรรออบเจ็กต์ล่วงหน้าในพูล ดูตัวอย่างที่ใช้สไตล์นี้ได้ที่ PoolAllocator sample