AddressSanitizer

AddressSanitizer (ASan) เป็นเครื่องมือที่ทำงานเร็วซึ่งอิงตามคอมไพเลอร์สำหรับตรวจหาข้อบกพร่องเกี่ยวกับหน่วยความจำในโค้ดเนทีฟ

ASan จะตรวจหาสิ่งต่อไปนี้

  • สแต็กและฮีปบัฟเฟอร์ล้น/ล้นเกิน
  • การใช้งานฮีปหลังจากมีการปลดปล่อย
  • การใช้สแต็กนอกขอบเขต
  • ฟรีแบบคู่/ฟรีแบบไม่มีเงื่อนไข

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

Android 10 และ AOSP สาขาหลักบน AArch64 สนับสนุน AddressSanitizer ที่ใช้ฮาร์ดแวร์ (HWASan) เครื่องมือที่คล้ายกันที่มีโอเวอร์เฮดของ RAM ต่ำลงและ ในช่วงข้อบกพร่องที่ตรวจพบได้ HWASan จะตรวจหาการใช้งานสแต็กหลังจากการคืนค่า นอกเหนือจากข้อบกพร่องที่ ASan ตรวจพบ

HWASan มีค่าใช้จ่ายเพิ่มเติมเกี่ยวกับ CPU และขนาดโค้ดที่คล้ายกัน แต่ค่าใช้จ่ายเพิ่มเติมเกี่ยวกับ RAM น้อยกว่ามาก (15%) HWASan เป็นแบบไม่แน่นอน มีค่าแท็กที่เป็นไปได้เพียง 256 ค่า จึงมีค่าคงที่ 0.4% ก็อาจจะเกิดข้อผิดพลาด HWASan ไม่มีโซนสีแดงขนาดจำกัดของ ASan สำหรับตรวจหาการล้น และไม่มีพื้นที่กักกันแบบจำกัดความจุสำหรับตรวจหาการใช้งานหลังจากการปลดปล่อยหน่วยความจำแล้ว ดังนั้น HWASan จึงไม่สนใจว่าหน่วยความจำจะล้นมากน้อยเพียงใด หรือมีการปลดปล่อยหน่วยความจำไปนานเท่าใดแล้ว ซึ่งทำให้ HWASan ดีกว่า ASan คุณสามารถอ่านเพิ่มเติมเกี่ยวกับการออกแบบของ HWASan หรือเกี่ยวกับการใช้ HWASan ใน Android

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

เอกสารนี้จะอธิบายวิธีสร้างและเรียกใช้ Android บางส่วน/ทั้งหมดด้วย Asan หากคุณกำลังสร้างแอป SDK/NDK ด้วย ASan โปรดดู ตัวล้างที่อยู่ แทน

ล้างข้อมูลไฟล์ปฏิบัติการแต่ละรายการด้วย ASan

เพิ่ม LOCAL_SANITIZE:=address หรือ sanitize: { address: true } ลงในกฎการสร้างสําหรับไฟล์ปฏิบัติการ คุณสามารถค้นหาโค้ดเพื่อดูตัวอย่างที่มีอยู่หรือค้นหาตัวกรองที่พร้อมใช้งานอื่นๆ

เมื่อตรวจพบข้อบกพร่อง ASan จะพิมพ์รายงานแบบละเอียดทั้งในเอาต์พุตมาตรฐานและ logcat จากนั้นจะขัดข้อง

ล้างข้อมูลไลบรารีที่ใช้ร่วมกันด้วย ASan

จากวิธีการทำงานของ ASan ห้องสมุดที่สร้างด้วย ASan จะใช้งานได้โดย ไฟล์ปฏิบัติการที่สร้างขึ้นด้วย ASan

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

LOCAL_SANITIZE:=address
LOCAL_MODULE_RELATIVE_PATH := asan

ซึ่งจะวางคลังไว้ใน /system/lib/asan แทน /system/lib จากนั้นเรียกใช้ไฟล์ปฏิบัติการด้วยสิ่งต่อไปนี้

LD_LIBRARY_PATH=/system/lib/asan

สำหรับ Daemon ของระบบ ให้เพิ่มรายการต่อไปนี้ลงในส่วนที่เหมาะสมของ /init.rc หรือ /init.$device$.rc

