
تحليل لثغرة استخدام بعد التحرير (UAF) في نواة لينكس الخاصة بـ CVE-2026-64560 مع كود إثبات المفهوم (PoC) المُحفِّز للسباق، ومراجعة التصحيح، ومصفوفة الإصدارات المتأثرة من LTS/أندرويد، وفحص ذاتي للأجهزة المحدّثة.
مُعيد الإنتاج / PoC (للتحقق من حدوث الثغرة): Linux وAndroid (NDK) يُستخدم هذا المستودع فقط للتحقق من حالة التصحيح ولأغراض البحث والتعلّم على أجهزة الاختبار الخاصة بك، ولا يحتوي على أي بدائيات تصعيد صلاحيات أو استغلال.
| الحقل | المحتوى |
|---|---|
| CVE ID | CVE-2026-64560 |
| العنوان | posix-cpu-timers: Prevent UAF caused by non-leader exec() race |
| النوع | استخدام بعد التحرير (Use-After-Free) (CWE-416)، حالة سباق (race condition) |
| CNA | kernel.org (Linux CNA) |
| CVSS v3.1 | 7.8 High — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| CVSS v4.0 (SUSE) | 8.5 High — CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N |
| EPSS | ~0.12% (المئين الثاني، عند 2026-08) |
| CISA KEV | غير مُدرج |
| تاريخ الإفصاح | 2026-07-29 |
| التزام الإصلاح (mainline) | 920f893f735e92ba3a1cd9256899a186b161928d |
| الالتزام المُسبِّب للمشكلة (Fixes:) | 55e8c8eb2c7b (v5.7, 2020) — "posix-cpu-timers: Store a reference to a pid not a task" |
| المُصلِح | Thomas Gleixner <[email protected]> |
| المُبلِّغ | Wongi Lee <[email protected]>, Jungwoo Lee <[email protected]> |
| الملفات المتأثرة | kernel/exit.c, kernel/signal.c, kernel/time/posix-cpu-timers.c |
أُدخلت الثغرة في v5.7 (2020-05)، ونُقل الإصلاح (backport) إلى فروع stable المختلفة:
نواة Android GKI مبنية على 5.10 / 5.15 / 6.1 / 6.6 / 6.12 LTS، وجميعها ضمن النطاق المتأثر. نظرًا لأن التزام الإصلاح الرئيسي (mainline) صدر في 2026-07-29، فإن أجهزة Android التي يكون مستوى التصحيح الأمني (SPL) فيها 2026-08-01 أو قبل ذلك لا تتضمن هذا الإصلاح في الغالب. يمكنك التأكد من إصدار النواة وSPL على الجهاز باستخدام adb shell cat /proc/version وgetprop ro.build.version.security_patch.
تُدار موقّتات POSIX CPU (timer_create(CLOCK_PROCESS_CPUTIME_ID, ...) / timer_create(CLOCK_THREAD_CPUTIME_ID, ...)) داخل النواة عبر kernel/time/posix-cpu-timers.c. يحتفظ كل k_itimer بالمهمة المستهدفة من خلال it.cpu.pid؛ وعند التعامل مع الموقّت يلزم استدعاء lock_task_sighand(p, &flags) للحصول على sighand->siglock الخاص بتلك المهمة لحماية timerqueue.
الالتزام 55e8c8eb2c7b لعام 2020 استبدل مؤشر task المخزَّن مؤقتًا في الموقّت بـ مرجع pid (لإصلاح مشكلة سبّبها workaround عام 2010 e0a70217107e)، مع إجراء بحث pid_task(pid, type) قبل كل عملية. ترك هذا التغيير نافذة السباق (race window) الخاصة بهذه الثغرة.
عندما يتم استدعاء execve() بواسطة خيط غير قائد (non-leader thread)، يؤدي de_thread() → switch_leader() إلى نقل TGID من القائد القديم إلى القائد الجديد، ويمرّ القائد القديم عبر release_task() → __exit_signal()، حيث يتم تعيين old_leader->sighand = NULL واستدعاء unhash_task(old_leader).
في الوقت نفسه، على نواة معالجة (CPU) أخرى، يُنفَّذ sys_timer_delete() → posix_cpu_timer_del():
sys_timer_delete() exec()
posix_cpu_timer_del()
// 观察到旧 leader
p = pid_task(pid, pid_type); de_thread()
switch_leader();
release_task(old_leader)
__exit_signal(old_leader)
sighand = lock(old_leader, sighand);
posix_cpu_timers*_exit();
sighand = lock_task_sighand(p) unhash_task(old_leader);
sh = lock(p, sighand) old_leader->sighand = NULL;
unlock(sighand);
(p->sighand == NULL)
unlock(sh)
return NULL;
// 直接返回,没有摘链!
if (!sighand)
return 0;
free_posix_timer(); // ← k_itimer 被释放
إن p الذي يعثر عليه posix_cpu_timer_del() هو القائد القديم، وعند هذه النقطة يكون p->sighand == NULL، فتعتبر الدالة أن «المهمة في طور الخروج وأن مسار exit سيتكفل بفكّ الارتباط»، وبالتالي تعود بالنجاح دون أن تفعل أي شيء. بعد ذلك يُحرِّر free_posix_timer() كائن k_itimer.
النقطة الأساسية: يختلف exec() عن exit() — ففي exec() لا يتغير TGID، والموقّتات المفعلة (armed) المرتبطة بمستوى العملية (p->signal->cpu_timers) تُورَّث وتبقى في قائمة الانتظار. وبالتالي:
run_posix_cpu_timers() (الذي يجتاز timerqueue في كل tick) يصل إلى timerqueue_node لكائن مُحرَّر → قراءة/كتابة UAF؛مشكلات مماثلة موجودة أيضًا في:
posix_cpu_timer_set(): بالنسبة إلى الموقّت العادي فإنها تُرجع -ESRCH مؤقتًا فقط؛ لكن do_cpu_nanosleep() داخل النواة تستخدم k_itimer مُخَصَّصًا على المكدس (stack)، وهي نفس مشكلة UAF.posix_cpu_timer_rearm(): فشل صامت في إعادة التسليح (rearm)، فلا يعود الموقّت ينقضي (خطأ وظيفي).أشار Frederic Weisbecker إلى أن tsk->sighand = NULL داخل __exit_signal() هو تخزين عادي (plain store)؛ وعلى البنى ضعيفة الترتيب مثل ARM64، عندما يرصد posix_cpu_timer_del() أن sighand == NULL فإنه ليس مضمونًا أن يرصد أيضًا عمليات الكتابة الخاصة بفكّ الارتباط التي سبقت posix_cpu_timers*_exit()، مما قد يؤدي إلى إنذار خاطئ من WARN_ON_ONCE(timer_queued(tmr)).
__exit_signal() يتم التعديل إلى smp_store_release(&tsk->sighand, NULL)؛smp_acquire__after_ctrl_dep() في مسار !sighand داخل lock_task_sighand()؛timer_lock_sighand(): تبحث عن المهمة + تقفل sighand، وإذا كان sighand == NULL فإنها لا تعود بل تعيد محاولة البحث — في سيناريو exec ستجد القائد الجديد، ولا تستسلم إلا في سيناريو exit عندما يفشل البحث؛_del / _set / _rearm) تتحول جميعها إلى استخدام هذه الدالة المساعدة بشكل موحّد.انظر الـ diff الكامل في patches/920f893f735e.patch.
يوفر مجلد poc/ مُحفِّز سباق (race trigger): خيطان يعمل كل منهما في حلقة عالية الكثافة
timer_create(CLOCK_PROCESS_CPUTIME_ID) → تسليح (arm) (بزمن انتهاء ابتدائي قصير جدًا) → انتظار مشغول (busy-wait) لحدوث الإثارة → timer_delete()؛fork() → داخل العملية الابنة إنشاء خيط غير قائد، يقوم هذا الخيط باستدعاء execve() (exec من خيط غير قائد شرط ضروري لهذه الثغرة)، وتقوم العملية الأم فورًا باستدعاء waitpid() لاستعادة (reap) العملية.عندما يتزامن timer_delete() مع de_thread()/__exit_signal() في السباق، يبقى k_itimer المُحرَّر مرتبطًا بشجرة rbtree الخاصة بـ signal->cpu_timers في نواة غير مُصحَّحة، وبعد ذلك تصل run_posix_cpu_timers() أو عمليات timerqueue أخرى إلى العقدة المعلّقة. مع نواة KASAN يمكن ملاحظة تقارير مثل BUG: KASAN: use-after-free in run_posix_cpu_timers / timerqueue_del بشكل موثوق؛ وبدون KASAN تظهر عادةً كتحذيرات نواة متقطعة أو panic.
توضيح الطبيعة: هذا مُحفِّز سباق مكتوب بلغة C خالصة، لا يتضمن أي بدائيات استغلال مثل heap spray أو وضع كائنات بديلة (object placement) أو التحكم في RIP. تحويله إلى exploit لتصعيد الصلاحيات يتطلب قدرًا كبيرًا من العمل الإضافي (هندسة الكومة heap feng shui، كائنات بديلة في slab cache حيث يوجد
k_itimer، تجاوز KASLR/CFI، إلخ)، ويرتبط ارتباطًا وثيقًا ببناء النواة المحدد. هذا المستودع لا يتضمن هذا الجزء عمدًا.
├── README.md ← 本文
├── patches/
│ └── 920f893f735e.patch ← mainline 修复补丁全文
└── poc/
├── cve_2026_64560_poc.c ← 触发器源码(Linux/Android 通用)
├── Makefile ← Linux / NDK 交叉编译
└── Android.mk ← NDK ndk-build(可选)
cd poc
make # 生成 cve_2026_64560_poc
sudo ./cve_2026_64560_poc -d 60
# 观察 dmesg: sudo dmesg -wH | grep -iE 'kasan|use-after|BUG|WARNING'
cd poc
export ANDROID_NDK_HOME=/path/to/ndk
make android # 生成 cve_2026_64560_poc_arm64(static, pie)
adb push cve_2026_64560_poc_arm64 /data/local/tmp/cvepoc
adb shell chmod 755 /data/local/tmp/cvepoc
adb shell /data/local/tmp/cvepoc -d 120
# 观察内核日志:
adb shell su 0 dmesg -w | grep -iE 'kasan|use-after|BUG|WARNING|timer'
# 无 root 时也可在触发崩溃后用 adb shell cat /sys/fs/pstore/console-ramoops* 查看
يجب أن يستوفي الجهاز الشروط التالية:
CLOCK_PROCESS_CPUTIME_ID يستهدف TGID → يُربط بـ signal->cpu_timers ويُورَّث بعد exec — وهذا هو الشرط المسبق لـ UAF (CLOCK_THREAD_CPUTIME_ID لا يصلح)؛switch_leader() القائد القديم الذي يعيده pid_task(TGID) يصبح sighand = NULL فورًا — وهذا شرط ضروري للسباق؛timer_create/arm/delete بتردد عالٍ وfork/exec بتردد عالٍ بالتوازي يزيد إلى أقصى حد من احتمال وقوع posix_cpu_timer_del() داخل النافذة؛ ومعالجة انتهاء صلاحية الموقّت (run_posix_cpu_timers) نفسها تلمس العقدة المعلّقة أيضًا، فلا حاجة إلى مُحفِّز إضافي.# Android:
adb shell cat /proc/version # 内核版本是否 >= 上表修复版本
adb shell getprop ro.build.version.security_patch # SPL 是否 > 2026-08
# Linux:
uname -r
# 或直接检查源码是否包含 timer_lock_sighand:
grep -r timer_lock_sighand /usr/src/linux/kernel/time/posix-cpu-timers.c
إذا شغّلت الـ PoC لبضع دقائق دون أي KASAN/panic وكانت النواة ≥ إصدار الإصلاح، فيُعتبر الجهاز مُصحَّحًا (يتضمن الـ PoC نفسه وضع --check لإجراء اختبار دخان (smoke) خفيف).
هذا المستودع موجّه فقط للبحث الأمني والتحقق الدفاعي. شغّله فقط على الأجهزة التي تملكها أو التي حصلت على تفويض كتابي لاستخدامها. قد يؤدي الـ PoC إلى عدم استقرار النواة وحتى panic، لذا لا تشغّله على أجهزة الإنتاج. لا يتحمل المؤلف أي مسؤولية عن العواقب الناتجة عن إساءة الاستخدام.
| الفرع | المتأثر | الإصدار المُصحَّح (≥) | التزام الإصلاح في stable |
|---|
| 5.10 LTS | 5.7 ~ 5.10.261 | 5.10.262 | 67aa823e3e8c |
| 5.15 LTS | ~ 5.15.212 | 5.15.213 | d8bcb28abad8 |
| 6.1 LTS | ~ 6.1.179 | 6.1.180 | cc35ddbc4973 |
| 6.6 LTS | ~ 6.6.146 | 6.6.147 | 12a891c773ae |
| 6.12 LTS | ~ 6.12.99 | 6.12.100 | e74443f5db00 |
| 6.18 | ~ 6.18.40 | 6.18.41 | 6a7ecc25abe6 |
| 7.1 | ~ 7.1.4 | 7.1.5 | ad1cafa1bdaa |
| mainline | < 7.2-rc3 | 7.2-rc3 | 920f893f735e |