آدرس ضد عفونی کننده

Android NDK از Address Sanitizer (همچنین به عنوان ASan شناخته می شود) پشتیبانی می کند که با سطح API 27 (Android O MR 1) شروع می شود.

ASan یک ابزار سریع مبتنی بر کامپایلر برای تشخیص اشکالات حافظه در کدهای بومی است. ASan تشخیص می دهد:

  • سرریز/زیر جریان بافر پشته و پشته
  • استفاده از پشته پس از رایگان
  • پشته استفاده خارج از محدوده
  • دو برابر رایگان / وحشی رایگان

سربار CPU ASan تقریباً 2 برابر است، سربار اندازه کد بین 50٪ تا 2x است، و سربار حافظه بزرگ است (بسته به الگوهای تخصیص شما، اما به ترتیب 2x).

نمونه برنامه

یک برنامه نمونه نحوه پیکربندی یک نوع ساخت برای 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 (سطح API 27)، یک برنامه کاربردی می‌تواند یک اسکریپت Wrap Shell ارائه کند که می‌تواند فرآیند برنامه را بپیچد یا جایگزین کند. این به یک برنامه قابل اشکال زدایی اجازه می دهد تا راه اندازی برنامه خود را سفارشی کند، که استفاده از ASan را در دستگاه های تولیدی امکان پذیر می کند.

  1. android:debuggable به مانیفست برنامه اضافه کنید.
  2. useLegacyPackaging را در فایل build.gradle برنامه خود روی true تنظیم کنید. برای اطلاعات بیشتر به راهنمای اسکریپت wrap shell مراجعه کنید.
  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 می‌توان بازگشایی کند را برای همه ردیابی‌های پشته فعال کرد.