ที่อยู่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 } ให้กับกฎการ 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 ไม่สามารถมองเห็นโค้ด 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 บางส่วนจากไลบรารีแบบคงที่ที่พวกเขาต้องพึ่งพา

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