setenv LD_LIBRARY_PATH /system/lib/asan

ยืนยันว่ากระบวนการใช้ไลบรารีจาก /system/lib/asan เมื่อนำเสนอโดยการอ่าน /proc/$PID/maps หากไม่เป็นเช่นนั้น คุณอาจต้องปิดใช้ SELinux โดยทำดังนี้

adb root
adb shell setenforce 0
# restart the process with adb shell kill $PID
# if it is a system service, or may be adb shell stop; adb shell start.

สแต็กเทรซที่ดียิ่งขึ้น

ASan ใช้โปรแกรมยกเลิกการจัดเฟรมที่รวดเร็วเพื่อบันทึกการติดตามกองซ้อนสําหรับเหตุการณ์การจัดสรรและการยกเลิกการจัดสรรหน่วยความจําทั้งหมดในโปรแกรม พบบ่อยที่สุด ของ Android สร้างขึ้นโดยไม่ใช้ตัวชี้เฟรม ด้วยเหตุนี้ คุณจึงมักได้รับเฟรมที่มีความหมายเพียง 1-2 เฟรมเท่านั้น ในการแก้ไขปัญหานี้ ให้สร้างไลบรารีอีกครั้งด้วย ASan (แนะนำ!) หรือด้วย:

LOCAL_CFLAGS:=-fno-omit-frame-pointer
LOCAL_ARM_MODE:=arm

หรือตั้งค่า ASAN_OPTIONS=fast_unwind_on_malloc=0 ในสภาพแวดล้อมกระบวนการ ซึ่งอาจใช้ CPU มาก ด้วย

สัญลักษณ์

ในช่วงแรก รายงาน ASan จะมีข้อมูลอ้างอิงถึงออฟเซตในไบนารีและไลบรารีที่แชร์ การดูข้อมูลไฟล์และบรรทัดมี 2 วิธีดังนี้

  • ตรวจสอบว่ามีไบนารี llvm-symbolizer ใน /system/bin llvm-symbolizer สร้างขึ้นจากแหล่งที่มาใน third_party/llvm/tools/llvm-symbolizer
  • กรองรายงานผ่านexternal/compiler-rt/lib/asan/scripts/symbolize.py สคริปต์

วิธีที่ 2 สามารถให้ข้อมูลเพิ่มเติม (เช่น สถานที่ตั้ง file:line แห่ง) เนื่องจาก ความพร้อมใช้งานของไลบรารีสัญลักษณ์ในโฮสต์

ASan ในแอป

ASan มองเห็นโค้ด Java ไม่ได้ แต่ตรวจพบข้อบกพร่องใน JNI ได้ ห้องสมุด โดยคุณต้องสร้างไฟล์ปฏิบัติการด้วย ASan ซึ่งในกรณีนี้คือ /system/bin/app_process(32|64) ซึ่งจะเปิดใช้ ASan ในแอปทั้งหมดในอุปกรณ์พร้อมกัน ซึ่งจะทำให้เกิดภาระงานหนัก แต่อุปกรณ์ที่มี RAM 2 GB ควรรองรับการดำเนินการนี้ได้

เพิ่ม LOCAL_SANITIZE:=address ลงในกฎการสร้าง app_process ใน frameworks/base/cmds/app_process เพิกเฉย ตอนนี้เป้าหมาย app_process__asan ในไฟล์เดียวกัน ยังคงมีอยู่ ณ เวลาที่คุณอ่านข้อความนี้)

แก้ไขส่วน service zygote ของไฟล์ system/core/rootdir/init.zygote(32|64).rc ที่เหมาะสมเพื่อเพิ่มบรรทัดต่อไปนี้ลงในบล็อกบรรทัดที่มีการเยื้องซึ่งมี class main โดยเยื้องเท่าๆ กัน

    setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib
    setenv ASAN_OPTIONS allow_user_segv_handler=true

บิลด์, adb sync, fastboot flash boot และรีบูต

ใช้พร็อพเพอร์ตี้ wrap

