ความสมบูรณ์ของโฟลว์การควบคุมในเคอร์เนล

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 ไม่ส่งผลกระทบต่ออุปกรณ์