组件: IOGPUFamily 内核扩展(com.apple.iokit.IOGPUFamily)
受影响版本: iOS / iPadOS 26.6 (23G71) 及更早版本
修复版本: iOS / iPadOS 26.6.1 (23G83)
类型: IOGPUDevice::create_resource_iosurface 中的释放后使用(Use-After-Free)
影响: 内核内存破坏;已在 A14(iPhone 12)上确认 UAF 解引用
发现者:f00l (@PPPF00L)、3ndy1 (@_3ndy1)、Minghao Lin (@Y1nkoc)、云散花折、Arjanit Isufi
(依据 Apple 安全公告 — iOS 26.6.1)
IOGPUDevice::create_resource_iosurface 分配一个 IOGPUSysMemory 对象(约 0x100 字节,kalloc.256 区域)并将其注册到设备的 IOSurface 资源表中。在返回之前,通过 ADDS width+height → b.hs 触发维度溢出检查。溢出路径分支到错误返回处,但未释放该对象,导致资源表中留下一个指向已释放内存的活动条目。
create_resource_iosurface:
kalloc(0x100) → obj ; 分配 IOGPUSysMemory
table[new_id] = obj ; 注册并持有引用
ADDS w_result, w_width, w_height
b.hs error_path ; ← 溢出:跳转至此
error_path:
; 对象未释放,表条目未清除
return kIOReturnInvalid
该过期表条目会一直保留,直到相同的资源 ID 被重用或设备被关闭。
IOGPUDevice::set_resource_purgeable(id) 通过 get_resource_by_id() 查找过期的 ID,发现悬空条目,并对其进行非虚拟访问:
该 PoC 使用 Metal 的 newTextureWithDescriptor:iosurface:plane: 并构造溢出的维度来触发 UAF。在 UAF 解引用之前:
kalloc.256 槽位[+0x10]=0、[+0x24]=0、[+0x28]=0 → Call2 和 Call3 被安全跳过触发检测: 对不存在的资源调用 set_resource_purgeable 返回 0xe00002c2。在 UAF + 喷洒回收该槽位后,相同调用返回 0xe0002be(找到资源,Call1 触发)——确认过期条目正在使用我们的喷洒数据进行处理。
iOS 26 中的区域隔离(zone sequestration)会将已释放的 IOGPUSysMemory 对象放回类型稳定的区域空闲列表中。[obj+0x10](IOMemoryDescriptor)和 [obj+0x18](IOGPUDevice)都持有外部引用计数,并在对象被释放后仍然有效。因此,completeMemory() 始终在有效的 ivar 数据上执行——UAF 解引用已确认,但在 iOS 26 上仅通过此路径无法获得内核读写原语,除非有辅助原语来打破区域隔离。
clang -arch arm64 -framework Metal -framework IOSurface \
-framework IOKit -framework Foundation \
-o poc poc/poc_iogpu_uaf.m
或在 Xcode 中打开,设置有效的签名团队,然后在设备上运行。
| 日期 | 事件 |
|---|---|
| 2026-08-17 | 发布包含修复的 iOS 26.6.1 |
| 2026-08-17 | Apple 在安全公告中发布致谢 |
| 偏移量 | 操作 | 描述 |
|---|
+0x010c | ldaddl w9(-4), w8, [obj+0x24] | 对 [obj+0x24] 进行原子递减;旧值 → w8 |
+0x0128 | cbz w8, skip; ldr x1, [obj+0x28] | 如果旧 [+0x24] != 0:将 [obj+0x28] 作为参数加载到 IOGPUMemory::setAllocation |
+0x0134 | ldr x0, [obj+0x10] | 将 [obj+0x10] 作为 IOCommandGate 参数加载;若为 0 则 cbz → skip |