AddressSanitizer

‫AddressSanitizer (ASan) هي أداة سريعة مستندة إلى برنامج التجميع لرصد أخطاء الذاكرة في الرموز البرمجية الأصلية.

يرصد ASan ما يلي:

  • فائض سعة المخزن المؤقت أو نقصها في المكدّس والكومة
  • استخدام الذاكرة بعد تحريرها
  • استخدام الحزمة خارج النطاق
  • Double free/wild free

يمكن تشغيل ASan على كل من ARM‏ 32 بت و64 بت، بالإضافة إلى x86 وx86-64. يبلغ الحمل الزائد لوحدة المعالجة المركزية في ASan حوالي ضعف الحمل الزائد لحجم الرمز البرمجي، ويتراوح بين 50% ومرتين، كما أنّ الحمل الزائد للذاكرة كبير (يعتمد على أنماط التخصيص، ولكن بترتيب مضاعف).

يتوافق الإصدار 10 من نظام التشغيل Android وأحدث فرع من إصدار AOSP على AArch64 مع AddressSanitizer المستند إلى الأجهزة (HWASan)، وهي أداة مشابهة ذات حمل زائد أقل على ذاكرة الوصول العشوائي ونطاق أكبر من الأخطاء التي يتم رصدها. ترصد أداة HWASan حالات استخدام الذاكرة المخصّصة للمكدّس بعد إرجاع القيمة، بالإضافة إلى الأخطاء التي ترصدها أداة ASan.

تتضمّن أداة HWASan تكلفة إضافية مشابهة لوحدة المعالجة المركزية وحجم الرمز، ولكنها تتضمّن تكلفة إضافية أقل بكثير لذاكرة الوصول العشوائي (RAM) (بنسبة %15). ‫HWASan غير حتمي. لا تتوفّر سوى 256 قيمة محتملة للعلامة، لذا هناك احتمال ثابت بنسبة %0.4 لعدم رصد أي خطأ. لا يتضمّن HWASan مناطق حمراء محدودة الحجم مثل ASan لرصد حالات تجاوز سعة التخزين المؤقت، ولا يتضمّن أيضًا حجرًا صحيًا محدود السعة لرصد حالات الاستخدام بعد التحرير، لذا لا يهمّ HWASan حجم تجاوز سعة التخزين المؤقت أو المدة التي انقضت منذ إلغاء تخصيص الذاكرة. وهذا يجعل HWASan أفضل من ASan. يمكنك الاطّلاع على مزيد من المعلومات حول تصميم HWASan أو حول استخدام HWASan على Android.

ترصد أداة ASan حالات تجاوز سعة المخزن المؤقت في الذاكرة المكدّسة/العامة بالإضافة إلى حالات تجاوز سعة المخزن المؤقت في الذاكرة المخصّصة، وهي سريعة وتتطلّب الحد الأدنى من الذاكرة.

يوضّح هذا المستند كيفية إنشاء وتشغيل أجزاء من نظام التشغيل Android أو كلّه باستخدام ASan. إذا كنت بصدد إنشاء تطبيق باستخدام حزمة تطوير البرامج (SDK) أو حزمة NDK مع ASan، يُرجى الاطّلاع على Address Sanitizer بدلاً من ذلك.

تنظيف الملفات التنفيذية الفردية باستخدام ASan

أضِف LOCAL_SANITIZE:=address أو sanitize: { address: true } إلى قاعدة الإنشاء الخاصة بالملف التنفيذي. يمكنك البحث في الرمز عن أمثلة حالية أو للعثور على أدوات التعقيم الأخرى المتاحة.

عند رصد خطأ، تطبع ASan تقريرًا مفصّلاً في كل من الناتج العادي وlogcat، ثم توقف العملية.

تنظيف المكتبات المشتركة باستخدام ASan

بسبب طريقة عمل ASan، لا يمكن استخدام مكتبة تم إنشاؤها باستخدام ASan إلا من خلال ملف تنفيذي تم إنشاؤه باستخدام ASan.

