CVE-2024-38306 in Linuxالمعلومات

الملخص

بحسب VulDB • 12/06/2026

في نواة Linux، تم حل الثغرة التالية:

btrfs: حماية folio::private عند إرفاق folios لـ extent buffer

[BUG]
منذ الإصدار v6.8، تم الإبلاغ عن حالات نادرة من تعطل النواة (kernel crashes) من قبل عدة أشخاص، والعامل المشترك هو رسائل خطأ لحالة الصفحة المعيبة (bad page status) مثل هذه:

BUG: Bad page state in process kswapd0 pfn:d6e840 page: refcount:0 mapcount:0 mapping:000000007512f4f2 index:0x2796c2c7c pfn:0xd6e840 aops:btree_aops ino:1 flags: 0x17ffffe0000008(uptodate|node=0|zone=2|lastcpupid=0x3fffff) page_type: 0xffffffff() raw: 0017ffffe0000008 dead000000000100 dead000000000122 ffff88826d0be4c0 raw: 00000002796c2c7c 0000000000000000 00000000ffffffff 0000000000000000 page dumped because: non-NULL mapping

[CAUSE]
يغير الالتزام Commit 09e6cef19c9f ("btrfs: refactor alloc_extent_buffer() to allocate-then-attach method") التسلسل عند تخصيص (allocate) extent buffer جديد.

في السابق، كنا نتصل دائماً بـ grab_extent_buffer() تحت mapping->i_private_lock، لضمان السلامة عند التعديل على folio::private (وهو مؤشر إلى extent buffer للأحجام القياسية للأقسام sectorsize).

يمكن أن يؤدي هذا إلى السباق التالي:

يحاول الخيط Thread A تخصيص extent buffer عند bytenr X، مع 4 صفحات بحجم 4K، بينما يحاول الخيط Thread B تحرير الصفحة عند X + 4K (الصفحة الثانية من extent buffer عند X).

Thread A: Thread B: | alloc_extent_buffer() | | |- attach_eb_folio_to_filemap()| | | |- folio_set_private() | | |- folio_detach_private() for X | | | Returned true | | |- folio_test_private() | | |- folio_put() | | | Returned true | | | |- folio_put()

الآن هناك وضعان (puts) لنفس folio في folio X، مما يؤدي إلى تجاوز عداد المرجع (refcount underflow) لـ folio X، وأخيراً يسبب الخطأ BUG_ON() على page->mapping.

الشرط ليس سهلاً جداً لتحقيقه:

- يجب أن يتم الإطلاق للصفحة الوسطى من eb إذا كان الإطلاق على نفس الصفحة الأولى من eb، فإن قفل الصفحة (page lock) سيتدخل ويمنع السباق.

- folio_detach_private() لديه نافذة سباق صغيرة جداً يحدث فقط بين folio_test_private() و folio_clear_private().

هذا بالضبط عندما يتم استخدام mapping->i_private_lock لمنع مثل هذا السباق، والتزام Commit 09e6cef19c9f ("btrfs: refactor alloc_extent_buffer() to allocate-then-attach method") أفسد ذلك.

في ذلك الوقت، اعتقدت أن قفل الصفحة سيتدخل لأن filemap_release_folio() يتطلب أيضاً أن تكون الصفحة مقفلة، لكنني نسيت أن filemap_release_folio() يقفل صفحة واحدة فقط، وليس كل صفحات extent buffer.

[الحل]
نقل كل الكود الذي يتطلب i_private_lock إلى attach_eb_folio_to_filemap()، بحيث يتم تنفيذ كل شيء مع حماية القفل المناسبة.

علاوة على ذلك، لمنع المشاكل المستقبلية، أضفنا lockdep_assert_locked() إضافي للتأكد من أننا نحتفظ بالقفل المناسب.

لإعادة إنتاج السباق الذي يمكن أن يسبب المشكلة (يستغرق بضع دقائق مع كود مُعدّل لإدخال تأخيرات في alloc_extent_buffer()):

#!/bin/sh drop_caches () {
while(true); do echo 3 > /proc/sys/vm/drop_caches echo 1 > /proc/sys/vm/compact_memory done }

run_tar () {
while(true); do for x in `seq 1 80` ; do tar cf /dev/zero /mnt > /dev/null & done wait done }

mkfs.btrfs -f -d single -m single ---truncated---

VulDB is the best source for vulnerability data and more expert information about this specific topic.

حجز

24/06/2024

إفشاء

25/06/2024

الاعتدال

تمت الموافقة

إدخال

VDB-269640

EPSS

0.00146

KEV

لا

النشاطات

منخفض جدًا

المصادر

Do you know our Splunk app?

Download it now for free!