المعقّم للعنوان

يتوافق Android NDK مع Address Sanitizer (المعروف أيضًا باسم ASan) بدءًا من المستوى 27 لواجهة برمجة التطبيقات (الإصدار 1 من Android O).

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

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

يبلغ الحمل الزائد لوحدة المعالجة المركزية في ASan ضِعف الحمل الزائد، ويتراوح الحمل الزائد لحجم الرمز بين 50% وضِعف الحمل الزائد، كما أنّ الحمل الزائد للذاكرة كبير (يعتمد على أنماط التخصيص، ولكنّه يبلغ ضِعف الحمل الزائد).

نموذج التطبيق

يوضّح تطبيق نموذجي كيفية ضبط تنويعة التصميم لتطبيق asan.

إنشاء

لإنشاء الرمز البرمجي الأصلي (JNI) لتطبيقك باستخدام Address Sanitizer، اتّبِع الخطوات التالية:

ndk-build

في ملف Application.mk:

APP_STL := c++_shared # Or system, or none.
APP_CFLAGS := -fsanitize=address -fno-omit-frame-pointer
APP_LDFLAGS := -fsanitize=address

لكل وحدة في ملف Android.mk:

LOCAL_ARM_MODE := arm

CMake

في ملف build.gradle الخاص بالوحدة:

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                // Can also use system or none as ANDROID_STL.
                arguments "-DANDROID_ARM_MODE=arm", "-DANDROID_STL=c++_shared"
            }
        }
    }
}

لكل هدف في ملف CMakeLists.txt:

target_compile_options(${TARGET} PUBLIC -fsanitize=address -fno-omit-frame-pointer)
set_target_properties(${TARGET} PROPERTIES LINK_FLAGS -fsanitize=address)

تشغيل

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

  1. أضِف android:debuggable إلى بيان التطبيق.
  2. اضبط useLegacyPackaging على true في ملف build.gradle الخاص بتطبيقك. اطّلِع على دليل برنامج نصي لتغليف التطبيق للحصول على مزيد من المعلومات.
  3. أضِف مكتبة وقت التشغيل ASan إلى jniLibs لوحدة تطبيقك.
  4. أضِف ملفات wrap.sh تتضمّن المحتوى التالي إلى كل دليل في دليل src/main/resources/lib.

    #!/system/bin/sh
    HERE="$(cd "$(dirname "$0")" && pwd)"
    export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1
    ASAN_LIB=$(ls $HERE/libclang_rt.asan-*-android.so)
    if [ -f "$HERE/libc++_shared.so" ]; then
        # Workaround for https://github.com/android-ndk/ndk/issues/988.
        export LD_PRELOAD="$ASAN_LIB $HERE/libc++_shared.so"
    else
        export LD_PRELOAD="$ASAN_LIB"
    fi
    "$@"
    

بافتراض أنّ اسم وحدة تطبيق مشروعك هو app، يجب أن يتضمّن بنية الدليل النهائي ما يلي:

<project root>
└── app
    └── src
        └── main
            ├── jniLibs
            │   ├── arm64-v8a
            │   │   └── libclang_rt.asan-aarch64-android.so
            │   ├── armeabi-v7a
            │   │   └── libclang_rt.asan-arm-android.so
            │   ├── x86
            │   │   └── libclang_rt.asan-i686-android.so
            │   └── x86_64
            │       └── libclang_rt.asan-x86_64-android.so
            └── resources
                └── lib
                    ├── arm64-v8a
                    │   └── wrap.sh
                    ├── armeabi-v7a
                    │   └── wrap.sh
                    ├── x86
                    │   └── wrap.sh
                    └── x86_64
                        └── wrap.sh

تتبعات بنية تخزين العناصر

يحتاج Address Sanitizer إلى إلغاء تجميع الرموز البرمجية في كل استدعاء malloc/realloc/free. أمامك خياران:

  1. أداة إلغاء تتسم بالسرعة وتستند إلى مؤشر الإطار. يتم استخدام هذا الرمز باتّباع التعليمات الواردة في قسم الإنشاء.

  2. أداة فك ترميز CFI "بطيئة". في هذا الوضع، تستخدم ASan _Unwind_Backtrace. ولا يتطلّب ذلك سوى -funwind-tables، الذي يكون مفعّلاً تلقائيًا في العادة.

يتم استخدام أداة إلغاء التجميع السريع تلقائيًا مع malloc/realloc/free. يتم تلقائيًا استخدام أداة فك التشفير البطيئة في عمليات تتبُّع تسلسل استدعاء الدوال البرمجية التي تؤدي إلى حدوث خطأ فادح. يمكن تفعيل أداة فك التشفير البطيئة لجميع تتبُّعات تسلسل استدعاء الدوال البرمجية من خلال إضافة fast_unwind_on_malloc=0 إلى المتغيّر ASAN_OPTIONS في ملف wrap.sh.