AddressSanitizer (ASan) เป็นเครื่องมือคอมไพเลอร์ที่รวดเร็วสำหรับการตรวจจับจุดบกพร่องของหน่วยความจำในโค้ดเนทีฟ
ASan ตรวจพบ:
- สแต็กและฮีปบัฟเฟอร์ล้น/อันเดอร์โฟลว์
- ใช้กองหลังจากฟรี
- ใช้สแต็คนอกขอบเขต
- ฟรีสองเท่า/ไวด์ฟรี
ASan ทำงานบน ARM ทั้งแบบ 32 บิตและ 64 บิต รวมทั้ง x86 และ x86-64 โอเวอร์เฮด CPU ของ ASan ประมาณ 2x โอเวอร์เฮดขนาดโค้ดอยู่ระหว่าง 50% ถึง 2x และโอเวอร์เฮดหน่วยความจำขนาดใหญ่ (ขึ้นอยู่กับรูปแบบการจัดสรรของคุณ แต่เรียงตามลำดับ 2x)
Android 10 และสาขาหลัก AOSP บน AArch64 รองรับ ASan ที่เร่งด้วยฮาร์ดแวร์ (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 โปรดดู Address Sanitizer แทน
ฆ่าเชื้อไฟล์ปฏิบัติการแต่ละรายการด้วยASan
เพิ่ม LOCAL_SANITIZE:=address
หรือ sanitize: { address: true }
ให้กับกฎการสร้างสำหรับการเรียกทำงาน คุณสามารถค้นหาโค้ดสำหรับตัวอย่างที่มีอยู่หรือค้นหาผลิตภัณฑ์ฆ่าเชื้ออื่นๆ ที่มีอยู่
เมื่อตรวจพบจุดบกพร่อง ASan จะพิมพ์รายงานโดยละเอียดทั้งไปยังเอาต์พุตมาตรฐานและไปยัง logcat
จากนั้นจึงหยุดกระบวนการ
ฆ่าเชื้อไลบรารีที่แชร์ด้วย ASan
เนื่องจากวิธีการทำงานของ ASan ไลบรารีที่สร้างด้วย ASan สามารถใช้ได้โดยไฟล์เรียกทำงานที่สร้างด้วย ASan เท่านั้น
ในการฆ่าเชื้อไลบรารีที่ใช้ร่วมกันที่ใช้ในไฟล์สั่งการหลายรายการ ไม่ใช่ทุกไฟล์ที่สร้างด้วย ASan คุณต้องมีไลบรารีสองชุด วิธีที่แนะนำในการทำเช่นนี้คือการเพิ่มสิ่งต่อไปนี้ใน Android.mk
สำหรับโมดูลที่เป็นปัญหา:
LOCAL_SANITIZE:=address LOCAL_MODULE_RELATIVE_PATH := asan
สิ่งนี้ทำให้ไลบรารี่ใน /system/lib/asan
แทน /system/lib
จากนั้นเรียกใช้ไฟล์ปฏิบัติการของคุณด้วย:
LD_LIBRARY_PATH=/system/lib/asan
สำหรับ system daemons ให้เพิ่มส่วนต่อไปนี้ในส่วนที่เหมาะสมของ /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 ส่วนใหญ่สร้างขึ้นโดยไม่มีตัวชี้เฟรม เป็นผลให้คุณมักจะได้รับเฟรมที่มีความหมายเพียงหนึ่งหรือสองเฟรม ในการแก้ไขปัญหานี้ ให้สร้างไลบรารีใหม่ด้วย ASan (แนะนำ!) หรือด้วย:
LOCAL_CFLAGS:=-fno-omit-frame-pointer LOCAL_ARM_MODE:=arm
หรือตั้งค่า ASAN_OPTIONS=fast_unwind_on_malloc=0
ในสภาพแวดล้อมกระบวนการ อย่างหลังอาจใช้ CPU มาก ขึ้นอยู่กับโหลด
สัญลักษณ์
เริ่มแรก รายงาน ASan มีการอ้างอิงถึงออฟเซ็ตในไบนารีและไลบรารีที่ใช้ร่วมกัน มีสองวิธีในการรับไฟล์ต้นฉบับและข้อมูลบรรทัด:
- ตรวจสอบให้แน่ใจว่าไบนารี
llvm-symbolizer
มีอยู่ใน/system/bin
llvm-symbolizer
สร้างขึ้นจากแหล่งที่มาในthird_party/llvm/tools/llvm-symbolizer
- กรองรายงานผ่าน
external/compiler-rt/lib/asan/scripts/symbolize.py
วิธีที่สองสามารถให้ข้อมูลเพิ่มเติม (นั่นคือตำแหน่ง 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, บูตแฟลช fastboot และรีบูต
การใช้คุณสมบัติห่อ
วิธีการในส่วนก่อนหน้านี้ทำให้ ASan อยู่ในทุกแอปในระบบ (ที่จริงแล้ว ในทุกลูกหลานของกระบวนการไซโกต) เป็นไปได้ที่จะเรียกใช้แอปเดียว (หรือหลายแอป) กับ 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-instrumented จาก /system/lib/asan
เป็นที่ต้องการของไลบรารีปกติใน /system/lib
เมื่อรันด้วย asanwrapper
หากพบจุดบกพร่อง แอปจะขัดข้อง และรายงานจะถูกพิมพ์ลงในบันทึก
SANITIZE_TARGET
Android 7.0 ขึ้นไปรวมถึงการสนับสนุนสำหรับการสร้างแพลตฟอร์ม Android ทั้งหมดด้วย ASan ในครั้งเดียว (หากคุณกำลังสร้างรุ่นที่สูงกว่า Android 9 HWASan เป็นตัวเลือกที่ดีกว่า)
เรียกใช้คำสั่งต่อไปนี้ในแผนผังการสร้างเดียวกัน
make -j42
SANITIZE_TARGET=address make -j42
ในโหมดนี้ userdata.img
มีไลบรารีพิเศษและต้องแฟลชไปยังอุปกรณ์ด้วย ใช้บรรทัดคำสั่งต่อไปนี้:
fastboot flash userdata && fastboot flashall
สิ่งนี้สร้างไลบรารีที่แบ่งใช้สองชุด: ปกติใน /system/lib
(การเรียกใช้ make ครั้งแรก) และ ASan-instrumented ใน /data/asan/lib
(การเรียกใช้ครั้งที่สอง) ไฟล์เรียกทำงานจากบิลด์ที่สองจะเขียนทับไฟล์เหล่านั้นจากบิลด์แรก ไฟล์เรียกทำงานที่ใช้เครื่องมือ ASan รับพาธการค้นหาไลบรารีอื่นที่มี /data/asan/lib
ก่อน /system/lib
ผ่านการใช้ /system/bin/linker_asan
ใน PT_INTERP
ระบบบิลด์ปิดบังไดเรกทอรีอ็อบเจ็กต์ระดับกลางเมื่อค่า $SANITIZE_TARGET
เปลี่ยนไป สิ่งนี้บังคับให้สร้างเป้าหมายใหม่ทั้งหมดในขณะที่รักษาไบนารีที่ติดตั้งไว้ภายใต้ /system/lib
ไม่สามารถสร้างเป้าหมายบางอย่างด้วย ASan:
- ไฟล์ปฏิบัติการที่เชื่อมโยงแบบสถิต
-
LOCAL_CLANG:=false
-
LOCAL_SANITIZE:=false
ไม่ใช่ AS สำหรับSANITIZE_TARGET=address
โปรแกรมเรียกทำงานเช่นนี้ถูกข้ามไปใน SANITIZE_TARGET
ด์ และเวอร์ชันจากการเรียกใช้ make แรกจะเหลืออยู่ใน /system/bin
ห้องสมุดเช่นนี้สร้างขึ้นโดยไม่มี ASan พวกเขาสามารถมีรหัส ASan บางส่วนจากไลบรารีสแตติกที่ขึ้นอยู่กับ