لتنظيف مكتبة مشتركة مستخدَمة في ملفات تنفيذية متعددة، ليس جميعها تم إنشاؤه باستخدام ASan، تحتاج إلى نسختَين من المكتبة. الطريقة المقترَحة لإجراء ذلك هي إضافة ما يلي إلى Android.mk للوحدة النمطية المعنيّة:

LOCAL_SANITIZE:=address
LOCAL_MODULE_RELATIVE_PATH := asan

يؤدي ذلك إلى وضع المكتبة في /system/lib/asan بدلاً من /system/lib. بعد ذلك، شغِّل الملف التنفيذي باستخدام الأمر التالي:

LD_LIBRARY_PATH=/system/lib/asan

بالنسبة إلى برامج الخدمة في الخلفية للنظام، أضِف ما يلي إلى القسم المناسب من /init.rc أو /init.$device$.rc.

setenv LD_LIBRARY_PATH /system/lib/asan

تأكَّد من أنّ العملية تستخدم مكتبات من /system/lib/asan عند توفّرها من خلال قراءة /proc/$PID/maps. إذا لم يكن كذلك، قد تحتاج إلى إيقاف SELinux باتّباع الخطوات التالية:

adb root
adb shell setenforce 0
# restart the process with adb shell kill $PID
# if it is a system service, or may be adb shell stop; adb shell start.

تقارير أفضل لتتبُّع تسلسل استدعاء الدوال البرمجية

تستخدم أداة ASan أداة إلغاء ربط سريعة تستند إلى مؤشر الإطار لتسجيل تتبُّع تسلسل استدعاء الدوال البرمجية لكل حدث من أحداث تخصيص الذاكرة وإلغاء تخصيصها في البرنامج. تم إنشاء معظم نظام Android بدون مؤشرات إطارات. ونتيجةً لذلك، غالبًا ما تحصل على لقطة واحدة أو لقطتَين فقط. لإصلاح هذه المشكلة، عليك إعادة إنشاء المكتبة باستخدام ASan (ننصح بذلك) أو باستخدام:

LOCAL_CFLAGS:=-fno-omit-frame-pointer
LOCAL_ARM_MODE:=arm

أو اضبط ASAN_OPTIONS=fast_unwind_on_malloc=0 في بيئة المعالجة. ويمكن أن تكون هذه العملية الأخيرة مكثفة جدًا بالنسبة إلى وحدة المعالجة المركزية، وذلك حسب الحِمل.

الترميز

في البداية، تحتوي تقارير ASan على مراجع إلى الإزاحات في الملفات الثنائية والمكتبات المشتركة. هناك طريقتان للحصول على معلومات الملف المصدر والسطر:

  • تأكَّد من توفُّر الملف الثنائي llvm-symbolizer في /system/bin. تم إنشاء llvm-symbolizer من مصادر في third_party/llvm/tools/llvm-symbolizer.
  • فلترة التقرير من خلال external/compiler-rt/lib/asan/scripts/symbolize.pyالبرنامج النصي

يمكن أن يوفّر الأسلوب الثاني المزيد من البيانات (أي file:line مواقع جغرافية) بسبب توفّر مكتبات رموز على المضيف.

ASan في التطبيقات

لا يمكن لـ ASan الوصول إلى رمز Java، ولكن يمكنه رصد الأخطاء في مكتبات JNI. لإجراء ذلك، عليك إنشاء الملف التنفيذي باستخدام ASan، وهو /system/bin/app_process(32|64) في هذه الحالة. يؤدي ذلك إلى تفعيل ASan في جميع التطبيقات على الجهاز في الوقت نفسه، ما يشكّل حملاً ثقيلاً، ولكن يجب أن يتمكّن الجهاز الذي يحتوي على ذاكرة وصول عشوائي بسعة 2 غيغابايت من التعامل مع ذلك.

أضِف LOCAL_SANITIZE:=address إلى قاعدة الإنشاء app_process في frameworks/base/cmds/app_process.

