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 มาพร้อมกับ เป็นส่วนหนึ่งของ Toolchain ของ 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 | เปิดใช้เนื้อหาแบบไม่มีการแบ่งกลุ่มเมื่อจัดสรรและยกเลิกการจัดสรร | 
| 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 สำหรับไบนารีที่กำหนดเพื่อยืนยันว่าไม่ส่งผลกระทบต่ออุปกรณ์
การแก้ปัญหา
หากตรวจพบปัญหาที่กู้คืนไม่ได้ ตัวจัดสรรจะ
แสดงข้อความแสดงข้อผิดพลาดไปยังตัวอธิบายข้อผิดพลาดมาตรฐาน แล้วสิ้นสุดกระบวนการ
ระบบจะเพิ่ม Stack Trace ที่ทําให้เกิดการสิ้นสุดลงในบันทึกของระบบ
โดยปกติแล้ว เอาต์พุตจะเริ่มต้นด้วย Scudo ERROR: ตามด้วย
สรุปสั้นๆ ของปัญหาพร้อมคำแนะนำ
ต่อไปนี้คือรายการข้อความแสดงข้อผิดพลาดในปัจจุบันและสาเหตุที่อาจเกิดขึ้น
- corrupted chunk header: การยืนยัน checksum ของ ส่วนหัวของก้อนข้อมูลไม่สำเร็จ ซึ่งอาจเกิดจาก 2 สาเหตุ ได้แก่ ส่วนหัวถูกเขียนทับ (บางส่วนหรือทั้งหมด) หรือพอยน์เตอร์ที่ส่งไปยังฟังก์ชันไม่ใช่ก้อนข้อมูล
- race on chunk header: 2 เธรดที่แตกต่างกันพยายาม แก้ไขส่วนหัวเดียวกันในเวลาเดียวกัน โดยปกติแล้วอาการนี้บ่งบอกถึง การแข่งขันหรือการขาดการล็อกทั่วไปเมื่อดำเนินการกับ ก้อนข้อมูลนั้น
- invalid chunk state: Chunk ไม่อยู่ในสถานะที่คาดไว้สำหรับการดำเนินการที่กำหนด เช่น ไม่ได้จัดสรรเมื่อพยายามปล่อย หรือไม่ได้กักกันเมื่อพยายามรีไซเคิล สาเหตุทั่วไปของข้อผิดพลาดนี้คือการเรียกใช้ฟังก์ชัน free() สองครั้ง
- misaligned pointer: ข้อกำหนดการจัดแนวพื้นฐาน จะบังคับใช้อย่างเคร่งครัด: 8 ไบต์ในแพลตฟอร์ม 32 บิต และ 16 ไบต์ในแพลตฟอร์ม 64 บิต หากพอยน์เตอร์ที่ส่งไปยังฟังก์ชันของเราไม่ตรงกับพอยน์เตอร์เหล่านั้น แสดงว่าพอยน์เตอร์ที่ส่งไปยังฟังก์ชันใดฟังก์ชันหนึ่ง ไม่ตรงกัน
- allocation type mismatch: เมื่อเปิดใช้ตัวเลือกนี้ ฟังก์ชันการยกเลิกการจัดสรรที่เรียกใช้ในก้อนข้อมูลต้อง ตรงกับประเภทของฟังก์ชันที่เรียกใช้เพื่อจัดสรร การไม่ตรงกันประเภทนี้อาจทำให้เกิดปัญหาด้านความปลอดภัย
- invalid sized delete: เมื่อใช้ตัวดำเนินการลบที่มีขนาด C++14 และเปิดใช้การตรวจสอบที่ไม่บังคับ จะมีขนาดที่ไม่ตรงกันระหว่างขนาดที่ส่งเมื่อยกเลิกการจัดสรรก้อนข้อมูลกับขนาดที่ขอเมื่อจัดสรร โดยปกติแล้ว ปัญหานี้เกิดจากปัญหาของคอมไพเลอร์หรือการสับสนประเภท ในออบเจ็กต์ที่ถูกยกเลิกการจัดสรร
- RSS limit exhausted: เกิน RSS สูงสุดที่ระบุไว้ (ไม่บังคับ)
หากกำลังแก้ไขข้อบกพร่องของข้อขัดข้องในระบบปฏิบัติการ คุณสามารถใช้บิลด์ OS ของ HWASan ได้ หากคุณกำลัง แก้ไขข้อบกพร่องของแอปที่ขัดข้อง คุณก็ใช้บิลด์แอป HWASan ได้เช่นกัน
