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 บางส่วนจากไลบรารีแบบคงที่ที่ใช้