Scudo เป็นตัวจัดสรรหน่วยความจำแบบไดนามิกในโหมดผู้ใช้ หรือตัวจัดสรร ฮีป ซึ่งออกแบบ มาให้มีความยืดหยุ่นต่อช่องโหว่ที่เกี่ยวข้องกับฮีป (เช่น บัฟเฟอร์ล้นที่อิงตาม ฮีป การใช้หน่วยความจำหลังจากปล่อยหน่วยความจำ, และ การปล่อยหน่วยความจำซ้ำ) พร้อมกับรักษาประสิทธิภาพไว้ โดยมีฟังก์ชันพื้นฐานสำหรับการจัดสรรและ ปล่อยหน่วยความจำของภาษา 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) ของการกักกันที่ใช้เพื่อหน่วงเวลาการปล่อยหน่วยความจำของ Chunk จริง ค่าที่ต่ำลงอาจลดการใช้หน่วยความจำ แต่จะลดประสิทธิภาพของการลดความเสี่ยง หากค่าเป็นลบ ระบบจะกลับไปใช้ค่าเริ่มต้น การตั้งค่าทั้งตัวเลือกนี้และ ThreadLocalQuarantineSizeKb เป็น 0 จะปิดใช้การกักกันทั้งหมด |
QuarantineChunksUpToSize |
2048 |
512 |
ขนาด (หน่วยเป็นไบต์) สูงสุดที่ Chunk สามารถกักกันได้ |
ThreadLocalQuarantineSizeKb |
64 |
16 |
ขนาด (หน่วยเป็น KB) ของแคชต่อเธรดที่ใช้เพื่อลดการกักกันทั่วโลก
ค่าที่ต่ำลงอาจลดการใช้งานหน่วยความจำ แต่จะเพิ่มการแย่งชิงในการกักกันทั่วโลก การตั้งค่าทั้งตัวเลือกนี้และ QuarantineSizeKb เป็น 0 จะปิดใช้การกักกันทั้งหมด |
DeallocationTypeMismatch |
false |
false |
เปิดใช้การรายงานข้อผิดพลาดใน malloc/delete, new/free, new/delete[] |
DeleteSizeMismatch |
true |
true |
เปิดใช้การรายงานข้อผิดพลาดเมื่อขนาดของ new และ delete ไม่ตรงกัน |
ZeroContents |
false |
false |
เปิดใช้เนื้อหา Chunk เป็น 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 ไม่ส่งผลกระทบต่ออุปกรณ์
การแก้ปัญหา
หากตรวจพบปัญหาที่กู้คืนไม่ได้ ตัวจัดสรรจะแสดงข้อความแสดงข้อผิดพลาดไปยังตัวอธิบายข้อผิดพลาดมาตรฐาน แล้วยกเลิกกระบวนการ
ระบบจะเพิ่ม Stack Trace ที่ทำให้เกิดการยกเลิกในบันทึกของระบบ
โดยปกติแล้ว เอาต์พุตจะเริ่มต้นด้วย Scudo ERROR: ตามด้วยข้อมูลสรุปสั้นๆ ของปัญหาพร้อมกับตัวชี้
ต่อไปนี้เป็นรายการข้อความแสดงข้อผิดพลาดปัจจุบันและสาเหตุที่อาจเกิดขึ้น
corrupted chunk header: การตรวจสอบ Checksum ของส่วนหัว Chunk ล้มเหลว สาเหตุอาจเกิดจาก 2 กรณีต่อไปนี้ ส่วนหัวถูกเขียนทับ (บางส่วนหรือทั้งหมด) หรือ Pointer ที่ส่งไปยังฟังก์ชันไม่ใช่ Chunkrace on chunk header: เธรด 2 รายการพยายามจัดการส่วนหัวเดียวกันในเวลาเดียวกัน โดยปกติแล้ว นี่เป็นอาการของการเกิด Race Condition หรือการล็อกไม่เพียงพอโดยทั่วไปเมื่อดำเนินการกับ Chunk นั้นinvalid chunk state: Chunk ไม่อยู่ในสถานะที่คาดไว้สำหรับการดำเนินการที่กำหนด เช่น ไม่ได้จัดสรรเมื่อพยายามปล่อยหน่วยความจำ หรือไม่ได้กักกันเมื่อพยายามรีไซเคิล การปล่อยหน่วยความจำซ้ำเป็นสาเหตุทั่วไปของข้อผิดพลาดนี้misaligned pointer: เราบังคับใช้ข้อกำหนดการจัดแนวพื้นฐานอย่างเข้มงวด นั่นคือ 8 ไบต์ในแพลตฟอร์ม 32 บิต และ 16 ไบต์ในแพลตฟอร์ม 64 บิต หากตัวชี้ที่ส่งไปยังฟังก์ชันของเราไม่เป็นไปตามข้อกำหนดดังกล่าว ตัวชี้ที่ส่งไปยังฟังก์ชันใดฟังก์ชันหนึ่งจะไม่อยู่ในแนวallocation type mismatch: เมื่อเปิดใช้ตัวเลือกนี้ ฟังก์ชันการปล่อยหน่วยความจำที่เรียกใช้ใน Chunk ต้องตรงกับประเภทของฟังก์ชันที่เรียกใช้เพื่อจัดสรร การไม่ตรงกันประเภทนี้อาจทำให้เกิดปัญหาด้านความปลอดภัยinvalid sized delete: เมื่อใช้ตัวดำเนินการ delete ที่มีขนาดใน C++14 และเปิดใช้การตรวจสอบที่ไม่บังคับ ขนาดที่ส่งเมื่อปล่อยหน่วยความจำของ Chunk จะไม่ตรงกับขนาดที่ขอเมื่อจัดสรร โดยปกติแล้ว นี่เป็นปัญหาของคอมไพเลอร์หรือการ สับสนประเภท ในออบเจ็กต์ที่กำลังปล่อยหน่วยความจำRSS limit exhausted: เกินขีดจำกัด RSS สูงสุดที่ระบุไว้ (ไม่บังคับ)
หากคุณกำลังแก้ไขข้อบกพร่องของข้อขัดข้องในระบบปฏิบัติการเอง คุณสามารถใช้ บิลด์ระบบปฏิบัติการ HWASan ได้ หากคุณกำลังแก้ไขข้อบกพร่องของข้อขัดข้องในแอป คุณสามารถใช้บิลด์แอป HWASan ได้เช่นกัน