سكودو

‫Scudo هو أداة توزيع ذاكرة ديناميكية في وضع المستخدم، أو أداة توزيع المكبّر، وهي مصمّمة لتكون مقاومة للثغرات الأمنية المرتبطة بالمكبّر (مثل overflow المركم المستنِد إلى المكبّر والاستخدام بعد التحرير والتحرير المزدوج) مع الحفاظ على الأداء. وتوفّر هذه البنية الأساسية عناصر تخصيص ملف برمجي و إلغاء تخصيصه في C العادية (مثل malloc وfree)، بالإضافة إلى عناصر C++ الأساسية (مثل new وdelete).

إنّ Scudo هو أداة تخفيف للمخاطر أكثر من أن تكون أداة رصد فعّالة لأخطاء الذاكرة، مثل AddressSanitizer (ASan).

منذ إصدار Android 11، يتم استخدام scudo لجميع الرموز البرمجية الأصلية (باستثناء الأجهزة ذات الذاكرة المنخفضة، حيث لا يزال يتم استخدام jemalloc). في وقت التشغيل، تعالج أداة Scudo جميع عمليات تخصيص وتدمير ملف heap الأصلي لجميع الملفات التنفيذية وملفات مكتبة التابعة لها، ويتم إيقاف العملية إذا تم رصد تعذُّر أو سلوك مريب في ملف heap.

يعتبر Scudo برنامجًا مفتوحًا المصدر وجزءًا من مشروع التجميع والتجميع (LLVM). تتوفّر المستندات على الرابط https://llvm.org/docs/ScudoHardenedAllocator.html. كان وقت تشغيل Scudo جزءًا من سلسلة أدوات Android وتمت إضافة الدعم إلى تطبيق Soong and 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 عند بلوغ RSS الخاص بالعملية هذا الحدّ، تنتهي العملية.
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: متبوعًا بعبارة ملخّص موجز للمشكلة بالإضافة إلى أي إشارات.

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

  • 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. إذا كنت تصحح أخطاء عطل في أحد التطبيقات، من الممكن استخدام إصدار تطبيق HWASan أيضًا.