راهکارهای عملی برای افزایش کارایی فاز تست با AFL++ — Tips for more effective fuzz testing with AFL++
خلاصه
این مقاله تجربیات ارزشمندی را برای اجرای بهینهتر فاز تست (Fuzz Testing) روی برنامههای C و C++ با ابزار AFL++ ارائه میدهد. با بهکارگیری این ترفندها، از جمله استفاده از حالت پایدار و تنظیم دقیق سانتیلایزرها، میتوانید اشکالات پنهان نرمافزاری را سریعتر و مؤثرتر کشف کنید.
فاز تست یا Fuzz Testing یکی از روشهای فوقالعاده مؤثر برای کشف خودکار باگها و آسیبپذیریهای نرمافزاری است. با این حال، بسیاری از پروژهها هنوز از این ابزارها بهره میبرند یا پیکربندیهای بهینهای ندارند. اگر برنامهای ورودیهای پیچیده را پردازش میکند و تاکنون فاز تست روی آن انجام نشده، احتمال یافتن حداقل یک باگ بسیار بالا است. ابزار AFL++ یکی از بهترین پلتفرمها برای این کار در برنامههای C و C++ روی لینوکس به شمار میرود. در ادامه به بررسی نکات کلیدی برای اثربخشی بیشتر این ابزار میپردازیم.
(۱) پیکربندی سانتیلایزرها و Assertions
استفاده از Assertions بیشتر به معنای فاز تست کارآمدتر است و سانتیلایزرها در واقع Assertionsهای خودکار هستند. همیشه برنامهها را با Address Sanitizer (ASan) و Undefined Behavior Sanitizer (UBSan) کامپایل کنید. پیکربندی پیشفرض ASan برای فاز تست ایدهآل نیست و باید متغیر محیطی ASAN_OPTIONS را طوری تنظیم کنید که با اولین خطا متوقف شود:
export ASAN_OPTIONS="abort_on_error=1:halt_on_error=1:symbolize=0"
همچنین برای حساسیت بیشتر در برابر باگهای Use-after-free و Use-after-return، حافظه تخصیصیافته را بهطور کامل با الگو پر کنید و محدودیت پیشفرض ۴ کیلوبایتی را افزایش دهید تا از پنهان ماندن باگها جلوگیری شود.
(۲) استفاده از Persistent Mode
حالت پایدار یا Persistent Mode در AFL++ به شما اجازه میدهد حلقهای از تستها را درون یک فرایند اجرا کنید که سرعت تست را به طور چشمگیری (تا یک مرتبه بزرگی) افزایش میدهد. نوشتن یک قالب ساده برای این کار، اجرای تستها را بسیار سریعتر و روانتر میکند.
(۳) گنجاندن فایلهای سورس به جای فایلهای هدر
برای سادگی کار و اجتناب از پیچیدگیهای ساخت (Build Configuration)، میتوانید به جای مدیریت فایلهای آبجکت مختلف، فایلهای سورس مورد نیاز را مستقیماً در قالب تست خود اینکلود (#include) کنید. این روش به شما اجازه میدهد حتی توابع static داخلی برنامه را که دسترسی به آنها دشوار است، بهسادگی تست کنید.
(۴) ایزولهسازی تستها از یکدیگر
تستها نباید تحت تأثیر نتایج تستهای قبلی قرار گیرند. در حالت پایدار، مدیریت وضعیتهای سراسری (Global State) و پاکسازی حافظه heap بسیار چالشبرانگیز است. متغیرهای سراسری را حذف کنید و حتماً پارامترهای خروجی را پیش از تست صفر یا مقداردهی اولیه کنید.
(۵) اجتناب از تست مستقیم روی بافر فاز تست
یکی از خطاهای رایج، پاس دادن مستقیم بوفر و طول ورودی به برنامه است. به دلیل اینکه بوفر زیربنایی AFL++ بزرگتر از اندازه طول ورودی است، ممکن است سانتیلایزر سرریزهای خواندن را تشخیص ندهد. همیشه یک کپی با اندازه دقیق از دادهها بسازید و سپس به هدف ارسال کنید.
(۶) عدم اتلاف وقت برای آزادسازی حافظه
در فاز تست، از انجام کارهای غیرضروری مثل آزادسازی دقیق حافظه (تخریبکنندهها) خودداری کنید، مگر اینکه برنامه بسیار ولخرج در مصرف حافظه باشد یا نیاز به بررسی رفتار Destructor داشته باشید. این کار سرعت تست را بالا میبرد.
(۷) استفاده از Descriptorهای حافظه برای فایلهای نامگذاری شده
اگر برنامهای فقط ورودی را از مسیرهای فایل سیستم میپذیرد، ساخت یک Memory File Descriptor با memfd_create و استفاده از مسیر /proc/self/fd/ راهکار سریعی است که از فرسایش SSD جلوگیری کرده و امکان فاز تست موازی را بدون تداخل فراهم میکند.
(۸) پیکربندی هدف برای بوفرهای کوچکتر
بوفرهای ثابت بسیار بزرگ در برنامههای قدیمی میتوانند باگها را پنهان کنند. با کاهش اندازهی این بوفرها در محیط تست (مثلاً کاهش از دهها کیلوبایت به مقادیر بسیار کوچک)، میتوانید شرایط بحرانی را سریعتر شبیهسازی کرده و مسیرهای اجرایی عمیقتر را کشف کنید.
این مطلب بهصورت کاملاً خودکار و با هوش مصنوعی از مقالهٔ اصلی و بحث هکرنیوز تهیه شده و ممکن است خطا داشته باشد. حقوق مطلب اصلی متعلق به نویسندهٔ آن است. قوانین و گزارش مشکل