ที่อยู่Sanitizer

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 บางส่วนจากไลบรารีสแตติกที่ขึ้นอยู่กับ

เอกสารสนับสนุน