
أداة إعادة إنتاج السباق واختبار الإجهاد لـ CVE-2026-52910، وهو use-after-free في نواة Linux ضمن برامج محدد cBPF الخاصة بـ reuseport، مع فحوصات dmesg والتسريب.
أداة إعادة إنتاج سباق (race) ومجموعة اختبار إجهاد لـ CVE-2026-52910، وهو use-after-free (UAF) في معالجة نواة Linux لـ برامج محدِّد classic BPF (cBPF) الخاصة بـ reuseport، تم إصلاحه في المنبع بواسطة الالتزام "bpf: Free reuseport cBPF prog after RCU grace period".
| CVE | CVE-2026-52910 |
| النوع | Use-after-free / قراءة خارج الحدود (CWE-125)، CVSS 3.1 7.8 HIGH AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| أُدخل في | v4.5 (مع دعم reuseport cBPF) |
| أُصلح في | 5.10.259, 5.15.210, 6.1.176, 6.6.143, 6.12.94, 6.18.36, 7.0.13 (stable); mainline v7.1 |
| تقرير المنبع | BUG: KASAN: vmalloc-out-of-bounds in reuseport_select_sock (net/core/sock_reuseport.c:596) |
| أبلغ عنه | Eulgyu Kim |
[!WARNING] هذه أداة إجهاد للنواة. على نواة مصابة فإنها توسّع عمداً نافذة سباق use-after-free؛ أي إصابة قد تُسقط الجهاز أو تُفسده. شغّلها فقط على أجهزة تملكها أو لديك تفويض صريح باختبارها (أجهزة اختبار افتراضية، أجهزة CI مؤقتة)، وليس أبداً على أنظمة الإنتاج.
SO_REUSEPORT يسمح لعدة sockets بربط نفس منفذ UDP؛ ولكل حزمة واردة
تختار النواة أحد sockets المجموعة في
reuseport_select_sock() (net/core/sock_reuseport.c). يمكن للمجموعة تثبيت
برنامج محدِّد (selector program) — وهو برنامج classic BPF يُرفق عبر
setsockopt(SO_ATTACH_REUSEPORT_CBPF) — يقرر لكل حزمة أي socket
من المجموعة يستقبلها. يُنفَّذ البرنامج في RX softirq (معالجة استقبال الشبكة)
داخل قسم حرج للقراءة من RCU.
الخلل: عند استبدال البرنامج أو فصله عبر setsockopt()
(reuseport_attach_prog() / reuseport_detach_prog())، يُحرَّر برنامج cBPF القديم
فوراً بواسطة sk_reuseport_prog_free()، دون انتظار قُرّاء RCU الجاريين.
وحدة معالجة مركزية لا تزال تمرّ على تعليمات البرنامج المحرَّر تقرأ ذاكرة
vmalloc محرَّرة:
sequenceDiagram
autonumber
participant C as CPU0 — churner thread
participant K as setsockopt() path
participant R as CPU1 — RX softirq
R->>R: rcu_read_lock()
R->>R: prog = rcu_dereference(reuse->prog)
C->>K: setsockopt(SO_ATTACH_REUSEPORT_CBPF, progB)
K->>K: swap progA → progB
K->>K: sk_reuseport_prog_free(progA)
Note right of K: unfixed kernels: bpf_prog_free()<br/>runs NOW — no RCU grace period
R->>R: execute progA->insns (run_bpf_filter)
Note right of R: progA was already freed<br/>KASAN: vmalloc-out-of-bounds
Note over K: fix: call_rcu(sk_reuseport_prog_free_rcu) —<br/>free deferred by one RCU grace period
مسار محدِّد eBPF (SO_ATTACH_REUSEPORT_EBPF) غير متأثر: فهو
يحرّر البرامج بالفعل عبر مراحل bpf_prog_put() المؤجلة. يمنح الإصلاح
مسار cBPF المعاملة نفسها — فترة سماح RCU واحدة قبل تحرير البرنامج القديم.
تقرير KASAN من المنبع (على نواة تصحيح 7.0):
BUG: KASAN: vmalloc-out-of-bounds in reuseport_select_sock+0xedc/0x1220
Read of size 4 at addr ffffc9000051e004 by task slowme/10208
net/core/sock_reuseport.c:596
| الملف | الغرض |
|---|---|
reuseport_race_hammer.c | مُعيد الإنتاج: أداة hammer متعددة الخيوط تُقلّب محدِّد cBPF تحت حمل UDP كامل، ثم تتحقق من سلامة التسليم. |
run_hammer.sh | غلاف تشغيل واحد: يرفع net.core.optmem_max، يشغّل الـ hammer، ثم يفحص dmesg بحثاً عن splats، و/proc/vmallocinfo بحثاً عن تخصيصات bpf_prog المسرَّبة، واختيارياً kmemleak. |
livepatch_cycle.sh | يطبّق/يتراجع عن livepatch يحمل الإصلاح أثناء تشغيل الـ hammer — يبحث عن مخاطر دورة حياة livepatch في الإصلاح القائم على call_rcu(). |
Makefile | يبني الـ hammer. |
.github/workflows/ci.yml | CI: بناء + shellcheck (لا اختبارات نواة وقت التشغيل؛ انظر CI). |
على جهاز اختبار Linux:
$ make
$ sudo ./run_hammer.sh 600 # 10-minute run
...
== result: RC=0 (0 clean / 1 setup / 2 splat / 3 leak / 4 integrity) ==
المتطلبات:
gcc وbash.dmesg، /proc/vmallocinfo، sysctl)؛
الـ hammer نفسه يعمل بدون صلاحيات (شُغّل مُعيد إنتاج المنبع بمعرّف UID 1000).النتائج المتوقعة:
RC=2؛ وأحياناً يشتعل فحص
سلامة الـ hammer أولاً → RC=4.RC=0، عدد vmalloc لـ bpf_prog مستقر.نافذة السباق ضيقة جداً (التحرير مقابل تنفيذ RX الجاري)، لذا اعتبر تشغيلاً نظيفاً واحداً غير حاسم. للاختبار الحقيقي شغّله ساعات، مثلاً:
$ sudo ./run_hammer.sh 86400 512 8 16 4 127.0.0.1 0
reuseport_race_hammer$ ./reuseport_race_hammer [dur_sec] [insns] [nports] [nsocks] [nsenders] [ip] [ebpf]
| المعامل | الافتراضي | المعنى |
|---|---|---|
dur_sec | 300 (min 45) | إجمالي ثواني التشغيل |
insns | 256 | تعليمات حشو في برنامج cBPF المُقلَّب؛ البرنامج الأكبر يعني منطقة محرَّرة أكبر للإصابة. إذا فشل الإرفاق بـ ENOMEM، ارفع net.core.optmem_max (الغلاف يفعل هذا لك). |
nports | 4 (max 64) | مجموعات reuseport (منفذ UDP واحد لكل منها، من 21000) |
nsocks | 8 (max 512) | sockets لكل مجموعة |
nsenders | 4 (max 32) | خيوط مُرسِل UDP لكل مجموعة |
ip | 127.0.0.1 | العنوان الهدف؛ استخدم عنوان NIC فعلي لتوزيع RX softirqs عبر وحدات المعالجة (RSS) |
ebpf | 0 | 1 = أيضاً قلّب إرفاق/فصل SO_ATTACH_REUSEPORT_EBPF (يحتاج CAP_BPF/CAP_NET_ADMIN)؛ هذا المسار غير مصاب، وهذا للمقارنة/التغطية |
تشغّل كل مجموعة خيط churner واحداً يبدّل ويفصل محدِّد cBPF
عبر setsockopt() في حلقة محكمة، وخيوط sender تُغرق المجموعة
بحزم UDP بحجم 64 بايت بينما يعدّ المستقبلون التسليم لكل socket.
مراحل التشغيل (T = dur_sec):
time ──────────────────────────────────────────────────────────────►
[0 ──────────── T-15s) [T-15s ── T-10s) [T-10s ─────────── T]
CHURN + FLOOD SETTLE MEASURE
churner swaps/detaches churn frozen, deterministic program
the selector prog at final program (selects the LAST
max rate under full attached socket): EVERY packet
UDP flood — THE (selects LAST must land on the LAST
race window open socket) socket; snapshot A →
run → snapshot B
فحص السلامة: خلال مرحلة القياس يختار البرنامج النهائي بشكل حتمي آخر socket في المجموعة. إذا لم تهبط كل الحزم التي استقبلتها المجموعة خلال تلك النافذة على ذلك الـ socket، فقد أخطأ الاختيار (تأثير UAF محتمل حتى بدون KASAN) → رمز الخروج 2.
رموز خروج الـ hammer: 0 نجاح · 1 خطأ إعداد/وقت تشغيل · 2 تحذير سلامة.
run_hammer.sh — غلاف تشغيل واحديشغّل الـ hammer ويضيف الفحوصات التي تجعل تشغيلاً واحداً ذا معنى:
bpf_prog في /proc/vmallocinfo؛net.core.optmem_max حتى تُرفق برامج cBPF متعددة الكيلوبايت بنظافة؛DRAIN (افتراضياً 30 ثانية) حتى تنتهي عمليات التحرير المؤجلة لـ RCU/workqueue؛BUG:، Oops:، WARNING:، RIP:،
leaked، stuck)؛bpf_prog قبل/بعد (فحص التسريب)، و
اختيارياً يفحص kmemleak إذا وُجد /sys/kernel/debug/kmemleak.| متغير البيئة | الافتراضي | المعنى |
|---|---|---|
HAMMER | ./reuseport_race_hammer | ملف الـ hammer التنفيذي |
DRAIN | 30 | ثواني الانتظار بعد التشغيل قبل الفحص |
OPTMEM_MAX | 131072 | قيمة net.core.optmem_max؛ 0 = لا تلمسها |
رموز الخروج: 0 نظيف · 1 خطأ إعداد (بما في ذلك فشل إعداد الـ hammer) ·
2 splat في النواة · 3 تسريب bpf_prog محتمل · 4 تحذير سلامة
من الـ hammer.