عدِّل قسم service zygote من ملف system/core/rootdir/init.zygote(32|64).rc المناسب لإضافة الأسطر التالية إلى مجموعة الأسطر التي تتضمّن مسافة بادئة وتحتوي على class main، مع إضافة مسافة بادئة بنفس المقدار:

    setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib
    setenv ASAN_OPTIONS allow_user_segv_handler=true

إنشاء ومزامنة adb وتثبيت fastboot flash boot وإعادة التشغيل

استخدام السمة wrap

يؤدي الأسلوب الموضّح في القسم السابق إلى إدراج ASan في كل تطبيق على النظام (في الواقع، في كل عنصر فرعي من عملية Zygote). يمكن تشغيل تطبيق واحد فقط (أو عدة تطبيقات) باستخدام ASan، مع التضحية ببعض سعة الذاكرة مقابل بدء تشغيل التطبيق بشكل أبطأ.

يمكن إجراء ذلك من خلال بدء تطبيقك باستخدام السمة wrap.. يشغّل المثال التالي تطبيق Gmail ضمن ASan:

adb root
adb shell setenforce 0  # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"

في هذا السياق، تعيد asanwrapper كتابة /system/bin/app_process إلى /system/bin/asan/app_process، التي تم إنشاؤها باستخدام ASan. ويضيف أيضًا /system/lib/asan في بداية مسار البحث في المكتبة الديناميكية. بهذه الطريقة، ستكون المكتبات التي تم تفعيل ASan فيها من /system/lib/asan هي الخيار المفضّل على المكتبات العادية في /system/lib عند التشغيل باستخدام asanwrapper.

في حال العثور على خطأ، سيتعطّل التطبيق وسيتم تسجيل التقرير في السجلّ.

SANITIZE_TARGET

يتضمّن الإصدار 7.0 من نظام التشغيل Android والإصدارات الأحدث إمكانية إنشاء منصة Android بأكملها باستخدام ASan في آن واحد. (إذا كنت بصدد إنشاء إصدار أعلى من Android 9، سيكون HWASan خيارًا أفضل).

نفِّذ الأوامر التالية في شجرة الإصدار نفسها.

make -j42
SANITIZE_TARGET=address make -j42

في هذا الوضع، يحتوي userdata.img على مكتبات إضافية ويجب أيضًا نقلها إلى الجهاز. استخدِم سطر الأوامر التالي:

fastboot flash userdata && fastboot flashall

يؤدي ذلك إلى إنشاء مجموعتَين من المكتبات المشترَكة: المجموعة العادية في /system/lib (أول عملية استدعاء make)، والمجموعة التي تم تفعيل ASan فيها في /data/asan/lib (ثاني عملية استدعاء make). تستبدل الملفات التنفيذية من الإصدار الثاني تلك من الإصدار الأول. تحصل الملفات التنفيذية التي تم تفعيل ASan فيها على مسار بحث مختلف للمكتبة يتضمّن /data/asan/lib قبل /system/lib من خلال استخدام /system/bin/linker_asan في PT_INTERP.

يزيل نظام الإنشاء أدلة الكائنات الوسيطة عندما تتغير قيمة $SANITIZE_TARGET. يفرض ذلك إعادة إنشاء جميع الاستهدافات مع الحفاظ على الملفات الثنائية المثبَّتة ضمن /system/lib.

لا يمكن إنشاء بعض الأهداف باستخدام ASan:

  • الملفات التنفيذية المرتبطة بشكل ثابت
  • LOCAL_CLANG:=false أهداف
  • LOCAL_SANITIZE:=false غير متوافق مع SANITIZE_TARGET=address

يتم تخطّي الملفات التنفيذية المشابهة في الإصدار SANITIZE_TARGET، ويتم الاحتفاظ بالإصدار من عملية الاستدعاء الأولى في /system/bin.

يتم إنشاء مكتبات مثل هذه بدون ASan. ويمكن أن تحتوي على بعض رموز ASan من المكتبات الثابتة التي تعتمد عليها.

المستندات الداعمة