تقرير فني ومقال بحثي

أتمتة إنشاء واجهة اختبار التضبيب (Fuzzing) باستخدام النماذج اللغوية الكبيرة والتحسين التطوري

تصميم إطار ديناميكي وتكراري للتطور الدلالي لأكواد Fuzz Harness بناءً على ملاحظات بيئة التنفيذ متعددة الأبعاد.

ضرورة ودافع البحث

تعتبر البرمجيات العمود الفقري للعديد من الخدمات الحيوية في المجتمعات الحديثة. اليوم، تعتمد الأنظمة المصرفية، وشبكات توزيع الطاقة، والبنية التحتية للاتصالات، وحتى الخدمات الصحية على صحة وأمن الأكواد. هذا الاعتماد جعل أي ثغرة برمجية لم تعد مجرد خطأ تقني بسيط، بل يمكن أن تحمل عواقب اقتصادية وأمنية واجتماعية خطيرة. تزداد أهمية هذه المشكلة بشكل خاص في المكتبات المكتوبة بلغات C و C++؛ وهي المكتبات التي استُخدم العديد منها لسنوات أو حتى عقود في البنية التحتية للبرمجيات اليوم. في عام 2024 وحده، تم تسجيل أكثر من أربعين ألف ثغرة أمنية جديدة في قاعدة بيانات CVE، وجزء كبير من هذه الثغرات يتعلق بهذه الأنظمة البيئية القديمة ولكنها لا تزال مستخدمة على نطاق واسع.

في مثل هذه البيئة، أصبح “اختبار التضبيب” (Fuzzing test) أحد أكثر الطرق فعالية لاكتشاف الثغرات تلقائياً. تقوم أدوات مثل AFL و libFuzzer بتحديد مسارات تنفيذ جديدة من خلال التنفيذ المتكرر للبرنامج على مدخلات متغيرة (Mutated)، والاحتفاظ بالمدخلات التي تؤدي إلى اكتشاف سلوكيات جديدة وتطويرها. أظهر نجاح هذا النهج في مشاريع مثل OSS-Fuzz الخاص بـ Google و syzkaller أن اختبار التضبيب يمكن أن يكشف آلاف الأخطاء والثغرات الحقيقية على نطاق واسع.

التحدي الرئيسي: جودة Fuzz Harness

ومع ذلك، لا يعتمد نجاح اختبار التضبيب على قوة محرك التغيير (Mutation engine) فقط. أحد العوامل الحاسمة هو جودة “واجهة اختبار التضبيب” (Fuzz Harness)؛ وهو الجزء من الكود الذي يحول الإدخال الخام الناتج عن الفازر إلى استدعاءات ذات معنى في واجهة برمجة تطبيقات (API) المكتبة المستهدفة. إذا كانت هذه الواجهة ضعيفة أو محدودة أو مبسطة للغاية، فلن يتمكن الفازر من الوصول إلى الأجزاء العميقة من الكود مهما كان متقدماً.

بعبارة أخرى، واجهة اختبار التضبيب هي النافذة التي يرى الفازر من خلالها المكتبة؛ وكلما كانت هذه النافذة أضيق، بقي جزء أكبر من الكود في الظلام. كتابة هذه الواجهات يدوياً، خاصة للمكتبات المعقدة مثل OpenSSL، تعتبر مهمة صعبة وتستغرق وقتاً طويلاً. يجب أن يعرف المطور التسلسل الصحيح للاستدعاءات، وتهيئة هياكل البيانات، وشروط الـ API المخفية، والأنماط الصحيحة لإدارة الذاكرة؛ وهي معرفة لا تتوفر دائماً في الوثائق الرسمية. بالإضافة إلى ذلك، عادة ما تغطي الواجهة المكتوبة يدوياً عرضاً ثابتاً لـ API فقط، وبعد فترة، يتوقف نمو تغطية الكود. تُعرف هذه الظاهرة في اختبار التضبيب باسم الركود (Plateau).

مشكلة البحث والفجوة الحالية

تُظهر الأدلة التجريبية أنه في العديد من المكتبات المعقدة، لا يحدث ركود التضبيب بسبب ضعف محرك الفازر، ولكن بسبب قيود واجهة الاختبار نفسها. إذا كانت الواجهة تُفعل مساراً واحداً محدداً فقط من الـ API، فلا يمكن للفازر أن يتجاوز النطاق الذي بنته له تلك الواجهة. لذلك، فإن المشكلة الأساسية في هذا البحث هي كيفية تحويل واجهة اختبار التضبيب من جزء ثابت من الكود إلى كيان قابل للتحسين والتطور.

