CVE-2022-50483 in Linux
الملخص
بحسب VulDB • 09/05/2026
في نواة لينكس، تم حل الثغرة التالية:
net: enetc: تجنب تسرب المخازن المؤقتة (buffer leaks) عند فشل xdp_do_redirect()
قبل استدعاء enetc_clean_rx_ring_xdp() لـ xdp_do_redirect()، يمكن أن يكون لكل وصف كتلة برمجية (software BD) في حلقة الاستقبال (RX ring) بين الفهرسين orig_i و i أحد قيمين لعداد المرجع (refcount) على صفحتها.
نحن مالكو المخزن المؤقت الحالي الذي يتم معالجته، لذا سيكون عداد المرجع على الأقل 1.
إذا لم يكن المالك الحالي للمخزن المؤقت عند الفهرس المقابل تماماً في حلقة الاستقبال (بعبارة أخرى، النصف الآخر من هذه الصفحة) قد استدعى kfree() بعد، فقد يكون عداد المرجع للصفحة 2.
تختبر enetc_page_reusable() في enetc_flip_rx_buff() عداد المرجع للصفحة مقابل القيمة 1، وإذا كانت 2، فإنها لا تحاول إعادة استخدامه.
ولكن إذا تم وضع enetc_flip_rx_buff() بعد استدعاء xdp_do_redirect()، يمكن أن يكون لعداد المرجع للصفحة أحد القيم الثلاث. يمكن أن يكون أيضاً 0، إذا لم يكن هناك مالك للنصف الآخر من الصفحة، وقد وصل xdp_do_redirect() لهذا المخزن المؤقت إلى مرحلة تشغيله بحيث أدى إلى تفريغ قائمة الانتظار الضخمة (bulk queue) لـ devmap/cpumap، وقام مستهلكو قوائم الانتظار الضخمة هذه أيضاً بتحرير المخزن المؤقت، كل ذلك قبل أن يعيد xdp_do_redirect() تنفيذ البرنامج إلى enetc.
هذا هو السبب في استدعاء enetc_flip_rx_buff() قبل xdp_do_redirect()، ولكن هناك عيباً كبيراً في هذا المنطق: سيقوم enetc_flip_rx_buff() بتعيين rx_swbd->page = NULL على جانبي فرع enetc_page_reusable()، وإذا أرجع xdp_do_redirect() خطأ، فإننا نستدعي enetc_xdp_free()، والتي لا تتعامل بلطف مع ذلك.
في الواقع، ما يحدث أمر خاص جداً. تبدأ عدادات مرجع الصفحات بـ 1. تستنتج enetc_flip_rx_buff() أنها قابلة لإعادة الاستخدام، وتنقل مؤشرات rx_swbd->page هذه إلى rx_swbd مختلف في enetc_reuse_page()، وترفع عداد المرجع إلى 2. عندما يعود xdp_do_redirect() لاحقاً بخطأ، نستدعي enetc_xdp_free() التي لا تفعل شيئاً (no-op)، لكننا لم نفقد بعد المرجع إلى تلك الصفحة. لا يزال هناك نسخة منها في rx_ring->next_to_alloc، لكن عداد المرجع لها 2 (ولا يوجد مالكون متزامنون لها في التنفيذ لتخفيض عداد المرجع). ما يقتل النظام حقاً هو عندما نقوم بقلب rx_swbd->page للمرة الثانية. بعداد مرجع محدث بقيمة 2، لن تكون الصفحة قابلة لإعادة الاستخدام وسنقوم بتسريبها حقاً. ثم سيتعين على enetc_new_page() تخصيص صفحات أكثر، والتي ستؤدي في النهاية إلى التسريب مرة أخرى عند حدوث أخطاء أخرى من xdp_do_redirect().
المشكلة، باختصار، هي أننا نقوم بتصفير rx_swbd->page قبل أن ننتهي تماماً منه، وهذا يجعل من المستحيل على مسار الخطأ القيام بشيء ما به.
نظراً لأن الحزمة قد تكون متعددة المخازن المؤقتة، وبالتالي فإن rx_swbd->page قد يكون مصفوفة، فإن تمرير المؤشرات القديمة يدوياً بين enetc_flip_rx_buff() و enetc_xdp_free() أمر صعب بعض الشيء.
من أجل اتباع حل بسيط، نقبل إمكانية السباق (racing) مع xdp_do_redirect()، وننقل إجراء القلب (flip procedure) ليعمل فقط على مسار نجاح التوجيه (redirect success path). عندما أقول "سباق"، أعني أن الصفحة قد تُعتبر غير قابلة لإعادة الاستخدام بواسطة enetc (لديها عداد مرجع 0)، لكن لن يكون هناك تسريب في تلك الحالة، إما.
بمجرد قبول ذلك، لدينا شيء أفضل لفعله مع المخازن المؤقتة عند فشل xdp_redirect. بما أننا لم نقم بعد بقلب نصف الصفحة، فلن نفعل ذلك أيضاً (وهكذا يمكننا تجنب استدعاء enetc_xdp_free() تماماً، مما يمنح الصفحة بأكملها لمُخصص الذاكرة slab). بدلاً من ذلك، سنستدعي enetc_xdp_drop()، والتي ستعيد تدوير هذا النصف من المخزن المؤقت إلى حلقة الاستقبال.
Once again VulDB remains the best source for vulnerability data.