แนวทางในส่วนก่อนหน้านี้จะใส่ ASan ลงในแอปทุกแอปในระบบ (จริงๆ แล้วคือใส่ลงในกระบวนการ Zygote ทุกรายการที่สืบทอดมา) คุณจะเรียกใช้แอปเพียงแอปเดียว (หรือหลายแอป) ด้วย ASan ได้ เพิ่มโอเวอร์เฮดหน่วยความจำบางส่วนเพื่อให้การเริ่มต้นแอปที่ช้าลง

ซึ่งทำได้โดยเริ่มแอปด้วยพร็อพเพอร์ตี้ wrap. ตัวอย่างต่อไปนี้เรียกใช้แอป Gmail ภายใต้ ASan

adb root
adb shell setenforce 0  # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"

ในบริบทนี้ asanwrapper จะเขียน /system/bin/app_process ใหม่เป็น /system/bin/asan/app_process ซึ่งสร้างขึ้นด้วย ASan รวมถึงเพิ่ม /system/lib/asan ที่จุดเริ่มต้นของเส้นทางการค้นหาคลังแบบไดนามิก วิธีนี้ใช้โดย ASan ไลบรารีจาก /system/lib/asan เหมาะสำหรับไลบรารีปกติ ใน /system/lib เมื่อทำงานกับ asanwrapper

หากพบข้อบกพร่อง แอปจะขัดข้องและระบบจะพิมพ์รายงานไปยังบันทึก

ทำให้เป้าหมายเสร็จสมบูรณ์

Android 7.0 ขึ้นไปมีการสนับสนุนในการสร้างแพลตฟอร์ม Android ทั้งแพลตฟอร์มด้วย ASan ได้พร้อมกัน (หากคุณกำลังสร้างรุ่นที่สูงกว่า Android 9 การใช้ HWASan ถือเป็นตัวเลือกที่ดีกว่า)

เรียกใช้คำสั่งต่อไปนี้ในโครงสร้างการสร้างเดียวกัน

make -j42
SANITIZE_TARGET=address make -j42

ในโหมดนี้ userdata.img จะมีไลบรารีเพิ่มเติมและต้อง ในอุปกรณ์ด้วย ใช้บรรทัดคำสั่งต่อไปนี้

fastboot flash userdata && fastboot flashall

ซึ่งจะสร้างไลบรารีที่ใช้ร่วมกัน 2 ชุด ได้แก่ ไลบรารีปกติใน /system/lib (การเรียกใช้ make ครั้งแรก) และไลบรารีที่มี ASan ตรวจสอบใน /data/asan/lib (การเรียกใช้ make ครั้งที่สอง) ไฟล์ดำเนินการจาก บิลด์ที่ 2 จะเขียนทับบิลด์จากบิลด์แรก ใช้เครื่องดนตรี ASan ไฟล์ปฏิบัติการจะได้รับเส้นทางการค้นหาไลบรารีอื่นที่รวม /data/asan/lib ก่อน /system/lib ผ่านการใช้ /system/bin/linker_asan ใน PT_INTERP

ระบบบิลด์จะลบไดเรกทอรีออบเจ็กต์กลางออกเมื่อค่า $SANITIZE_TARGET มีการเปลี่ยนแปลง ซึ่งจะบังคับให้สร้างเป้าหมายทั้งหมดขึ้นใหม่โดยที่ยังเก็บไบนารีที่ติดตั้งไว้ภายใต้ /system/lib ไว้

เป้าหมายบางรายการสร้างด้วย ASan ไม่ได้

  • ไฟล์ปฏิบัติการที่ลิงก์แบบคงที่
  • เป้าหมาย LOCAL_CLANG:=false
  • LOCAL_SANITIZE:=false ไม่ได้ผ่าน ASan สำหรับ SANITIZE_TARGET=address

ระบบจะข้ามไฟล์ปฏิบัติการเช่นนี้ในบิลด์ SANITIZE_TARGET และ เวอร์ชันจากการเรียกใช้ครั้งแรกยังคงอยู่ใน /system/bin

ห้องสมุดลักษณะนี้สร้างขึ้นโดยไม่มี ASan ไฟล์เหล่านี้อาจมีโค้ด ASan บางส่วนจากไลบรารีแบบคงที่ที่ใช้

เอกสารประกอบ