Scudo

‫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.

تتوفّر الخيارات التالية.

Option الإصدار 64 بت التلقائي الإصدار التلقائي بسعة 32 بت الوصف
QuarantineSizeKb 256 64 حجم الحجر الصحي (بالكيلوبايت) المستخدَم لتأخير عملية إلغاء تخصيص الشرائح قد تؤدي القيمة الأقل إلى تقليل استخدام الذاكرة، ولكنّها تؤدي إلى انخفاض فعالية تدابير التخفيف. أمّا القيمة السالبة، فتؤدي إلى الرجوع إلى الإعدادات التلقائية. يؤدي ضبط كلاً من هذين الخيارَين وThreadLocalQuarantineSizeKb على القيمة صفر إلى إيقاف وضع العزل تمامًا.
QuarantineChunksUpToSize 2048 512 الحد الأقصى لحجم المقاطع التي يمكن عزلها (بالبايت)
ThreadLocalQuarantineSizeKb 64 16 حجم ذاكرة التخزين المؤقت لكل سلسلة محادثات (بالكيلوبايت) لنقل بيانات الحجر الصحي العام قد تؤدي القيمة الأقل إلى تقليل استخدام الذاكرة، ولكن قد تزيد من الاحتكار في العزلة الشاملة. يؤدي ضبط كل من هذا الإعداد وQuarantineSizeKb على القيمة صفر إلى إيقاف الحجر الصحي بالكامل.
DeallocationTypeMismatch false false تفعيل الإبلاغ عن الأخطاء في malloc/delete وnew/free وnew/delete[]
DeleteSizeMismatch true true تفعيل إعداد تقارير الأخطاء في حال عدم تطابق أحجام الملفات الجديدة والملفات المُراد حذفها
ZeroContents false false تفعيل محتوى الوحدات غير القابلة للتجزئة عند التخصيص وإلغاء التخصيص
allocator_may_return_null false false تشير إلى أنّه يمكن لعامل التوزيع عرض قيمة فارغة عند حدوث خطأ قابل للاسترداد بدلاً من إنهاء العملية.
hard_rss_limit_mb 0 0 عندما يصل معدل نقل البيانات في العملية إلى هذا الحدّ، تنتهي العملية.
soft_rss_limit_mb 0 0 عندما يصل معدل نقل البيانات في الثانية الخاص بالعملية إلى هذا الحدّ، يتعذّر إجراء المزيد من عمليات التخصيص أو يعود بقيمة null (حسب قيمة allocator_may_return_null)، إلى أن يعود معدل نقل البيانات في الثانية إلى الانخفاض للسماح بعمليات تخصيص جديدة.
allocator_release_to_os_interval_ms لا ينطبق 5000 لا يؤثر إلا في أداة تخصيص 64 بت. في حال ضبطها، يحاول الجهاز تحرير الذاكرة غير المستخدَمة لمنح مساحتها لنظام التشغيل، ولكن ليس بمعدل تكرار أعلى من هذا الفاصل الزمني (بالملي ثانية). إذا كانت القيمة سالبة، لا يتم تحرير الذاكرة لنظام التشغيل.
abort_on_error true true في حال ضبطها، تستدعي الأداة abort() بدلاً من _exit() بعد طباعة رسالة الخطأ.

التحقُّق

لا تتوفّر حاليًا اختبارات CTS مخصّصة لتطبيق Scudo. بدلاً من ذلك، تأكَّد من اجتياز اختبارات CTS مع تفعيل Scudo أو بدونه لبرنامج ثنائي معيّن للتحقّق من عدم تأثُّر الجهاز.

تحديد المشاكل وحلّها

في حال رصد مشكلة لا يمكن حلّها، يعرض الموزّع رسالة خطأ لموصّف الخطأ العادي، ثم يُنهي العملية. تتم إضافة عمليات تتبُّع تسلسل استدعاء الدوال البرمجية التي تؤدي إلى إنهاء العملية في سجلّ النظام. يبدأ الإخراج عادةً برمز Scudo ERROR: متبوعًا ب summary قصير للمشكلة مع أي إشارات.

في ما يلي قائمة برسائل الخطأ الحالية وأسبابها المحتملة:

  • corrupted chunk header: تعذّر التحقّق من صحة المجموع الاختباري لعنوان القطعة. من المحتمل أن يرجع ذلك إلى سبب من السببَين التاليَين: تم استبدال الرأس (جزئيًا أو كليًا)، أو أنّ المؤشر الذي تم تمريره إلى الدالة ليس قطعة.
  • race on chunk header: تحاول سلاسل محادثتَان مختلفتان التلاعب بالعنوان نفسه في الوقت نفسه. يشير ذلك عادةً إلى حالة تداخل أو عدم قفل المحتوى بشكل عام عند تنفيذ العمليات على هذا الجزء.
  • invalid chunk state: ليست الشريحة في الحالة المتوقّعة لمحاولة معيّنة، على سبيل المثال، لم يتم تخصيصها عند محاولة تحريرها، أو لم يتم وضعها في الحجر الصحي عند محاولة إعادة تدويرها. إنّ عملية تحرير الذاكرة مرتين هي السبب المعتاد لظهور هذا الخطأ.
  • misaligned pointer: يتم فرض متطلبات alignment الأساسية بشكلٍ صارم: 8 بايت على الأنظمة الأساسية 32 بت و16 بايت على الأنظمة الأساسية 64 بت. إذا لم يكن المؤشر الذي تم تمريره إلى وظائفنا متوافقًا مع هذه الشروط، يعني ذلك أنّ المؤشر الذي تم تمريره إلى إحدى الوظائف غير مُحاذاة.
  • allocation type mismatch: عند تفعيل هذا الخيار، يجب أن تتطابق دالّة إلغاء التخصيص التي يتم استدعاؤها في قطعة مع نوع الدالة التي تم استدعاؤها لتخصيصها. ويمكن أن يؤدي هذا النوع من عدم المطابقة إلى حدوث مشاكل في الأمان.
  • invalid sized delete: عند استخدام عامل التشغيل delete المزوّد بحجم في C++14 وتفعيل التحقّق الاختياري، يحدث عدم تطابق بين الحجم الذي تم تمريره عند إلغاء تخصيص جزء والحجم الذي تم طلبه عند تخصيصه. وعادةً ما تكون هذه مشكلة في المُجمِّع أو التباس في النوع في الكائن الذي يتمّ إلغاء تخصيصه.
  • RSS limit exhausted: تم تجاوز الحد الأقصى المسموح به لخلاصة RSS التي تم تحديدها اختياريًا.

إذا كنت بصدد تصحيح أخطاء في نظام التشغيل نفسه، يمكنك استخدام إصدار نظام التشغيل HWASan. إذا كنت بصدد debugging a crash in an app، من الممكن استخدام HWASan app build أيضًا.