اشکال زدایی خرابی حافظه با استفاده از Address Sanitizer

این سند به شما نشان می دهد که چگونه هنگام استفاده از AGDE ابزارهای ویژه اشکال زدایی را فعال کنید. این ابزارها می توانند به تشخیص خرابی حافظه و خطاهای بازنویسی کمک کنند.

HWAddress Sanitizer و Address Sanitizer

HWAddress Sanitizer (HWASan) و Address Sanitizer (ASan) ابزارهای اشکال زدایی خرابی حافظه هستند که به رفع اشکال خرابی حافظه و خطاهای بازنویسی کمک می کنند، مانند موارد زیر:

  • بافر پشته سرریز و زیر سرریز می شود
  • بافر هیپ سرریز و زیر سرریز می شود
  • استفاده از پشته خارج از محدوده آن
  • خطاهای رایگان و وحشی را دو برابر کنید
  • استفاده از پشته پس از بازگشت (فقط HWASan)

توصیه می‌کنیم HWASan یا ASan را فقط زمانی که مشکلی را اشکال‌زدایی می‌کنید یا به عنوان بخشی از آزمایش خودکار فعال کنید. در حالی که این ابزارها کارآمد هستند، استفاده از آنها جریمه دارد.

رفتار در زمان اجرا

وقتی فعال باشد، هر دو HWASan و ASan به طور خودکار خرابی حافظه را در برنامه شما بررسی می کنند.

اگر خطای حافظه شناسایی شود، برنامه با یک خطای SIGBART (انقطاع سیگنال) خراب می شود و یک پیام دقیق برای logcat چاپ می کند. یک کپی از پیام نیز در فایلی در زیر /data/tombstones نوشته می شود.

پیام خطا شبیه به زیر است:

ERROR: HWAddressSanitizer: tag-mismatch on address 0x0042a0826510 at pc 0x007b24d90a0c
WRITE of size 1 at 0x0042a0826510 tags: 32/3d (ptr/mem) in thread T0
    #0 0x7b24d90a08  (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x2a08)
    #1 0x7b8f1e4ccc  (/apex/com.android.art/lib64/libart.so+0x198ccc)
    #2 0x7b8f1db364  (/apex/com.android.art/lib64/libart.so+0x18f364)
    #3 0x7b8f2ad8d4  (/apex/com.android.art/lib64/libart.so+0x2618d4)

0x0042a0826510 is located 0 bytes to the right of 16-byte region [0x0042a0826500,0x0042a0826510)
allocated here:
    #0 0x7b92a322bc  (/apex/com.android.runtime/lib64/bionic/libclang_rt.hwasan-aarch64-android.so+0x212bc)
    #1 0x7b24d909e0  (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x29e0)
    #2 0x7b8f1e4ccc  (/apex/com.android.art/lib64/libart.so+0x198ccc)

پیش نیازها

الزامات HWASan

برای استفاده از HWASan:

  • باید از AGDE 24.1.99 یا بالاتر استفاده کنید.
  • برنامه باید با استفاده از NDK 26 یا بالاتر ساخته شود.
  • برنامه باید با هدف SDK 34 یا بالاتر ساخته شود.
  • هدف باید یک دستگاه arm64-v8a با Android 14 (سطح API 34) یا بالاتر باشد.

از کتابخانه استاندارد C++ مشترک در پروژه خود استفاده کنید

به دلیل یک مشکل شناخته شده، ASan هنگام استفاده از libc++_static با مدیریت استثناهای C++ ناسازگار است. این مشکل هنگام استفاده از libc++_shared دیده نمی شود.

HWASan اجرای خود را از اپراتورهای new و delete دارد، که اگر کتابخانه استاندارد به صورت ایستا به پروژه مرتبط باشد، قابل استفاده نیست.

برای تغییر این تنظیم، به بخش پیوند دادن کتابخانه استاندارد C++ این سند مراجعه کنید.

فعال کردن Frame Pointer

