Scudo เป็นตัวจัดสรรหน่วยความจำแบบไดนามิกหรือตัวจัดสรร heap ในโหมดผู้ใช้ ซึ่งออกแบบมาให้ทนต่อช่องโหว่ที่เกี่ยวข้องกับฮีป (เช่น บัฟเฟอร์ที่มีฮีปเกิน ใช้หลังจากช่วงเวลาว่าง และการเพิ่มพื้นที่ว่างเป็น 2 เท่า) ขณะเดียวกันก็ยังคงรักษาประสิทธิภาพการทำงานไว้ได้ โดยจะมีฟังก์ชันพื้นฐานสำหรับการจองและการยกเลิกการจองหน่วยความจำของ C (เช่น malloc และ free) รวมถึงฟังก์ชันพื้นฐานของ C++ (เช่น new และ delete)
Scudo เป็นการบรรเทามากกว่าเครื่องตรวจหาข้อผิดพลาดด้านหน่วยความจำที่สมบูรณ์แบบอย่าง AddressSanitizer (ASan)
ตั้งแต่ Android 11 เป็นต้นไป ระบบจะใช้ scudo สำหรับโค้ดเนทีฟทั้งหมด (ยกเว้นในอุปกรณ์ที่มีหน่วยความจำต่ำซึ่งยังคงใช้ jemalloc) ขณะรันไทม์ Scudo จะจัดการการจัดสรรและการจัดสรรใหม่ของฮีปแบบเนทีฟทั้งหมดสําหรับไฟล์ปฏิบัติการและไลบรารีที่เกี่ยวข้องทั้งหมด และกระบวนการจะหยุดลงหากตรวจพบความเสียหายหรือลักษณะการทำงานที่น่าสงสัยในฮีป
Scudo เป็น โครงการโอเพนซอร์สและเป็นส่วนหนึ่งของโปรเจ็กต์ compiler-rt ของ LLVM ดูเอกสารประกอบได้ที่ https://llvm.org/docs/ScudoHardenedAllocator.html รันไทม์ Scudo จะมาพร้อมกับชุดเครื่องมือของ Android และมีการเพิ่มการรองรับใน Soong และ Make เพื่อให้เปิดใช้ตัวจัดสรรในไบนารีได้ง่าย
คุณสามารถเปิดหรือปิดใช้การบรรเทาเพิ่มเติมภายในตัวจัดสรรได้โดยใช้ตัวเลือกที่อธิบายไว้ด้านล่าง
การปรับแต่ง
คุณสามารถกำหนดพารามิเตอร์บางรายการของตัวจัดสรรได้แบบทีละกระบวนการผ่านหลายวิธี ดังนี้
- แบบคงที่: กำหนดฟังก์ชัน
__scudo_default_options
ในโปรแกรมที่จะแสดงผลสตริงตัวเลือกที่จะแยกวิเคราะห์ ฟังก์ชันนี้ต้องมีโปรโตไทป์ต่อไปนี้extern "C" const char *__scudo_default_options()
- แบบไดนามิก: ใช้ตัวแปรสภาพแวดล้อม
SCUDO_OPTIONS
ที่มีสตริงตัวเลือกที่จะแยกวิเคราะห์ ตัวเลือกที่กําหนดด้วยวิธีนี้จะลบล้างคําจํากัดความที่กําหนดผ่าน__scudo_default_options
โดยมีตัวเลือกต่อไปนี้
ตัวเลือก | ค่าเริ่มต้น 64 บิต | ค่าเริ่มต้น 32 บิต | คำอธิบาย |
---|---|---|---|
QuarantineSizeKb |
256 |
64 |
ขนาด (หน่วยเป็น KB) ของเขตกักบริเวณที่ใช้เพื่อหน่วงเวลาดีลตำแหน่งจริงของกลุ่ม ค่าที่ต่ำลงอาจลดการใช้หน่วยความจํา แต่ประสิทธิภาพของการบรรเทาก็ลดลงด้วย ค่าลบจะกลับไปใช้ค่าเริ่มต้น การตั้งค่าทั้งค่านี้และ ThreadLocalQuarantineSizeKb เป็น 0 จะปิดใช้การกักเก็บโดยสมบูรณ์ |
QuarantineChunksUpToSize |
2048 |
512 |
ขนาด (เป็นไบต์) สูงสุดของข้อมูลที่จะแยกออกได้ |
ThreadLocalQuarantineSizeKb |
64 |
16 |
ขนาด (เป็น KB) ของการใช้แคชต่อเธรดเพื่อลดภาระการกักเก็บทั่วโลก
ค่าที่ต่ำกว่าอาจลดการใช้หน่วยความจำแต่อาจทำให้มีการแข่งขันกันในเขตกักบริเวณทั่วโลกมากขึ้น การตั้งค่าทั้งนี้และ QuarantineSizeKb เป็น 0 จะปิดใช้การกักเก็บโดยสมบูรณ์ |
DeallocationTypeMismatch |
false |
false |
เปิดใช้การรายงานข้อผิดพลาดเกี่ยวกับ malloc/delete, new/free, new/delete[] |
DeleteSizeMismatch |
true |
true |
เปิดใช้การรายงานข้อผิดพลาดเมื่อขนาดใหม่และขนาดที่ลบไม่ตรงกัน |
ZeroContents |
false |
false |
เปิดใช้เนื้อหากลุ่ม 0 ในการจองและการยกเลิกการจอง |
allocator_may_return_null |
false |
false |
ระบุว่าตัวจัดสรรสามารถแสดงผลเป็นค่า Null ได้เมื่อเกิดข้อผิดพลาดที่กู้คืนได้ แทนที่จะสิ้นสุดกระบวนการ |
hard_rss_limit_mb |
0 |
0 |
เมื่อ RSS ของกระบวนการถึงขีดจำกัดนี้ กระบวนการก็จะสิ้นสุดลง |
soft_rss_limit_mb |
0 |
0 |
เมื่อ RSS ของกระบวนการถึงขีดจำกัดนี้ การจัดสรรเพิ่มเติมจะไม่สำเร็จหรือแสดงผล null (ขึ้นอยู่กับค่า allocator_may_return_null ) จนกว่า RSS จะย้อนกลับเพื่ออนุญาตการจัดสรรใหม่ |
allocator_release_to_os_interval_ms |
ไม่มี | 5000 |
มีผลต่อตัวจัดสรรแบบ 64 บิตเท่านั้น หากตั้งค่าไว้ ระบบจะพยายามปล่อยหน่วยความจำที่ไม่ได้ใช้ให้กับระบบปฏิบัติการ แต่ไม่เกินช่วงเวลานี้ (เป็นมิลลิวินาที) หากค่าเป็นลบ ระบบจะไม่คืนหน่วยความจำให้กับระบบปฏิบัติการ |
abort_on_error |
true |
true |
หากตั้งค่าไว้ เครื่องมือจะเรียกใช้ abort() แทน _exit() หลังจากพิมพ์ข้อความแสดงข้อผิดพลาด |
การตรวจสอบความถูกต้อง
ปัจจุบันยังไม่มีการทดสอบ CTS สำหรับ Scudo โดยเฉพาะ แต่ให้ตรวจสอบว่าไบนารีหนึ่งๆ ผ่านการทดสอบ CTS ไม่ว่าจะเปิดใช้ Scudo หรือไม่ เพื่อยืนยันว่าไม่ส่งผลต่ออุปกรณ์
การแก้ปัญหา
หากตรวจพบปัญหาที่กู้คืนไม่ได้ ผู้จัดสรรจะแสดงข้อความแสดงข้อผิดพลาดต่อตัวบ่งชี้ข้อผิดพลาดมาตรฐาน จากนั้นจะสิ้นสุดกระบวนการ
ระบบจะเพิ่มสแต็กเทรซที่นำไปสู่การสิ้นสุดการใช้งานลงในบันทึกของระบบ
โดยปกติเอาต์พุตจะขึ้นต้นด้วย Scudo ERROR:
ตามด้วยสรุปสั้นๆ ของปัญหาพร้อมคำแนะนำ
ต่อไปนี้คือรายการข้อความแสดงข้อผิดพลาดในปัจจุบันและสาเหตุที่เป็นไปได้
corrupted chunk header
: การตรวจสอบ checksum ของส่วนหัวของข้อมูลโค้ดไม่สำเร็จ ปัญหานี้อาจเกิดจากสาเหตุอย่างใดอย่างหนึ่งต่อไปนี้ มีการเขียนทับส่วนหัว (บางส่วนหรือทั้งหมด) หรือตัวชี้ที่ส่งไปยังฟังก์ชันไม่ใช่กลุ่มrace on chunk header
: มี 2 เทรดพยายามที่จะดัดแปลงส่วนหัวเดียวกันในเวลาเดียวกัน โดยทั่วไปแล้ว อาการนี้แสดงถึงเงื่อนไขการแข่งขันหรือการล็อกทั่วไปไม่เพียงพอเมื่อดำเนินการกับข้อมูลดังกล่าวinvalid chunk state
: ชิ้นส่วนไม่อยู่ในสถานะที่คาดไว้สำหรับการดำเนินการหนึ่งๆ เช่น ไม่ได้รับการจัดสรรเมื่อพยายามทำให้ว่าง หรือไม่ได้ถูกกักบริเวณเมื่อพยายามรีไซเคิล การแจกฟรีเป็นสองเท่าคือ สาเหตุทั่วไปของข้อผิดพลาดนี้misaligned pointer
: ระบบจะบังคับใช้ข้อกำหนดการจัดแนวพื้นฐานอย่างเข้มงวด โดยกำหนดให้มีขนาด 8 ไบต์บนแพลตฟอร์ม 32 บิตและ 16 ไบต์บนแพลตฟอร์ม 64 บิต หากเคอร์เซอร์ที่ส่งไปยังฟังก์ชันของเราไม่ตรงกับเคอร์เซอร์เหล่านั้น แสดงว่าเคอร์เซอร์ที่ส่งไปยังฟังก์ชันใดฟังก์ชันหนึ่งไม่สอดคล้องกันallocation type mismatch
: เมื่อเปิดใช้ตัวเลือกนี้ ฟังก์ชันการจัดสรรหน่วยความจำใหม่ซึ่งเรียกใช้กับข้อมูลโค้ดต้องตรงกับประเภทของฟังก์ชันที่เรียกใช้เพื่อจัดสรรหน่วยความจำ ความไม่ตรงกันประเภทนี้อาจทำให้เกิดปัญหาด้านความปลอดภัยinvalid sized delete
: เมื่อใช้โอเปอเรเตอร์ลบขนาด C++14 และเปิดใช้การตรวจสอบที่ไม่บังคับ ขนาดที่ส่งเมื่อมีการยกเลิกการจัดสรรข้อมูลในลักษณะเป็นกลุ่มไม่ตรงกับขนาดที่ขอเมื่อจัดสรรข้อมูลในลักษณะเป็นกลุ่ม โดยทั่วไปแล้วปัญหานี้เกิดจากคอมไพเลอร์หรือความสับสนเกี่ยวกับประเภทของออบเจ็กต์ที่กำลังจะยกเลิกการจัดสรรRSS limit exhausted
: เกินจำนวน RSS สูงสุดที่ระบุไว้ (ไม่บังคับ)
หากกำลังแก้ไขข้อบกพร่องของข้อขัดข้องในตัวระบบปฏิบัติการ ให้ใช้บิลด์ของระบบปฏิบัติการ HWASan หากกำลังแก้ไขข้อขัดข้องในแอป คุณอาจใช้บิลด์แอป HWASan ได้ด้วย