AddressSanitizer

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

يرصد ASan ما يلي:

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

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

يتيح كلّ من 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 إلا من خلال ملف executible تم إنشاؤه باستخدام 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 بدون مؤشرات الإطارات. ونتيجةً لذلك، غالبًا ما تحصل على إطار واحد أو إطارَين ذيَين معنى فقط. لحلّ هذه المشكلة، يمكنك إعادة إنشاء المكتبة باستخدام IDE IDE (يُنصح به) أو باستخدام:

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

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

الترميز

في البداية، تحتوي تقارير 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 flash وإعادة التشغيل

استخدام سمة 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 في بداية مسار البحث في المكتبة الديناميكية. بهذه الطريقة، يتم تفضيل مكتبات /system/lib/asan التي تم تجهيزها لأداة 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). تُحلّ الملفات التنفيذية من الإصدار الثاني محلّ الملفات التنفيذية من الإصدار الأول. تحصل ملفات 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 من المكتبات الثابتة التي تعتمد عليها.

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