منذ عام 2016، يرتبط حوالي% 86 من جميع الثغرات الأمنية في Android بأمان الذاكرة. يستغل المهاجمون معظم الثغرات الأمنية عن طريق تغيير مجرى التحكّم المعتاد في أحد التطبيقات لتنفيذ أنشطة ضارة عشوائية باستخدام جميع امتيازات التطبيق المُستغَل. سلامة مجرى التحكّم (CFI) هي آلية أمان لا تسمح بإجراء تغييرات على المخطّط البياني الأصلي لمجرى التحكّم في ملف ثنائي مُجمَّع، ما يجعل تنفيذ هذه الهجمات أكثر صعوبة.
في الإصدار 8.1 من نظام التشغيل Android، فعّلنا تنفيذ LLVM لميزة "التحقّق من سلامة الرمز البرمجي" في حِزمة الوسائط. في الإصدار Android 9، فعّلنا CFI في المزيد من المكوّنات وفي النواة أيضًا. يتم تفعيل CFI للنظام تلقائيًا، ولكن عليك تفعيل CFI للنواة.
تتطلّب تقنية CFI في LLVM إجراء عملية الترجمة باستخدام ميزة التحسين في وقت الربط (LTO). تحافظ تقنية LTO على تمثيل LLVM لرمز البتات في ملفات العناصر إلى وقت الربط، ما يسمح للمجمِّع بتحديد التحسينات التي يمكن إجراؤها بشكل أفضل. يؤدي تفعيل LTO إلى تقليل حجم الملف الثنائي النهائي وتحسين الأداء، ولكنّه يزيد من وقت الترجمة. في الاختبارات على Android، يؤدي الجمع بين LTO وCFI إلى زيادة طفيفة في حجم الرمز البرمجي وأدائه، وفي حالات محدودة، يؤدي الجمع بينهما إلى تحسين كلاهما.
لمزيد من التفاصيل الفنية حول CFI وكيفية التعامل مع عمليات التحقّق الأخرى من التحكّم في التنفيذ، يُرجى الاطّلاع على مستندات تصميم LLVM.
الأمثلة والمصدر
يقدّم المُجمِّع تقنية CFI ويضيف أدوات قياس إلى الملف الثنائي أثناء وقت الترجمة. نتيح استخدام CFI في مجموعة أدوات Clang ونظام إنشاء Android في AOSP.
يكون CFI مفعَّلاً تلقائيًا على أجهزة Arm64 لمجموعة المكوّنات في
/platform/build/target/product/cfi-common.mk
.
ويتم تفعيله أيضًا مباشرةً في مجموعة من ملفات makefile/blueprint
لمكوّنات الوسائط، مثل /platform/frameworks/av/media/libmedia/Android.bp
و/platform/frameworks/av/cmds/stagefright/Android.mk
.
تنفيذ ميزة CFI للنظام
يتم تفعيل CFI تلقائيًا في حال استخدام Clang ونظام إنشاء Android. وبما أنّ CFI يساعد في الحفاظ على أمان مستخدمي Android، يجب عدم إيقافه.
في الواقع، ننصحك بشدة بتفعيل ميزة "التكلفة المستهدفة للتفاعل" للمكونات الإضافية. وتشمل العناصر المُقترَحة المثالية الرموز البرمجية الأصلية المميّزة أو الرموز البرمجية الأصلية التي تعالج مدخلات المستخدم غير الموثوق بها. إذا كنت تستخدم clang ونظام إنشاء Android، يمكنك تفعيل CFI في المكوّنات الجديدة عن طريق إضافة بضعة أسطر إلى ملفات makefiles أوملفّات blueprint.
إتاحة CFI في ملفات الإنشاء
لتفعيل CFI في ملف make، مثل /platform/frameworks/av/cmds/stagefright/Android.mk
،
أضِف ما يلي:
LOCAL_SANITIZE := cfi # Optional features LOCAL_SANITIZE_DIAG := cfi LOCAL_SANITIZE_BLACKLIST := cfi_blacklist.txt
LOCAL_SANITIZE
تحدِّد CFI كأداة تطهير أثناء مرحلة الإنشاء.- يؤدي الضغط على
LOCAL_SANITIZE_DIAG
إلى تفعيل وضع التشخيص في CFI. يطبع وضع "التشخيص" معلومات إضافية عن تصحيح الأخطاء في logcat أثناء تعطُّل التطبيقات، ما يُعدّ مفيدًا أثناء تطوير إصداراتك واختبارها. ومع ذلك، احرص على إزالة وضع التشخيص من الإصدارات العلنية. LOCAL_SANITIZE_BLACKLIST
تسمح للمكونات بشكل انتقائي بإيقاف أدوات CFI لوظائف أو ملفات مصدر فردية. يمكنك استخدام القائمة السوداء كحلّ أخير لحلّ أي مشاكل قد تواجه المستخدمين في حال عدم حلّها. لمزيد من التفاصيل، يُرجى الاطّلاع على إيقاف ميزة "التحقّق من التوافق مع الأجهزة".
إتاحة CFI في ملفات المخططات
لتفعيل CFI في ملف مخطّط، مثل /platform/frameworks/av/media/libmedia/Android.bp
،
أضِف ما يلي:
sanitize: { cfi: true, diag: { cfi: true, }, blacklist: "cfi_blacklist.txt", },
تحديد المشاكل وحلّها
في حال تفعيل CFI في المكوّنات الجديدة، قد تواجه بعض المشاكل المتعلّقة بأخطاء عدم تطابق نوع الدالة وأخطاء عدم تطابق نوع رمز التجميع .
تحدث أخطاء عدم تطابق نوع الدالة لأنّ CFI يفرض قيودًا على المكالمات غير المباشرة للانتقال فقط إلى الدوال التي لها النوع الديناميكي نفسه مثل النوع الثابت المستخدَم في المكالمة. تقيِّد CFI عمليات استدعاء الدوالّ الأعضاء الافتراضية وغير الافتراضية للانتقال فقط إلى العناصر التي تشكّل فئة مشتقة من النوع الثابت للعنصر المستخدَم للقيام بالاستدعاء. وهذا يعني أنّه عندما يكون لديك رمز ينتهك أيًا من هذين الافتراضات ، سيتم إيقاف الأدوات التي تضيفها CFI. على سبيل المثال، يعرض أثر التسلسل في ملف المشروعات خطأ SIGABRT ويحتوي logcat على سطر حول سلامة تدفق التحكّم الذي يرصد عدم تطابق.
لحلّ هذه المشكلة، تأكَّد من أنّ الدالة التي يتمّ استدعاؤها لها النوع نفسه الذي تم تحديده statically. في ما يلي مثالان على طلبات الشراء:
- البلوتوث: /c/platform/system/bt/+/532377
- الاتصال القصير المدى (NFC): /c/platform/system/nfc/+/527858
من المشاكل المحتملة الأخرى محاولة تفعيل CFI في الرمز الذي يحتوي على طلبات غير مباشرة للترجمة البرمجية. وبما أنّ رمز التجميع غير مكتوب، يؤدي ذلك إلى عدم تطابق نوع.
لحلّ هذه المشكلة، أنشئ وحدات ترميز أصلية لكل طلب تجميع، وامنح الوحدات ترميز توقيع الدالة نفسه المستخدَم في مؤشر الاستدعاء. يمكن للغلاف بعد ذلك استدعاء رمز التجميع مباشرةً. سيؤدي ذلك إلى حلّ المشكلة لأنّ الأدوات التي تستخدمها تقنية CFI لا تُستخدَم في الفروع المباشرة (لا يمكن إعادة توجيهها أثناء التشغيل، وبالتالي لا تشكل خطرًا على الأمان).
إذا كان هناك عدد كبير جدًا من دوالّ التجميع ولا يمكن إصلاحها كلها، يمكنك أيضًا إدراج جميع الدوالّ التي تحتوي على طلبات غير مباشرة لإنشاء تجميع في القائمة السوداء. لا يُنصح بتنفيذ ذلك لأنّه يؤدي إلى إيقاف عمليات التحقّق من CFI في هذه الدوال، ما يؤدي إلى فتح سطح الهجوم.
إيقاف ميزة "التحقّق من التوافق مع الأجهزة الجوّالة"
لم نرصد أيّ زيادة في وقت التحميل، لذا ليس عليك إيقاف CFI. ومع ذلك، إذا كان هناك تأثير على المستخدم، يمكنك إيقاف CFI selective للوظائف الفردية أو ملفات المصدر من خلال تقديم ملف قائمة سوداء للتنقية في وقت الترجمة. تُوجّه القائمة السوداء المُجمِّع إلى إيقاف أدوات CFI في مواضع محدّدة.
يوفّر نظام إنشاء Android إمكانية استخدام قوائم سوداء لكل مكوّن (تتيح لك اختيار الملفات المصدر أو الدوال الفردية التي لن تتلقّى أدوات CFI الفحص) لكل من Make وSoong. لمزيد من التفاصيل حول تنسيق ملف الادراج في القائمة السوداء، يُرجى الاطّلاع على مستندات Clang في المصدر.
التحقُّق
لا يتوفّر حاليًا اختبار CTS مخصّص لبرنامج CFI. بدلاً من ذلك، تأكَّد من اجتياز اختبارات CTS مع تفعيل CFI أو بدونه للتحقّق من أنّ CFI لا يؤثر في الجهاز.