
بعض الأخطاء التي تم العثور عليها عبر التنقيح الثنائي والتهيئة العشوائية
media-gen هي أداة سطر أوامر صغيرة مكتوبة بلغة Rust وخفيفة الاعتماديات لبناء حالتي اختبار لمحلّل الوسائط. تقوم بتعديل ملفات وسائط صالحة موجودة في مكانها؛ ولا تستدعي FFmpeg، ولا ترقّع متصفحًا، ولا تتصل بخادم، ولا ترفع أي شيء.
تتضمن حالة WebM تخطيط جسر لكلٍّ من مسار Vorbis الأقدم المؤجَّل بمخزن مؤقت واحد ومسار الإسقاط الفوري الحالي. تم التحقق من العيّنة القصيرة المضمّنة في المستودع باستخدام Discord Desktop 1.0.9257 (Electron 42.11.1 / Chromium 148.0.7778.280) وChrome 153.0.8010.48. تظل حالة M4A خاصة بإصدار Discord/FFmpeg المثبَّت. قد ترفض الإصدارات الأخرى هذه الملفات، أو تتعامل معها بأمان، أو تفشل بشكل مختلف.
| الحالة | المدخل | التأثير في الإصدار المتأثر | المُشغِّل |
|---|---|---|---|
| جسر إسقاط WebM/Vorbis | ملف WebM موجود يحتوي على مسار A_VORBIS | ينتهي المُصيّر بـ 0x80000003 (STATUS_BREAKPOINT) في فحص الإصدار AudioDiscardHelper الخاص بـ Chromium على كلا وضعي الإسقاط المُختبَرين | التشغيل/فك الترميز؛ تحميل البيانات الوصفية وحده غير كافٍ |
عدّاد stsz الثابت في M4A | بذرة AAC/M4A صالحة ذات بداية سريعة | يخصص المُصيّر مؤقتًا حوالي 6.6 جيجابايت (6.16 جيبابايت) من الذاكرة الخاصة أثناء تحميل البيانات الوصفية | تحميل البيانات الوصفية بعد تغيّر عنصر الصوت من preload="none" إلى البيانات الوصفية |
هاتان حالتا اختبار لحجب الخدمة/استهلاك الموارد، وليستا استغلالًا مُثبَتًا لتنفيذ التعليمات البرمجية. شغّلهما فقط في بيئة اختبار معزولة ومحدودة.
media-gen هي طبقة إعادة الإنتاج، وليست الطريقة التي اكتُشفت بها هذه الحالات. كان الجزء الصعب هو إيجاد وإثبات السلوك داخل ملف Discord التنفيذي الأصلي الكبير ومكتبات الوسائط المضمّنة معه. جعل blare2 ذلك عمليًا عبر إعادة كتابة نسخة معزولة من بيئة التشغيل الدقيقة والسماح لمجسّات ضيقة بمراقبة التنفيذ دون تغيير التطبيق المثبَّت.
في حالة WebM، توقف مجسّ blare2 الدلالي عند فحص AudioDiscardHelper::ProcessBuffers بالضبط وسجّل الحالة الفاشلة: discarded_frames = 129 وdecoder_delay = 128. بدون تلك الملاحظة، كان خروج المُصيّر بـ STATUS_BREAKPOINT سيحدد فقط تأكيد إصدار عام؛ ولن يثبت أي ثابت وسائط فشل أو ما إذا كانت الحشوة المصنوعة قد وصلت إليه فعلًا.
في حالة M4A، تكون التخصيصات الكبيرة عابرة وقد تختفي قبل أخذ عيّنة عملية عادية. أظهرت تغطية blare2 الدقيقة لبيئة التشغيل والأدوات الموجّهة، مقترنة بأخذ عيّنات عالية التردد للعملية والتفكيك، أن الملف الصغير وصل إلى باني جدول عيّنات MOV وأن العدّاد المُعلَن وسّع تخصيصات AVIndexEntry وجدول التوقيت. وقد فصل ذلك المشكلة عن فشل فك ترميز AAC عادي أو ارتباط مضلّل بحجم الملف/نفاد الذاكرة. لم تكن تغطية دخول الدوال الواسعة وحدها كافية: اتبعت ملفات العدّ المنخفض والمرتفع الدوال نفسها، بينما اختلفت أحجام تخصيصاتها اختلافًا جذريًا.
لم يحلّ blare2 محل تحليل الحاويات أو مراجعة الشيفرة أو عناصر التحكم، ولم يثبت أن مسار الرفع/CDN الإنتاجي الخاص بـ Discord يحافظ على هذه البايتات. كانت قيمته في الإسناد وقت التشغيل: فقد حوّل سلوك المحلّل المشبوه إلى نتيجة قابلة لإعادة الإنتاج ومثبَّتة بإصدار معيّن، مع حالة فاشلة دقيقة وتفسير دفاعي للتخصيص. بدون أدوات الفحص الثنائي من هذا النوع، كان العثور على هاتين الحالتين سيكون أبطأ بكثير وأصعب في التحقق.
من جذر المستودع:
cargo build --release -p media-gen
الملف التنفيذي هو target/release/media-gen (media-gen.exe على Windows). لا يحتاج المولّد نفسه سوى Rust والاعتماديات في Cargo.toml.
cargo run --release -p media-gen -- --help
cargo run --release -p media-gen -- webm --help
cargo run --release -p media-gen -- m4a --help
تتحول DiscardPadding السالبة في Matroska إلى تخطٍّ أمامي في Vorbis. يؤجّل مسار Vorbis الأقدم في Chromium تلك البيانات الوصفية بمقدار حزمة مُرمَّزة واحدة؛ بينما يطبّقها Chromium الحالي على المخرجات المفكوكة الحالية ويُسقط البيانات الوصفية للحزمة 0 عندما لا تُصدر تلك الحزمة التمهيدية أي PCM. تكرار تخطٍّ كبير واحد عبر الحزمتين 0 و1 يجسر بين السلوكين:
| حزمة الصوت | PCM المفكوك | التخطي الأمامي |
|---|---|---|
| 0 | لا شيء (تمهيد Vorbis) | 577 إطارًا |
| 1 | 576 إطارًا |
يحتوي المسار على CodecDelay بمقدار 128 إطارًا. في الوضع المؤجَّل الأقدم، يُطبَّق تخطي 577 إطارًا للحزمة 0 على الحزمة 1. في الوضع الحالي، يُسقَط تخطي الحزمة 0 ويُطبَّق تخطي الحزمة 1 المطابق مباشرة. في كلتا الحالتين، لا يمكن إزالة سوى 448 إطارًا بعد إزاحة تأخير فك الترميز، لذا تنتقل 129 إطارًا إلى الحزمة 2. يصل تخطيها الأمامي الموجب إلى فحص الإصدار في Chromium بعد إزالة تلك الـ 129 إطارًا بالفعل. الثابت المطلوب هو discarded_frames <= decoder_delay؛ و129 <= 128 يفشل ويُنهي المُصيّر.
يحلّل المولّد ترويسات إعداد Vorbis، ويحسب الأحجام المفكوكة للحزمتين 1 و2، ويفشل بشكل آمن ما لم يكن الجسر قابلًا للتطبيق. ثم يقوم بما يلي:
SimpleBlock صوتية إلى عناصر BlockGroup؛DiscardPadding سالبة مشتركة على الحزمتين 0 و1 ومُشغِّلًا موجبًا بإطار واحد على الحزمة 2؛SeekHead وCues وعناصر CRC الخاصة بالعناقيد المتأثرة القديمة بحيث تبقى الحاوية المُعاد كتابتها قابلة للتحليل.يُبلّغ البيان عن المسارين الفوري والمؤجَّل بشكل منفصل. بالنسبة للعيّنة المضمّنة في المستودع، يسمّي كلا المسارين الحزمة 2 كحزمة الفحص ويُبلّغان عن expected_carry_frames: 129 وexpected_check_fails: true.
يحتوي المستودع على ملف WebM ضابط بحجم 4,185 بايت ومدة 43 مللي ثانية:
cargo run --release -- webm \
--input samples/short-vorbis-dual-control.webm \
--output .build/short-vorbis-dual-crash.webm \
--manifest .build/short-vorbis-dual-crash.json \
--force
يجب أن يحتوي المدخل على:
A_VORBIS؛CodecDelay موجبة (تُقرأ عادةً من المسار)؛ ومن الخيارات المفيدة --trigger-skip و--codec-delay و--sample-rate. يبقى --second-skip اسمًا مستعارًا للخيار المُعاد تسميته --trigger-skip. القيم الافتراضية هي القيم اللازمة لحالة الفشل المعروفة. يطبع الأمر تقرير JSON إلى stdout ويكتب التقرير نفسه إلى --manifest عند توفير ذلك الخيار.
المرشّح المضمّن في المستودع بحجم 4,206 بايت ومدة 43 مللي ثانية. قيمة SHA-256 الخاصة به هي:
a0ab9e146c629f037b86612addc1ab6fff9200711d45aa5f5edbb9576cc206ac
يُتوقع أن تتغير قيمة تجزئة المخرجات إذا تغيّر المدخل أو نافذة الحزم أو الخيارات.
تستغل حالة M4A شكل جدول عيّنات MP4 صالحًا بدلًا من حمولة AAC المُرمَّزة. يغيّر المولّد بذرة كما يلي:
stsz بـ sample_size = 1.stsz، وأول تشغيل stsc، وأول تشغيل stts على القيمة الكبيرة نفسها.stco المطلقة بحيث تظل حمولة AAC بحجم بايت واحد تشير داخل mdat.يتعامل مُفكِّك حاويات FFmpeg المثبَّت مع العدّاد المُعلَن كمصدر موثوق أثناء بناء فهرسه وجداول التوقيت. تستخدم البنى ذات الصلة حوالي 24 بايت لكل عيّنة مُعلَنة لـ AVIndexEntry و12 بايت لكل عيّنة لبيانات التوقيت: أي 36 بايت لكل إدخال عدّ في المجموع. الحد الأقصى المقبول للعدّ في الإصدار المُختبَر هو 178,956,969 (0x0AAAAAA9)، وهو ما يُترجم إلى:
178,956,969 * 24 = 4,294,967,256 bytes
178,956,969 * 12 = 2,147,483,628 bytes
combined = 6,442,450,884 bytes
وصل مُصيّر Discord الدقيق إلى ذروة ذاكرة خاصة مقدارها 6,614,761,472 بايت أثناء تحميل البيانات الوصفية. العدّ المجاور 178,956,970 مرفوض من حد FFmpeg المثبَّت وظل قريبًا من الذاكرة الطبيعية. هذا استهلاك موارد غير مُتحكَّم فيه (CWE-400)، وليس التفافًا صحيحًا مُلاحَظًا، أو تخصيصًا بحجم سالب، أو كتابة خارج الحدود. يمكن قطع حمولة AAC الفعلية؛ ولا يُشترط نجاح التشغيل للوصول إلى التخصيص الكبير.
يقبل المولّد عمدًا بذرة بدلًا من تضمين مُرمِّز AAC. استخدم ملف AAC/M4A ذا بداية سريعة يحتوي على جدول stsz صريح واحد، وتشغيل stsc واحد، وإدخال stts واحد أو أكثر، وإزاحات قطع stco. يفشل بشكل آمن إذا كانت البنية المطلوبة غائبة.
cargo run --release -p media-gen -- m4a \
--input path/to/base-faststart.m4a \
--output .build/media-gen/constant-stsz.m4a \
--manifest .build/media-gen/constant-stsz.json \
--force
القيمة الافتراضية لـ --sample-count هي 178956969، وهي الحد الأقصى المقبول في الإصدار المثبَّت. استخدم قيمة أصغر لاختبار سريع منخفض الذاكرة، على سبيل المثال:
cargo run --release -p media-gen -- m4a \
--input path/to/base-faststart.m4a \
--output .build/media-gen/constant-stsz-32000000.m4a \
--sample-count 32000000 \
--force
--sample-count 178956970 هو ضابط رفض مجاور مفيد، وليس القيمة المُشغِّلة. تنقل العلامة الاختيارية --moov-at-end الصندوق moov بعد mdat وتحدّث stco/co64؛ وهي مفيدة عند اختبار ملف تظهر بايتات AAC الفعلية فيه قبل بياناته الوصفية، لكنها ليست مطلوبة لآلية التخصيص.
لإنتاج ذلك التخطيط صراحةً:
cargo run --release -p media-gen -- m4a \
--input path/to/base-faststart.m4a \
--output .build/media-gen/constant-stsz-moov-end.m4a \
--moov-at-end \
--force
الملفات المطابقة هي
samples/short-vorbis-dual-control.webm،
وsamples/short-vorbis-dual-crash.webm،
وsamples/short-vorbis-dual-crash.json.
| 577 إطارًا |
| 2 | 1,024 إطارًا | إطار واحد |