AddressSanitizer (ASan) เป็นเครื่องมือที่ใช้คอมไพเลอร์ที่รวดเร็วสำหรับตรวจหา ข้อบกพร่องด้านหน่วยความจำในโค้ดเนทีฟ
ASan ตรวจพบ
- บัฟเฟอร์ล้น/ขาดในสแต็กและฮีป
- การใช้ฮีปหลังจากปล่อย
- การใช้สแต็กนอกขอบเขต
- ดับเบิลฟรี/ไวลด์ฟรี
ASan ทำงานได้ทั้งใน ARM 32 บิตและ 64 บิต รวมถึง x86 และ x86-64 ค่าใช้จ่ายของ CPU ของ ASan จะอยู่ที่ประมาณ 2 เท่า ค่าใช้จ่ายของขนาดโค้ดจะอยู่ระหว่าง 50% ถึง 2 เท่า และค่าใช้จ่ายของหน่วยความจำจำนวนมาก (ขึ้นอยู่กับรูปแบบการจัดสรรของคุณ แต่จะอยู่ที่ประมาณ 2 เท่า)
Android 10 และสาขาการเผยแพร่ล่าสุดของ AOSP บน AArch64 รองรับ Hardware-assisted 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 โปรดดู Address Sanitizer แทน
ล้างข้อมูลที่เรียกใช้งานได้แต่ละรายการด้วย 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 ใช้ตัวยกเลิกการเรียกใช้ที่รวดเร็วซึ่งอิงตามตัวชี้เฟรมเพื่อบันทึก Stack Trace สำหรับเหตุการณ์การจัดสรรและการยกเลิกการจัดสรรหน่วยความจำทุกรายการในโปรแกรม 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
แก้ไขส่วน 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
Build, adb sync, fastboot flash boot และ reboot
ใช้พร็อพเพอร์ตี้ 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
หากพบข้อบกพร่อง แอปจะขัดข้องและพิมพ์รายงานไปยัง บันทึก
SANITIZE_TARGET
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
และจะปล่อยให้เวอร์ชันจากการเรียกใช้ make ครั้งแรกอยู่ใน /system/bin
ไลบรารีเช่นนี้สร้างขึ้นโดยไม่มี ASan โดยอาจมีโค้ด ASan บางส่วนจากไลบรารีแบบคงที่ที่ขึ้นอยู่กับ