HWASan و ASan از یک بازکردن سریع مبتنی بر اشاره گر فریم برای تولید اطلاعات ردیابی پشته برای رویدادهای تخصیص حافظه و توزیع استفاده می کنند. این بدان معناست که برای استفاده از این ویژگی ها باید در تنظیمات کامپایلر C++ خود، تولید اشاره گر فریم را فعال کنید. یعنی باید بهینه سازی حذف نشانگر فریم را غیرفعال کنید.

برای تغییر این تنظیم، بخش Enabling Frame Pointer Generation در این سند را ببینید.

پیکربندی پروژه ویژوال استودیو برای استفاده از HWASan یا ASan

فعال کردن HWASan یا ASan

برای فعال کردن HWASan یا ASan، به Configuration Properties > General در Property Pages برای پروژه خود بروید.

منوی ویژگی های Visual Studio Solution Explorer برای فعلی پروژه

شکل 1 : گزینه Properties پروژه در پنجره Visual Studio Solution Explorer.

گفتگوی Property Pages با مشخصات عمومی نشان داده شده و آدرس تنظیمات ضد عفونی کننده برجسته شده است.

شکل 2 : تنظیم Address Sanitizer (ASan) در خصوصیات کلی پروژه.

برای فعال کردن HWASan برای پروژه خود، تنظیم Address Sanitizer (ASan) را به Hardware ASan Enabled (fsanitize=hwaddress) تغییر دهید.

برای فعال کردن ASan برای پروژه خود، تنظیم Address Sanitizer (ASan) را به ASan Enabled (fsanitize=address) تغییر دهید.

فعال کردن Frame Pointer

تولید نشانگر فریم توسط تنظیمات کامپایلر Omit Frame Pointer C/C++ کنترل می‌شود و می‌توانید آن را در Property Pages پروژه خود در قسمت Configuration Properties > C/C++ > Optimization پیدا کنید.

گفتگوی Property Pages با ویژگی های C/C++ Optimization نشان داده شده است، و تنظیمات Frame Pointer را حذف کنید برجسته شده است.

شکل 3 : از کجا می توان تنظیمات Omit Frame Pointer را پیدا کرد.

هنگام استفاده از HWASan یا ASan، تنظیمات Omit Frame Pointer را روی No (-fno-omit-frame-pointer) قرار دهید.

پیوند دادن کتابخانه استاندارد C++ در حالت کتابخانه مشترک

تنظیمات حالت پیونددهنده برای کتابخانه استاندارد C++ را می‌توانید در صفحات ویژگی پروژه خود در قسمت خصوصیات پیکربندی > عمومی ، در بخش پیش‌فرض‌های پروژه پیدا کنید.

گفتگوی Property Pages با دسته بندی عمومی انتخاب شده و استفاده از تنظیمات STL برجسته شده است.

شکل 4 : محل پیدا کردن تنظیمات حالت پیوند دهنده برای کتابخانه استاندارد C++.

هنگام استفاده از HWASan یا ASan، Use of STL را روی Use C++ Standard Libraries (.so) تنظیم کنید. این مقدار کتابخانه استاندارد C++ را به عنوان یک کتابخانه مشترک به پروژه شما پیوند می دهد که برای عملکرد صحیح HWASan و ASan لازم است.

ایجاد یک پیکربندی Build برای استفاده از Address Sanitizer

اگر ترجیح می دهید از HWASan یا ASan به صورت موقت استفاده کنید، ممکن است نخواهید یک پیکربندی ساخت جدید را صرفاً برای استفاده آنها ایجاد کنید. اگر پروژه شما کوچک است، در حال کاوش در ویژگی یا در پاسخ به مشکلی است که در طول آزمایش متوجه شده اید، ممکن است این مورد اتفاق بیفتد.

با این حال، اگر آن را مفید می‌دانید و قصد دارید به طور منظم از آن استفاده کنید، ممکن است همانطور که در نمونه Teapot نشان داده شده است، یک پیکربندی ساخت جدید برای HWASan یا ASan ایجاد کنید. برای مثال، اگر به طور منظم Address Sanitizer را به عنوان بخشی از تست‌های واحد خود یا در طول تست‌های دود در طول شب بازی خود اجرا کنید، ممکن است این کار را انجام دهید.

