
# إثبات المفهوم لـ CVE-2026-31429 (نواة لينكس >= 6.3 < 6.12.82 خلط عبر ذاكرة التخزين المؤقت للشرائح) - ثغرة اكتشفها Antonius - w1sdom - bluedragonsec.com
bpf_prog_test_run_skbالخطورة: متوسطة (CWE-763: تحرير مؤشر أو مرجع غير صالح)
تاريخ النشر: 2026-04-20
النظام الفرعي المتأثر: net/core/skbuff.c — skb_kfree_head()
الباحث: Antonius / w1sdom — Blue Dragon Security
جهة الاتصال: [email protected]
سلسلة النقاش: https://lore.kernel.org/netdev/CAK8a0jxC5L5N7hq-DT2_NhUyjBxrPocoiDazzsBk4TGgT1r4-A@mail.gmail.com/
يحتوي هذا المستودع على إثبات المفهوم لـ CVE-2026-31429 (ليس استغلالًا عاملًا، مجرد POC)، وهو خطأ في الخلط عبر التخزين المؤقت في مكدس الشبكات لنواة لينكس. يتم تشغيل الخطأ عند تمكين KFENCE وقيام مستدعٍ (تحديدًا bpf_test_init في net/bpf/test_run.c) بتخصيص مخزن رأس SKB عبر kzalloc() بحجم يساوي بالصدفة SKB_SMALL_HEAD_CACHE_SIZE. بسبب دلالات KFENCE للإبلاغ عن الحجم الدقيق، تقوم دالة skb_kfree_head() في النواة بتحرير الكائن بشكل غير صحيح إلى skb_small_head_cache بدلاً من التخزين المؤقت الأصلي kmalloc-1k، مما يؤدي إلى إتلاف بيانات تعريف slab.
| الحالة | النطاق |
|---|---|
| متأثر | لينكس >= 6.3 (أُدخل بواسطة bf9f1baa279f) |
| غير متأثر | < 6.3 |
| مُصلح | >= 6.12.82 |
| مُصلح | >= 6.18.23 |
| مُصلح | >= 6.19.13 |
| مُصلح | >= 7.0 (الخط الرئيسي، الالتزام 0f42e3f4fe2a) |
تم إدخال الثغرة بواسطة الالتزام bf9f1baa279f ("net: add dedicated kmem_cache for typical/small skb->head")، الذي أضاف skb_small_head_cache ومنطق التحرير الشرطي في skb_kfree_head().
skb_small_head_cacheيتم تعيين SKB_SMALL_HEAD_CACHE_SIZE عمدًا إلى قيمة ليست من قوى العدد 2 (مثل 704 بايت على x86_64) لتجنب التصادم مع أحجام مجموعات kmalloc العامة (دائمًا من قوى العدد 2: 512، 1024، ...). يستغل الاستدلال في skb_kfree_head() هذا التفرد لتوجيه عمليات التحرير باستخدام skb_end_offset فقط:
// net/core/skbuff.c (قابل للاستغلال — قبل الإصلاح)
static void skb_kfree_head(void *head, unsigned int end_offset)
{
if (end_offset == SKB_SMALL_HEAD_HEADROOM)
kmem_cache_free(net_hotdata.skb_small_head_cache, head);
else
kfree(head);
}
end_offset == SKB_SMALL_HEAD_HEADROOM → يُفترض أنه من skb_small_head_cache → kmem_cache_free()kfree() عامهذا الاستدلال سليم فقط في ظل دلالات slab العادية، حيث يُرجع ksize() حجم المجموعة (1024 لطلب 704 بايت)، والذي لا يساوي أبدًا SKB_SMALL_HEAD_CACHE_SIZE.
KFENCE (Kernel Electric-Fence) يعترض مجموعة فرعية من تخصيصات النواة ويخدمها من ذاكرة محمية بصفحات حراسة. الاختلاف السلوكي الحرج: يُرجع kfence_ksize() الحجم المطلوب بالضبط، وليس حجم مجموعة slab.
BPF_PROG_TEST_RUN (استدعاء النظام 321، الأمر BPF_PROG_TEST_RUN=10)
└─> __sys_bpf()
└─> bpf_prog_test_run_skb()
└─> bpf_test_init()
└─> kzalloc(size, GFP_USER)
│ size == SKB_SMALL_HEAD_CACHE_SIZE (704 على x86_64)
│ KFENCE يعترض → يُخدم الكائن من منطقة kmalloc-1k
│
└─> slab_build_skb(data, NULL, size)
└─> ksize(data)
└─> kfence_ksize() ← يُرجع 704 (بالضبط!)
└─> skb_end_offset
= ksize(data) - sizeof(skb_shared_info)
= 704 - 320
= 384
= SKB_SMALL_HEAD_HEADROOM ← تطابق خاطئ!
[على مسار تحرير SKB:]
└─> sk_skb_reason_drop()
└─> skb_release_data()
└─> skb_free_head()
└─> skb_kfree_head(head, skb->end)
└─> (end_offset == SKB_SMALL_HEAD_HEADROOM) == TRUE
└─> kmem_cache_free(skb_small_head_cache, head)
↑ خطأ: head من kmalloc-1k، وليس من skb_small_head_cache!
→ warn_free_bad_obj() → إتلاف SLUB
skb_end_offset = 384؟على x86_64:
SKB_SMALL_HEAD_CACHE_SIZE = 704 بايت
sizeof(skb_shared_info) = 320 بايت
SKB_SMALL_HEAD_HEADROOM = 704 - 320 = 384
عندما يعترض KFENCE طلب kzalloc() بحجم 704 بايت، يُرجع kfence_ksize() 704 بالضبط. ينتج الحساب skb_end_offset = 384 = SKB_SMALL_HEAD_HEADROOM، مما يحقق الشرط في skb_kfree_head() — مما يؤدي إلى تشغيل مسار التحرير الخاطئ.
الإصلاح المنبع من Jiayuan Chen (تمت مراجعته بواسطة Eric Dumazet، ودمجه بواسطة Jakub Kicinski) يلغي الاستدلال بالكامل:
// net/core/skbuff.c (مُصلح)
static void skb_kfree_head(void *head, unsigned int end_offset)
{
kfree(head); // دائمًا عام؛ يعمل لكلتا الحالتين
}
kfree() آمن لكل من الذاكرة المخصصة عبر kmalloc والذاكرة المخصصة عبر skb_small_head_cache لأن kmem_cache_free() على skb_small_head_cache لم يعد ضروريًا — يحل kfree() العام التخزين المؤقت الصحيح داخليًا عبر مؤشر kmem_cache لصفحة slab.
تم تشغيل أداة إعادة الإنتاج (repro_bpf.c) على لينكس 7.0.0-rc5 في بيئة QEMU (i440FX، BIOS 1.17.0-debian). لوحظت سلسلة تحذيرات النواة التالية:
[ 3065.322973] ------------[ cut here ]------------
[ 3065.322990] kmem_cache_free(skbuff_small_head, ffff888186d6e000): object belongs to different cache kmalloc-1k
[ 3065.323005] WARNING: mm/slub.c:6258 at warn_free_bad_obj+0x91/0xc0, CPU#0: repro_bpf/2167
[ 3065.323061] CPU: 0 UID: 0 PID: 2167 Comm: repro_bpf Not tainted 7.0.0-rc5 #1 PREEMPT(lazy)
[ 3065.323098] RIP: 0010:warn_free_bad_obj+0x98/0xc0
...
[ 3065.323231] Call Trace:
[ 3065.323247] skb_free_head+0x1ec/0x290
[ 3065.323267] skb_release_data+0x7a6/0x9d0
[ 3065.323308] bpf_prog_test_run_skb+0x14f8/0x3410
[ 3065.323510] __sys_bpf+0x769/0x4b60
[ 3065.323763] __x64_sys_bpf+0x78/0xc0
[ 3065.323794] do_syscall_64+0x111/0x690
[ 3065.323813] entry_SYSCALL_64_after_hwframe+0x77/0x7f
تنتج سلسلة التحذيرات 4 رسائل منفصلة لكل تشغيل:
warn_free_bad_obj — الكشف الأساسي عن التحرير عبر التخزين المؤقت (mm/slub.c:6258)depot_fetch_stack — فهرس تجمع مخزن المكدس خارج الحدود (lib/stackdepot.c:506) على تتبع Allocatedstack_depot_print — اكتشاف مقبض تالف (lib/stackdepot.c:780)depot_fetch_stack + stack_depot_print — نفس الزوج مكرر لتتبع Freedتشير هذه السلسلة إلى أن بيانات تتبع SLUB للكائن (alloc_track / free_track) تشير إلى مقبض مخزن مكدس يصبح تالفًا بعد التحرير في التخزين المؤقت الخاطئ.
النواة: لينكس >= 6.3، مُجمعة مع:
CONFIG_KFENCE=y
CONFIG_BPF_SYSCALL=y
CONFIG_NET_SCH_INGRESS=y (أو أي برنامج تشغيل يدعم SCHED_CLS)
CONFIG_SLUB_DEBUG=y (لرؤية warn_free_bad_obj)
CONFIG_STACKDEPOT=y (للسلسلة الكاملة)
الصلاحيات: الجذر (uid=0) — مطلوب لـ BPF_PROG_LOAD
gcc -O2 -o cve-2026-31429-poc-only cve-2026-31429-poc-only.c
sudo ./cve-2026-31429-poc-only
dmesg | grep -E "warn_free_bad_obj|Wrong slab cache|cross-cache"
يقوم PoC بتحميل برنامج BPF صغير من 3 تعليمات (نوع BPF_PROG_TYPE_SCHED_CLS):
ld_imm64 r0, 0 ; تعليمتان (واسعتان)
exit ; تعليمة واحدة
ثم يستدعي BPF_PROG_TEST_RUN (cmd=10) مع:
data_size_in = 284 بايت من بيانات الحزم المشتقة من Syzkallerflags = BPF_F_TEST_RUN_ON_CPU (0x4) — يثبت التنفيذ على CPU 0repeat = 4تختبر بيانات الإدخال ذات 284 بايت مسار التخصيص في bpf_test_init بحيث يساوي حجم المخزن المطلوب SKB_SMALL_HEAD_CACHE_SIZE، مما يصيب نافذة اعتراض KFENCE بشكل احتمالي.
| الالتزام | الشجرة | تم دمجه بواسطة | التاريخ |
|---|---|---|---|
0f42e3f4fe2a | الخط الرئيسي | Jakub Kicinski | 2026-04-06 |
60313768a8ed | linux-stable | Greg Kroah-Hartman | 2026-04-18 |
2d64618ea846 | linux-stable | Greg Kroah-Hartman | 2026-04-18 |
474e00b935db | linux-stable | Greg Kroah-Hartman | 2026-04-18 |
سلسلة التوقيع: Jiayuan Chen → Reviewed-by Eric Dumazet (Google) → Jakub Kicinski → Greg Kroah-Hartman
إشعار المُبلّغ: Antonius <[email protected]> في جميع الالتزامات الأربعة
أُدخل بواسطة: bf9f1baa279f ("net: add dedicated kmem_cache for typical/small skb->head")
.
├── README.md — هذا الملف
├── cve-2026-31429-poc-only.c — إثبات المفهوم فقط (ليس استغلالًا)
└── dmesg.txt — رسائل النواة الخام من إعادة الإنتاج الناجحة
| التاريخ | الحدث |
|---|---|
| ~أوائل 2026 | اكتشاف الخطأ عبر اختبار Syzkaller على لينكس 7.0-rc5 |
| 2026-04-03 | كتابة التصحيح بواسطة Jiayuan Chen، مع إشعار Reported-by لـ Antonius |
| 2026-04-06 | دمج التزام الخط الرئيسي 0f42e3f4fe2a بواسطة Jakub Kicinski |
| 2026-04-18 | دمج النقل الخلفي المستقر بواسطة Greg Kroah-Hartman (6.12.x، 6.18.x، 6.19.x) |
| 2026-04-20 | نشر CVE-2026-31429 |
Antonius (اللقب: w1sdom)
المؤسس وكبير الباحثين — Blue Dragon Security
إندونيسيا
[email protected]
تم إصدار هذا PoC لأغراض تعليمية وبحثية بعد توفر التصحيح المنبع. لا تستخدمه على أنظمة لا تملكها أو ليس لديك إذن صريح لاختبارها. لا يتحمل المؤلف أي مسؤولية عن سوء الاستخدام.