العنوان

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

يكتشف ASan:

  • تجاوز سعة المخزن المؤقت للمكدس والكومة/تجاوز سعة المخزن المؤقت
  • استخدام الكومة بعد الحرة
  • استخدام المكدس خارج النطاق
  • مزدوج مجاني/وايلد مجاني

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

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

باستخدام الخاصية التفاف

النهج الموضح في القسم السابق يضع 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

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

قم بتشغيل الأوامر التالية في نفس شجرة البناء.

make -j42
SANITIZE_TARGET=address make -j42

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

fastboot flash userdata && fastboot flashall

يؤدي هذا إلى إنشاء مجموعتين من المكتبات المشتركة: مكتبات عادية في /system/lib (المكتبة الأولى لإجراء الاستدعاء)، ومكتبة ASan-instrumented في /data/asan/lib (المكتبة الثانية لإجراء الاستدعاء). تحل الملفات التنفيذية من الإصدار الثاني محل تلك الموجودة في الإصدار الأول. تحصل الملفات التنفيذية المزودة بأدوات 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 من المكتبات الثابتة التي تعتمد عليها.

الوثائق الداعمة