ایجاد یک پیکربندی ساخت جداگانه ممکن است به ویژه مفید باشد اگر پروژه بزرگی دارید که تعداد زیادی از کتابخانه های شخص ثالث مختلف را مصرف می کند که معمولاً آنها را به طور ایستا با کتابخانه استاندارد C++ پیوند می دهید. پیکربندی های ساخت اختصاصی می تواند به اطمینان از اینکه تنظیمات پروژه شما همیشه دقیق باقی می ماند کمک کند.

برای ایجاد یک پیکربندی ساخت، از Property Pages پروژه خود، روی دکمه Configuration Manager… کلیک کنید و سپس منوی کشویی Active solution configuration را باز کنید. سپس انتخاب کنید ، و یک پیکربندی ساخت جدید با نام مناسب ایجاد کنید (به عنوان مثال، HWASan فعال شده است ).

استفاده از HWASan با تخصیص دهنده های حافظه سفارشی

HWASan به طور خودکار حافظه اختصاص داده شده از طریق malloc (یا new ) را قطع می کند تا بتواند برچسب ها را به اشاره گرها تزریق کند و عدم تطابق برچسب ها را بررسی کند.

با این حال، هنگام استفاده از یک تخصیص دهنده حافظه سفارشی، HWASan نمی تواند به طور خودکار روش های تخصیص حافظه سفارشی شما را رهگیری کند. بنابراین، اگر می خواهید از HWASan با تخصیص دهنده حافظه سفارشی خود استفاده کنید، تخصیص دهنده حافظه خود را برای فراخوانی صریح HWASan ابزار کنید. این کار فقط با چند خط کد قابل انجام است.

پیش نیازها

متدهای HWASan که باید فراخوانی کنید در این هدر تعریف شده است:

#include "sanitizer/hwasan_interface.h"

روش تخصیص حافظه خود را ابزار دقیق کنید

  1. اشیاء را با دانه بندی و تراز بلوک 16 بایتی تخصیص دهید. به عنوان مثال، اگر یک تخصیص دهنده استخر دارید که به اشیاء با اندازه ثابت با اندازه 24 بایت سرویس می دهد، تخصیص های خود را تا 32 بایت گرد کنید و تا 16 بایت تراز کنید.

  2. یک تگ 8 بیتی ایجاد کنید. تگ شما نباید از مقادیر 0-16 استفاده کند، زیرا این مقادیر برای استفاده داخلی رزرو شده اند.

  3. HWASan را برای شروع ردیابی منطقه حافظه با آن تگ فعال کنید:

    __hwasan_tag_memory((void*) address, tag, size);
    
  4. تگ را به 8 بیت بالای اشاره گر خود تزریق کنید:

    address = __hwasan_tag_pointer((void*) address, tag);
    

روش تخصیص حافظه خود را ابزار دقیق کنید

  1. تگ را برای منطقه حافظه بازنشانی کنید تا باعث شود دسترسی های بیشتر از طریق نشانگرهای برچسب گذاری شده موجود با شکست مواجه شود:

    __hwasan_tag_memory(__hwasan_tag_pointer(ptr, 0), 0, size);
    

کار با مجموعه ای از اشیاء از پیش تخصیص داده شده

اگر تخصیص دهنده حافظه شما، اشیاء را در یک Pool از قبل تخصیص دهد و به جای آزاد کردن واقعی اشیاء، آنها را به pool برگرداند، روش توزیع شما می تواند مستقیماً برچسب حافظه و اشاره گر را با یک مقدار جدید بازنویسی کند:

```
__hwasan_tag_memory(__hwasan_tag_pointer(ptr, 0), tag, size);
ptr = __hwasan_tag_pointer((void*)ptr, tag);
```

اگر از این تکنیک استفاده می‌کنید، روش‌های تخصیص شما نیازی به برچسب‌گذاری نشانگرها یا بلوک‌های حافظه ندارند، اما زمانی که اشیاء موجود در pool خود را از قبل تخصیص می‌دهید، نشانگرها و بلوک‌های حافظه را تگ کنید. برای مثالی که از این سبک استفاده می کند، نمونه PoolAllocator را ببینید.