مطهّر العنوان

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

يكتشف ASan ما يلي:

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

تعمل تقنية ASan على كل من معالجات ARM بسعة 32 بت و64 بت، بالإضافة إلى الأجهزة المزوّدة بمعالجات x86 وx86-64. يبلغ معدل استخدام وحدة المعالجة المركزية (CPU) في ASan حوالي الضعف، ويتراوح حجم الرموز البرمجية بين 50% و2x، إضافةً إلى حجم كبير من الذاكرة (بناءً على أنماط التخصيص، ولكن بترتيب 2x).

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

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

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

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

تنظيف الملفات التنفيذية الفردية باستخدام 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 في جميع التطبيقات على الجهاز في الوقت نفسه، وهو حمل ثقيل، إلا أنّ الجهاز المزوّد بذاكرة وصول عشوائي (RAM) بسعة 2 غيغابايت سيكون قادرًا على معالجة هذه المشكلة.

أضِف LOCAL_SANITIZE:=address إلى قاعدة إصدار app_process في frameworks/base/cmds/app_process. تجاهَل هدف app_process__asan في الملف نفسه في الوقت الحالي (إذا كان لا يزال موجودًا في الوقت الذي تقرأ فيه هذا).

عدِّل القسم 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، وإعادة التشغيل.

استخدام سمة 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 (أول عملية استدعاء) وآلية ASan في /data/asan/lib (الاستدعاء الثاني). تُحلّ الملفات التنفيذية من الإصدار الثاني محلّ الملفات التنفيذية من الإصدار الأول. تحصل ملفات exes التي تم تجهيزها لأداة ASan على مسار بحث مكتبة مختلف يتضمّن /data/asan/lib قبل /system/lib من خلال استخدام /system/bin/linker_asan في PT_INTERP.

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

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

  • الملفات التنفيذية المرتبطة بشكل ثابت
  • LOCAL_CLANG:=false استهداف
  • LOCAL_SANITIZE:=false غير مُدرَج في ASan لـ SANITIZE_TARGET=address

يتم تخطي مثل هذه الملفات التنفيذية في إصدار SANITIZE_TARGET، ويتم ترك النسخة من أول استدعاء في /system/bin.

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

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