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 }
ให้กับกฎการ build สำหรับการเรียกทำงาน คุณสามารถค้นหาโค้ดเพื่อดูตัวอย่างที่มีอยู่หรือค้นหาน้ำยาฆ่าเชื้ออื่นๆ ที่มีจำหน่าย
เมื่อตรวจพบจุดบกพร่อง 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
.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 อยู่ในทุกแอปในระบบ (อันที่จริงคือในทุกลูกหลานของกระบวนการ 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
หากพบข้อบกพร่อง แอปจะขัดข้อง และรายงานจะถูกพิมพ์ลงในบันทึก
ฆ่าเชื้อ_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 ใน /data/asan/lib
(การเรียกใช้ make ครั้งที่สอง) ไฟล์ปฏิบัติการจากบิลด์ที่สองจะเขียนทับสิ่งเหล่านั้นจากบิลด์แรก โปรแกรมปฏิบัติการที่ใช้เครื่องมือ ASan จะได้รับพาธการค้นหาไลบรารีอื่นที่รวม /data/asan/lib
ก่อน /system/lib
ผ่านการใช้ /system/bin/linker_asan
ใน PT_INTERP
ระบบ build จะปิดกั้นไดเร็กทอรีอ็อบเจ็กต์ระดับกลางเมื่อค่า $SANITIZE_TARGET
มีการเปลี่ยนแปลง สิ่งนี้บังคับให้สร้างเป้าหมายทั้งหมดใหม่ในขณะที่รักษาไบนารีที่ติดตั้งไว้ภายใต้ /system/lib
เป้าหมายบางอย่างไม่สามารถสร้างด้วย ASan:
- โปรแกรมปฏิบัติการที่เชื่อมโยงแบบคงที่
-
LOCAL_CLANG:=false
-
LOCAL_SANITIZE:=false
ไม่ใช่ ASan สำหรับSANITIZE_TARGET=address
การดำเนินการเช่นนี้จะถูกข้ามไปในบิลด์ SANITIZE_TARGET
และเวอร์ชันจากการเรียกใช้ make ครั้งแรกจะเหลืออยู่ใน /system/bin
ห้องสมุดประเภทนี้สร้างขึ้นโดยไม่มี ASan พวกเขาสามารถมีรหัส ASan บางส่วนจากไลบรารีแบบคงที่ที่พวกเขาต้องพึ่งพา