
تحليل سباق CVE-2026-43805 IOKit IODMACommand وإثبات المفهوم
تحليل يكتشف القفل المفقود لحالة IODMACommand::PerformOperation_Impl في Apple IOKit
عبر مقارنة ثنائيات نواة macOS 26.5/26.6. في البنية المصابة، يمكن لـ
CompleteDMA تحرير نفس الحالة بينما يستخدم PerformOperation حالة الاستعداد
ووصف الذاكرة. تطبق رقعة 26.6 القفل fDextLock، الذي كان المساران يتشاركانه
بالفعل، على PerformOperation_Impl أيضًا.
حالة التحقق
تم التحقق من مقارنة الثنائيات، وأهداف الاستدعاء، وربط مصدر XNU العام، ونموذج السباق الحتمي. تمت كتابة مشغل DriverKit الأصلي في المستودع في بيئة Linux/WSL ولم يخضع بعد لبناء Xcode ولا لتشغيل A/B على macOS مصاب/مصحح. لذلك لا يدّعي هذا المستودع حاليًا أنه لاحظ panic فعليًا في النواة أو كتابة kernel write قابلة للتحكم.
صنّفت Apple هذه المشكلة كـ race condition في IOKit، وشرحت أن تطبيقًا محليًا قد يتسبب في إيقاف نظام غير متوقع أو كتابة في ذاكرة النواة. المُبلِّغ هو 이재영. إشعار أمان Apple macOS Tahoe 26.6، إشعار أمان Apple iOS/iPadOS 26.6
| المنتج | أول إصدار مصحح | زوج المصاب/المصحح المستخدم في التحليل |
|---|---|---|
| iOS / iPadOS | 26.6 | iPhone 11: 26.5.2 (23F84) → 26.6 (23G71) |
| macOS Tahoe | 26.6 | KDK 26.5 (25F71) → KDK 26.6 (25G70) |
| macOS Sequoia | 15.7.8 | تم التحقق من الإصدار المصحح فقط عبر الإشعار |
| macOS Sonoma | 14.8.8 | تم التحقق من الإصدار المصحح فقط عبر الإشعار |
| watchOS | 26.6 | تم التحقق من الإصدار المصحح فقط عبر الإشعار |
يمكن التحقق من الإصدارات المصححة لـ Sequoia وSonoma من إشعار Apple 15.7.8 و إشعار Apple 14.8.8 على التوالي، ومن إشعار Apple 26.6 لـ watchOS.
في 2026-09-27، تم البحث بدمج معرّف CVE مع PoC وexploit وGitHub وIOKit
واسم المُبلِّغ، لكن لم يتم العثور على كود إعادة إنتاج عام أو تحليل للسبب الجذري.
هذه الجملة هي ملاحظة وقت البحث ولا تعني أن PoC عامًا لا يمكن أن يوجد مطلقًا.
نظرًا لعدم نشر مصدر XNU المقابل للإصدار المصحح، تمت مقارنة نواة KDK بدلًا من
مقارنة commits المصدر. الجانب المصاب يتوافق تمامًا مع
xnu-12377.121.6
الذي نشرته Apple، لكن وسم xnu-12377.161.13 للجانب المصحح لم يكن موجودًا في
تاريخ التحليل. KDK هو أثر تصحيح ورموز نواة توفره Apple، وتوصي Apple أيضًا
باستخدام KDK المطابق للإصدار. دليل Apple KDK
الملفات المستخدمة في المقارنة غير مضمنة في المستودع.
| التصنيف | الملف | SHA-256 |
|---|---|---|
| KDK DMG مصاب | Kernel_Debug_Kit_26.5_build_25F71.dmg | 90ed319cd1ba6e23d1eefcee89fa1b10743f4cf60b85208ae52aa9f45543c7aa |
| KDK DMG مصحح | Kernel_Debug_Kit_26.6_build_25G70.dmg | edaa0b3d431b43ba7dda2f0e82314b19d5476ba4089a7d8c1de18c49b3ad168b |
| نواة x86_64 مصابة | XNU 12377.121.6~2 | f4dcaa7e59ae59f3d945c2332862772596b71e9823cc202983ba4f9d897b5bec |
| نواة x86_64 مصححة | XNU 12377.161.13~4 | 754db65cb15599183ccebfaa2e4909f673de5d7da42ba4293256cb1184587efd |
| kernelcache iOS مصاب | XNU 12377.122.8~1 | 7bd856548a2d170a5f8c962e9a5740290e5a4ba9a6ac39c96c927d4dd400d890 |
| kernelcache iOS مصحح | XNU 12377.162.13~2 | 680fa3925641e2b81541c8455fc6d8d432acad9f059ac44bb48324791049c9d6 |
يقرأ tools/diff_macho_symbols.py رموز Mach-O
وأقسام التنفيذ، ويستعيد أسماء C++، ثم يوحّد أهداف التفرع والاستدعاء والعناوين
النسبية لـ RIP ويقارن تعليمات كل دالة. من بين 19 دالة معدّلة تحمل أسماء IOKit،
وبعد استبعاد تغيرات أرقام الأسطر التشخيصية، تطابق تغير تدفق التحكم في
IODMACommand::PerformOperation_Impl مع "improved state handling" الوارد في
الإشعار.
IODMACommand::PerformOperation_Impl(...)
vulnerable: 0xffffff8000a39080, 512 bytes
fixed: 0xffffff8000a3b860, 544 bytes
normalized similarity: 0.621429
يظهر الاستدعاء التالي مُضافًا في النواة المصححة.
mov rax, [r14 + 0x70] ; IODMACommand::reserved
mov rdi, [rax + 0xc0] ; IODMACommandInternal::fDextLock
call IOLockLock
; fActive 확인 및 readBytes/writeBytes 작업
mov rax, [r14 + 0x70]
mov rdi, [rax + 0xc0]
call IOLockUnlock
بالنظر إلى المصدر العام المصاب، فإن
PrepareForDMA_Impl و
CompleteDMA_Impl
يأخذان fDextLock بالفعل. في المقابل، فإن
PerformOperation_Impl
يفحص fActive ثم يستخدم fMemory وreadBytes وwriteBytes دون القفل نفسه.
sequenceDiagram
participant A as Worker A: PerformOperation
participant C as IODMACommand state
participant B as Worker B: CompleteDMA
A->>C: fActive == true 확인
A->>A: 임시 버퍼 할당/준비
B->>C: fDextLock 획득
B->>C: complete(), fActive 감소
B->>C: clearMemoryDescriptor(), fMemory 해제
B-->>C: fDextLock 해제
A->>C: readBytes/writeBytes에서 fMemory 사용
Note over A,C: 상태 검사와 사용 사이 TOCTOU
إذا نُفِّذ CompleteDMA_Impl بين الفحص والاستخدام في PerformOperation_Impl،
فلن تظل حالة الاستعداد التي تم التحقق منها أولًا صالحة. fMemory هو
OSSharedPtr<IOMemoryDescriptor>، لكن هذا المسار لا يحصل على مرجع أو قفل
منفصل لحماية العملية بأكملها. ونتيجة لذلك، يمكن الاستمرار في استخدام حالة
ذاكرة DMA محررة أو مستبدلة.
بعد الرقعة، تُسلسِل استدعاءات DMA RPC الثلاثة انتقالات الحالة بالقفل نفسه.
flowchart LR
P[PrepareForDMA] -->|fDextLock| S[(fActive / fMemory)]
O[PerformOperation] -->|fDextLock 추가| S
C[CompleteDMA] -->|fDextLock| S
يذكر إشعار Apple كتابة ذاكرة النواة كتأثير محتمل، لكن مقارنة الثنائيات وحدها لا تُثبت التحكم المستقر في عنوان الكتابة وقيمتها. ما يمكن تأكيده هنا هو سباق العمر/حالة الاستعداد وإضافة القفل الذي يمنعه.
يعيد poc/model/race_model.cpp إنتاج التداخل
الإشكالي دون استدعاء واجهات النواة. في المسار المصاب، يفرض أن يحرر خيط دورة
الحياة الذاكرة مباشرة بعد فحص الحالة، وفي المسار المصحح يتحقق مما إذا كان
القفل نفسه يؤخر التحرير.
make model
المخرجات التي تم التحقق منها:
vulnerable path: stale memory access = observed
fixed path: stale memory access = not observed
هذا النموذج هو PoC مساعد للتحقق من مبدأ الرقعة، وليس نتيجة الاصطدام الفعلية لثغرة نواة Apple.
يعتمد poc/apple-driverkit-sample على
عينة DriverKit user-client
الرسمية من Apple. يكرر selector 6 المُضاف ما يلي في طابورَي DriverKit dispatch
مستقلين.
Worker A: IODMACommand::PerformOperation(Zero, ...)
Worker B: IODMACommand::CompleteDMA()
IODMACommand::PrepareForDMA(...)
يقوم PerformOperation(Zero) بإعداد مخزن مؤقت كبير ثم الكتابة في ذاكرة DMA،
مما يوسّع نافذة السباق بين فحص fActive وwriteBytes الفعلي. القيم الافتراضية
هي 1 MiB و10,000 تكرار، ويمكن ضبطها من SwiftRunDMACommandRace() في التطبيق.
نظرًا لاحتمال حدوث kernel panic، شغّله فقط على جهاز Mac اختبار منفصل لا يحتوي على أعمال غير محفوظة أو على وحدة تخزين اختبار قابلة للتراجع.