في السنوات الأخيرة، تم اقتراح أدوات مختلفة للإنشاء التلقائي لواجهة اختبار التضبيب. على سبيل المثال، يقوم FuzzGen بإنشاء الواجهة من خلال التحليل الثابت (Static analysis) لتبعيات الـ API، ولكنه لا يغيرها بعد الإنشاء ولا يستخدم ملاحظات تنفيذ الفازر لتحسينها. تستخدم أدوات مثل OSS-Fuzz-Gen و HarnessAgent النماذج اللغوية الكبيرة لإنشاء الواجهة ويمكنها إصلاح أخطاء الترجمة البرمجية (Compilation) إلى حد ما، لكن هذه العملية تبقى في الغالب على المستوى النحوي. بمجرد ترجمة الكود بنجاح، لا توجد عادة آلية للتحسين المنطقي والتدريجي للواجهة.

الفجوة البحثية (Research Gap)

تتمثل الفجوة الرئيسية في هذا المجال في أن واجهة اختبار التضبيب تعتبر عادة مخرجات ثابتة، وليست كياناً يمكن أن يتغير بناءً على نتائج التنفيذ. يركز هذا البحث بالضبط على هذه النقطة: تصميم إطار عمل يمكنه تحسين واجهة اختبار التضبيب بشكل تكراري وتطوري باستخدام ملاحظات التنفيذ.

النهج المقترح

الفكرة الأساسية لهذا البحث بسيطة ولكنها مهمة: إذا كانت المدخلات في اختبار التضبيب تتغير (Mutate) لاكتشاف مسارات جديدة في البرنامج، فلماذا لا تتغير واجهة الاختبار نفسها؟ بدلاً من إعطاء مدخلات جديدة دائماً لواجهة ثابتة، يمكن تغيير الواجهة نفسها لتفعيل أجزاء أكبر من الـ API وتوفير مسارات تنفيذ جديدة للفازر. وبناءً على ذلك، تم تحديد تصميم واجهة اختبار التضبيب في هذا البحث كمشكلة تحسين تطوري متعددة الأهداف على الكود المصدري.

مراحل عملية الدورة التطورية للنظام:

الخطوة الأولى: الإنشاء الأولي لمجموعة المرشحين
أولاً، يقوم النموذج اللغوي الكبير بإنشاء مجموعة أولية من الواجهات المرشحة بناءً على تواقيع واجهة برمجة التطبيقات (API signatures) للمكتبة المستهدفة.
الخطوة الثانية: الترجمة البرمجية (Compilation) والتنفيذ الديناميكي
بعد ذلك، تتم ترجمة كل واجهة وتنفيذها باستخدام أدوات مثل Clang و libFuzzer ومنظفات أخطاء الذاكرة والسلوك غير المحدد (Sanitizers).
الخطوة الثالثة: تحليل دالة اللياقة (Fitness function) متعددة المعايير
بعد التنفيذ، يتم تقييم جودة كل واجهة من خلال دالة لياقة مركبة. تأخذ دالة اللياقة هذه ثلاثة معايير في الاعتبار في وقت واحد: معدل تغطية الحواف (Edge coverage)، استقرار التنفيذ، و تنوع المسارات المكتشفة.
الخطوة الرابعة: تطبيق التغييرات والطفرات الدلالية (Semantic mutation)
في نهاية كل جيل، يتم تقديم أفضل الواجهات مع ملاحظات الأداء إلى النموذج اللغوي. يقترح النموذج بعد ذلك تغييرات مهيكلة؛ مثل تغيير طريقة تحليل المدخلات (Parsing)، أو إضافة استدعاءات API جديدة، أو إدخال منطق يعتمد على الحالة (Stateful logic)، أو تصحيح إدارة الذاكرة.

في إطار العمل هذا، لا يقتصر دور النموذج اللغوي الكبير على كونه مولد كود لمرة واحدة فقط، بل يلعب دور “عامل الطفرة الدلالية” (Semantic mutation operator). هذا الدور هو الفارق الرئيسي بين النهج المقترح والطرق السابقة.

لماذا تعمل النماذج اللغوية الكبيرة بنجاح كعوامل طفرة؟

التغيير العشوائي (Random mutation) على مستوى كود C/C++ عادة لا يكون فعالاً، لأن معظم التغييرات العشوائية تؤدي إلى كود غير صالح أو غير قابل للترجمة. لكن النماذج اللغوية الكبيرة، نظراً لتدريبها على كميات هائلة من الأكواد، على دراية تامة بأنماط API الشائعة، وتسلسلات الاستدعاء، والقواعد الضمنية لإدارة الذاكرة. لذلك يمكنها اقتراح تغييرات يكون احتمال قابليتها للترجمة البرمجية وصحتها الدلالية أعلى بكثير من الطفرات العشوائية.

النتائج التجريبية

تم تقييم الإطار المقترح على إحدى عشرة مكتبة C و C++ متنوعة. في سبع حالات، أي أكثر من 63% من الاختبارات، تمكن النظام من إنشاء تغطية كود قابلة للقياس وتحسين واجهة اختبار التضبيب عبر الأجيال المتعاقبة. كانت الحالات غير الناجحة مرتبطة بشكل أساسي بتبعيات الترجمة المعقدة أو واجهات برمجة تطبيقات (APIs) محددة للغاية؛ وهو وضع عانى فيه النموذج اللغوي أحياناً من افتراضات غير صحيحة أو هلوسات.

