
مُخصِّص ذاكرة مُحصَّن مصمم للأنظمة الحديثة. يتكامل مع مكتبة Bionic libc في Android ويمكن استخدامه خارجيًا مع musl وglibc كمكتبة ديناميكية للاستخدام على منصات Linux الأخرى. وسيكتسب مزيدًا من قابلية النقل والتكامل بمرور الوقت.
هذا مُخصِّص ذاكرة عام الغرض موجَّه نحو الأمان، يوفّر واجهة malloc API إلى جانب امتدادات متنوعة. يوفّر تحصينًا كبيرًا ضد ثغرات فساد الكومة (heap corruption). كما يؤدي التصميم الموجَّه نحو الأمان إلى تقليل كبير في الحمل الزائد للبيانات الوصفية (metadata) وهدر الذاكرة الناتج عن التجزئة (fragmentation) مقارنةً بتصميم مُخصِّص أكثر تقليدية. يهدف إلى تقديم أداء عام جيد مع التركيز على الأداء طويل الأمد واستخدام الذاكرة بدلًا من اختبارات الأداء الدقيقة للمُخصِّص. يوفّر قابلية للتوسع عبر عدد قابل للتهيئة من الساحات (arenas) المستقلة تمامًا، مع تقسيم القفل الداخلي داخل الساحات بشكل إضافي حسب فئة الحجم.
يدعم هذا المشروع حاليًا Bionic (Android) وmusl وglibc. قد يدعم أنظمة تشغيل أخرى غير مبنية على Linux في المستقبل. بالنسبة إلى Android، توجد تكاملات مخصصة وميزات تحصين أخرى مخطط لها أيضًا لـ musl في المستقبل. سيقتصر دعم glibc على استبدال تنفيذ malloc فقط لأن musl قاعدة أكثر متانة ونظافة للبناء عليها ويمكنها تغطية حالات الاستخدام نفسها.
هذا المُخصِّص مخصص كخلفٍ لتنفيذ سابق كان قائمًا على توسيع OpenBSD malloc بميزات أمان إضافية متنوعة. لا يزال يعتمد بشكل كبير على تصميم OpenBSD malloc، وإن لم يكن على الشيفرة الحالية عدا إعادة استخدام تنفيذ جدول التجزئة (hash table). تتمثل الاختلافات الرئيسية في التصميم في أنه يركّز حصريًا على التحصين بدلًا من اكتشاف الأخطاء، ويستخدم فئات حجم أدق إلى جانب أحجام slab تتجاوز 4k لتقليل التجزئة الداخلية، ولا يعتمد على امتلاك النواة لعشوائية mmap دقيقة، ويستهدف فقط معمارية 64-bit للاستفادة المكثفة من فضاء العناوين الكبير. هناك الكثير من الاختلافات الأصغر في نهج التنفيذ. يدمج الامتدادات السابقة التي أُجريت على OpenBSD malloc بما في ذلك إضافة حشو (padding) للتخصيصات من أجل الكناري (canaries) (مختلفة عن كناري OpenBSD malloc الحالية)، واكتشاف الكتابة بعد التحرير (write-after-free) المرتبط بالمسح الموجود عند التحرير، وقوائم انتظار إلى جانب المصفوفات العشوائية الحالية لحجر التخصيصات (quarantining)، واكتشاف سليم للتحرير المزدوج (double-free) للتخصيصات المحجورة. استُلهمت مناطق الذاكرة لكل فئة حجم ذات قواعد عشوائية خاصة بها بشكل غير مباشر من التقسيم القائم على الحجم والنوع في PartitionAlloc. انتهت التغييرات المخطط لها على OpenBSD malloc إلى كونها واسعة النطاق وتدخلية أكثر من اللازم، لذا بدأ هذا المشروع كتنفيذ جديد قادر بشكل أفضل على تحقيق الأهداف. بالنسبة إلى 32-bit، يمكن استخدام نسخة منقولة من OpenBSD malloc مع امتدادات صغيرة بدلًا من ذلك لأن هذا المُخصِّص لا يدعم تلك البيئة جذريًا.
تحدد Debian stable (حاليًا Debian 13) أقدم مجموعة مدعومة من التبعيات:
بالنسبة إلى Android، تُدعم فروع Linux GKI 6.1 و6.6 و6.12.
ومع ذلك، يُوصى بشدة باستخدام إصدارات أحدث. قد تكون الإصدارات الأقدم من التبعيات متوافقة في الوقت الحالي لكنها غير مختبرة ولن تكون مدعومة صراحةً.
لاستبدال malloc الخارجي مع musl، يلزم musl 1.1.20. ومع ذلك، سيكون هناك تكامل مخصص يقدم أداءً أفضل في المستقبل إلى جانب تحصين آخر لتنفيذ مكتبة C القياسية.
بالنسبة إلى Android، سيتم دعم فقط الجيل الحالي، فرع الصيانة قيد التطوير النشط لمشروع
Android مفتوح المصدر، والذي يعني حاليًا android17-release.
يمكن استخدام السكربت preload.sh للاختبار مع الملفات التنفيذية المرتبطة ديناميكيًا
التي تستخدم glibc أو musl:
./preload.sh krita --new-image RGBA,U8,500,500
قد يكون من الضروري زيادة vm.max_map_count في sysctl بشكل كبير
لاستيعاب العدد الكبير من التعيينات الناتجة عن guard slabs ومناطق حماية التخصيص الكبيرة. يمكن أيضًا تقليل عدد التعيينات بشكل
جذري عبر زيادة كبيرة في CONFIG_GUARD_SLABS_INTERVAL لكن
لهذه الميزة تكلفة منخفضة على الأداء واستخدام الذاكرة لذا لا يُنصح بذلك.
يمكن أن يقدم أداءً أفضل قليلًا عند دمجه في مكتبة C القياسية وهناك فرص أخرى لتحصين مماثل داخل مكتبة C القياسية وتنفيذات الرابط الديناميكي. على سبيل المثال، يمكن تنفيذ منطقة مكتبة لتوفير عزل مماثل للمكتبات الديناميكية كما يوفره هذا المخصص عبر فئات الحجم المختلفة. الهدف هو أن يُقدَّم هذا كجزء من المتغيرات المحصّنة من مكتبات Bionic وmusl C القياسية.
تتوفر مجموعة من الاختبارات الآلية البسيطة ويمكن تشغيلها باستخدام أمر make كما يلي:
make test
يلزم OpenSSH 8.1 أو أعلى للسماح لاستدعاءات النظام mprotect PROT_READ|PROT_WRITE
في مرشح seccomp-bpf بدلًا من قتل العملية.
في GrapheneOS، يتم دمج hardened_malloc في مكتبة C القياسية كتنفيذ malloc القياسي. يمكن لأنظمة التشغيل الأخرى المبنية على Android إعادة استخدام كود التكامل لتوفيره. إذا رغبت في ذلك، يمكن ترك jemalloc كخيار إعداد وقت التشغيل عبر استخدام hardened_malloc بشكل شرطي فقط لإعطاء المستخدمين الخيار بين الأداء والأمان. ومع ذلك، فإن هذا يقلل الأمان لنماذج التهديد التي تكون فيها الحالة الدائمة غير موثوقة، أي الإقلاع الموثّق والإثبات (انظر مشروع الإثبات الشقيق).
تأكد من رفع vm.max_map_count بشكل كبير أيضًا لاستيعاب العدد
الكبير جدًا من صفحات الحماية التي ينشئها hardened_malloc. يمكن القيام بذلك في
init.rc (system/core/rootdir/init.rc) بالقرب من إعدادات الذاكرة الافتراضية الأخرى:
write /proc/sys/vm/max_map_count 1048576
هذا غير ضروري إذا قمت بتعيين CONFIG_GUARD_SLABS_INTERVAL إلى قيمة كبيرة جدًا
في إعدادات البناء.
في أنظمة التشغيل التقليدية المبنية على Linux، يمكن دمج hardened_malloc
إما في تنفيذ libc كبديل لتنفيذ malloc القياسي
أو تحميله كمكتبة ديناميكية. بدلًا من إعادة بناء
كل ملف تنفيذي ليُربط به، يمكن إضافته كمكتبة محمّلة مسبقًا
إلى /etc/ld.so.preload. على سبيل المثال، مع تثبيت libhardened_malloc.so
في /usr/local/lib/libhardened_malloc.so، أضف ذلك المسار الكامل كسطر
إلى ملف الإعدادات /etc/ld.so.preload:
/usr/local/lib/libhardened_malloc.so
صيغة ملف الإعدادات هذا هي قائمة مفصولة بمسافات، لذا من الممارسات الجيدة وضع كل مكتبة على سطر منفصل.
للحصول على أقصى توافق يمكن تثبيت libhardened_malloc.so في
/usr/lib/ لتجنب إخفاقات التحميل المسبق الناتجة عن ملفات تعريف AppArmor أو قيود
ExecPaths= في systemd. تحقق من السجلات بالصيغة التالية:
ERROR: ld.so: object '/usr/local/lib/libhardened_malloc.so' from /etc/ld.so.preload cannot be preloaded (failed to map segment from shared object): ignored.
استخدام متغير البيئة LD_PRELOAD لتحميله على أساس كل حالة على حدة
لن يعمل عندما يكون AT_SECURE مضبوطًا كما هو الحال مع ثنائيات setuid. كما أنه
عمومًا ليس نهجًا موصى به للاستخدام في الإنتاج. التوصية
هي تمكينه عالميًا وإجراء استثناءات للحالات الحرجة من حيث الأداء عن طريق
تشغيل التطبيق في حاوية/فضاء أسماء بدون تمكينه.
تأكد أيضًا من رفع vm.max_map_count بشكل كبير لاستيعاب العدد
الكبير جدًا من صفحات الحماية التي ينشئها hardened_malloc. على سبيل المثال، في
/etc/sysctl.d/hardened_malloc.conf:
vm.max_map_count = 1048576
هذا غير ضروري إذا قمت بتعيين CONFIG_GUARD_SLABS_INTERVAL إلى قيمة كبيرة جدًا
في إعدادات البناء.