Control Flow Integrity (CFI) เป็นกลไกการรักษาความปลอดภัยที่ไม่อนุญาตให้มีการเปลี่ยนแปลงกราฟการควบคุมการไหลเดิมของไบนารีที่คอมไพล์แล้ว ซึ่งทำให้การโจมตีดังกล่าวทำได้ยากขึ้นอย่างมาก
ตั้งแต่ Android 9 เป็นต้นไป คุณสามารถเปิดใช้ CFI ในเคอร์เนลได้
เคอร์เนลของ Linux มีการใช้งาน CFI 2 แบบดังนี้
- สำหรับ Linux 6.0 และต่ำกว่า ให้ใช้ Clang CFI ซึ่งอาศัย Clang LTO
- สำหรับ Linux 6.1 และสูงกว่า ให้ใช้ Clang KCFI
Clang CFI ต้องคอมไพล์ด้วย Link Time Optimization (LTO) LTO จะเก็บการแสดงบิตโค้ด LLVM ของไฟล์ออบเจ็กต์ไว้จนกว่าจะถึงเวลาลิงก์ ซึ่งช่วยให้คอมไพเลอร์พิจารณาการเพิ่มประสิทธิภาพที่ทำได้ดีขึ้น ในการทดสอบบน Android การใช้ LTO ร่วมกับ CFI ทำให้ขนาดโค้ดและประสิทธิภาพเพิ่มขึ้นเพียงเล็กน้อย อย่างไรก็ตาม การเปิดใช้ LTO จะเพิ่มเวลาบิลด์เคอร์เนลอย่างมาก
Clang KCFI ไม่ต้องใช้ LTO ดังนั้นเคอร์เนลของ Android เวอร์ชันล่าสุดจึงได้รับประโยชน์จาก CFI โดยไม่มีค่าใช้จ่ายเพิ่มเติมในการสร้าง LTO
การใช้งาน
CFI ควบคุมโดยตัวเลือก CONFIG_CFI_CLANG ซึ่งเปิดใช้ Clang CFI หรือ Clang KCFI
ดูรายละเอียดทางเทคนิคเพิ่มเติมเกี่ยวกับ CFI และวิธีจัดการการตรวจสอบการควบคุมไปข้างหน้าอื่นๆ ได้ที่เอกสารประกอบการออกแบบ LLVM โดยในเอกสารดังกล่าวจะเรียก KCFI ว่า -fsanitize=kcfi
การแก้ปัญหา
หลังจากเปิดใช้แล้ว ให้แก้ไขข้อผิดพลาดประเภทไม่ตรงกันที่อาจเกิดขึ้นกับไดรเวอร์ การเรียกใช้ฟังก์ชันทางอ้อมผ่านตัวชี้ฟังก์ชันที่ไม่เข้ากันจะทำให้ CFI ทำงาน เมื่อตรวจพบความล้มเหลวของ CFI เคอร์เนลจะพิมพ์คำเตือนซึ่งรวมถึงฟังก์ชันที่เรียกใช้และ Stacktrace ที่ทำให้เกิดความล้มเหลว แก้ไขปัญหานี้โดยตรวจสอบว่าตัวชี้ฟังก์ชันมีประเภทเดียวกับฟังก์ชันที่เรียกใช้เสมอ
หากต้องการช่วยในการแก้ไขข้อบกพร่องของความล้มเหลวของ CFI ให้เปิดใช้ CONFIG_CFI_PERMISSIVE ซึ่งจะพิมพ์คำเตือนแทนที่จะทำให้เกิดเคอร์เนลแพนิก ห้ามใช้โหมดอนุญาตในเวอร์ชันที่ใช้งานจริง
การตรวจสอบ
ปัจจุบันยังไม่มีการทดสอบ CTS สำหรับ CFI โดยเฉพาะ แต่ให้ตรวจสอบว่าการทดสอบ CTS ผ่านทั้งที่เปิดใช้และไม่ได้เปิดใช้ CFI เพื่อยืนยันว่า CFI ไม่ส่งผลกระทบต่ออุปกรณ์