تم تحقيق أبرز نتيجة في مكتبة GPAC للوسائط المتعددة. في هذه الحالة، زادت تغطية الحواف من 75 حافة في الجيل الصفر إلى 231 حافة في الجيل الثامن؛ أي بنمو يعادل 208%. توضح هذه النتيجة أن الحلقة التطورية تمكنت من تغيير الواجهة بمرور الوقت بطريقة تفعل المزيد من مسارات التنفيذ. بالمقارنة مع HarnessAgent، وهو إحدى الأدوات المتقدمة الموجودة في هذا المجال، كان الفرق كبيراً.

الطريقة / أداة التقييمتغطية الحواف (مكتبة GPAC)التحسن مقارنة بالجيل الصفرالتفوق على HarnessAgent
الإنشاء لمرة واحدة (الجيل 0)75 حافة
أداة HarnessAgent44 حافةأساس المنافسة
النظام المقترح (الجيل الثامن)231 حافة208% نمو425% تفوق

أيضاً، في مقارنة مباشرة مع الإنشاء لمرة واحدة للواجهة بواسطة النموذج اللغوي، أضافت الحلقة التطورية متوسطاً قدره 68.7% تغطية إضافية للحواف. تشير هذه النتيجة إلى أن القيمة الأساسية للنظام لا تكمن فقط في قدرة النموذج اللغوي، بل في دمجه مع ملاحظات التنفيذ والتحسين التكراري.

في الاختبار الذي أجري على OpenSSL، تمكن النظام أيضاً من اكتشاف اثنتي عشرة ثغرة أمنية تلقائياً. يشير هذا الاكتشاف إلى أن التحسين التطوري لواجهة اختبار التضبيب يمكن أن يفعّل مسارات أعمق في الكود؛ وهي مسارات يكون فيها احتمال الوصول إلى الحالات الضعيفة أعلى. من حيث التكلفة، كان متوسط التكلفة الحسابية لكل هدف حوالي 44 سنتاً، وهو أمر فعال للغاية من حيث التكلفة مقارنة بالوقت والخبرة اللازمين لكتابة واجهات اختبار التضبيب يدوياً.

أهمية البحث

يمكن دراسة أهمية هذا البحث على عدة مستويات، ويمكن تصنيف إنجازاته في ثلاثة مجالات عامة:

01

أمن البرمجيات

يوفر هذا الإطار طريقة عملية لتوسيع نطاق اختبار التضبيب في المكتبات المعقدة؛ المكتبات التي تكون واجهات برمجة التطبيقات (APIs) الخاصة بها واسعة ومبنية على الحالة (Stateful) وتفتقر أحياناً إلى التوثيق الكامل.

02

الذكاء الاصطناعي التطبيقي

يوضح هذا البحث أن النماذج اللغوية الكبيرة يمكن أن تتجاوز مجرد إنشاء الكود أو إكماله، لتظهر كعنصر نشط في عملية التحسين التطوري.

03

هندسة البرمجيات

يمكن استخدام نمط “التحسين التكراري للكود باستخدام ملاحظات التنفيذ” في مجالات مثل اختبار الانحدار الآلي (Automated regression testing)، وتوليد البرامج (Program synthesis)، والتحليل الديناميكي للبرمجيات.

الخاتمة

يقدم هذا البحث إطار عمل لتحويل تصميم واجهة اختبار التضبيب من مهمة ثابتة إلى مشكلة تحسين تطوري. في هذا الإطار، يتولى النموذج اللغوي الكبير دور عامل الطفرة الدلالية، ويقوم بتحسين واجهات اختبار التضبيب تدريجياً بناءً على ملاحظات التنفيذ الفعلية. يتم قياس جودة كل واجهة بمزيج من تغطية الحواف، واستقرار التنفيذ، وتنوع المسار، وتشكل أفضل الواجهات أساساً للجيل القادم.

أظهرت النتائج التجريبية أن هذا النهج يمكن أن يوفر تغطية قابلة للقياس في جزء كبير من مكتبات C و C++، ويحقق نمواً ملحوظاً في التغطية في حالات مثل GPAC، ويتفوق في الأداء على الأدوات الحالية، وحتى يكتشف ثغرات حقيقية في مكتبة مثل OpenSSL. بشكل عام، تشير نتائج هذا البحث إلى أن الدمج الهادف للنماذج اللغوية الكبيرة مع ملاحظات التنفيذ متعددة الأبعاد يمكن أن يحول واجهة اختبار التضبيب من جزء كود ثابت إلى كيان ديناميكي قابل للتطور وأكثر فعالية في اكتشاف الثغرات العميقة.

Avatar photo
تیم تولید محتوای مبنا Website

Comments closed.