遗憾的是,即便已经处于开发周期末尾、即将发布Linux 7.1稳定版,Linux 7.1内核开发领域依旧忙乱,各项工作都没能顺利进入收尾平稳期。本周提交的内核图形/加速器驱动DRM拉取请求中,修复内容依然很多,此外由于去年合入的相关代码持续存在安全隐患,最终还禁用了一个ioctl接口。
红帽公司的戴维·艾尔利(David Airlie)在周五提交给Linux 7.1的DRM修复拉取请求中写道:
这是每周例行的DRM修复,很遗憾我这部分工作没能帮项目整体平稳下来,这次合入了大量驱动修复,涉及各种边界检查问题、内存泄漏、释放后重用(UAF)类漏洞。其中英特尔i915/xe驱动的修复算是最规整的,AMDGPU的修复散落在各处,然后ethosu也有很多小修复。
变更句柄ioctl的问题,真的给我们敲了个警钟——确实西马(Sima)是对的,我们早就该禁用这个接口。它其实才刚并入两三个内核版本,而且始终没能及时把测试代码合入上游主线。这次提交的补丁确实修复了西马发现的问题,但同时也直接禁用了这个ioctl,补丁里已经列清了它现存的所有已知问题,要求后续先补上合规的测试再重新启用。它本来就是给AMD ROCm的CRIU功能设计的小众用户态ioctl接口,所以我觉得直接禁用它没问题。
说不定这周项目就能平稳下来了。
戴夫
除了Linux 7.1版本仍在持续涌入大量修复补丁之外,这次的ioctl争议是另一件值得关注的事……这件事和drm_gem_change_handle_ioctl()有关,该函数是DRM PRIME框架下用来重新分配GEM句柄的接口。这个接口是AMD工程师在「用户空间检查点恢复」(Checkpoint and Restore in User-Space,简称CRIU)项目中开发的功能。CRIU需要能够创建或导入带有指定GEM句柄的缓存对象,这个接口正是AMD工程师在去年设计的。
AMD开发这项功能的目的,是实现对ROCm计算负载的运行应用/容器进行冻结,保存其运行状态以便后续恢复——比如用于在线迁移或者创建快照的场景。
今年早些时候,该接口被发现存在编号为CVE-2026-23149的安全漏洞,攻击者可以利用它让用户空间触发内核警告。开发团队今年1月曾尝试修复这个问题,但修复并未达到预期效果。
由于该问题涉及安全属性,相关讨论随后转移到邮件列表之外进行;西莫娜·维特(Simona Vetter)如今尝试第四次修复这个接口,最终决定暂时将其禁用。维特在本周提交给Linux 7.1的补丁提交说明中解释道:
(我把内容发到公开邮件列表)是因为现在漏洞已经曝光了,而且我们显然没办法在私有渠道把这个问题理清楚。截至目前,事情的来龙去脉是这样的:
提交5e28b7b9(提交说明为《drm: 在change_handle的PRIME交换操作前将旧句柄置空》)尝试修复gem_close和gem_change_handle两个ioctl之间的竞态条件,但出现了多处错误:
局部变量handle的命名造成了混淆:这个变量实际存储的是新句柄,导致我们把两阶段修复的逻辑错误用在了不对的IDR槽位上。提交7164d785(提交说明为《drm/gem: 修复change_handle和handle_delete之间的竞态》)尝试通过新增代码块修复这个问题,却忘记添加错误处理逻辑。这就导致现在有两条代码路径,全都是错的。
提交dc366607(提交说明为《drm: 替换指向新IDR的旧指针》)尝试做新一轮修复,但还是因为句柄命名混淆,修复逻辑前后不一致。就算我们对新句柄采用两阶段方案,这个修复也只能说勉强凑活——整个代码现在已经一团糟了,而且这也不符合最初修复的设计初衷。
另外,我们从始至终都没有把这个ioctl对应的IGT测试用例合入主线,这完全是违反开发规范的。最开始修复bug的时候,相关工作都是在非公开列表处理的,AMD的QA人员称bug已经修复。但很明显事实并非如此。这是我梳理整个问题后的处理方案:
- 将局部变量重命名为
new_handle,原来和参数args->handle共用别名的命名方式,混淆风险太高了。
- 将GEM对象查找和两阶段
idr_replace操作合并,避免我们在这里再把自己绕晕。 - 这样修改后,我们就不会再额外多出一个临时引用了——现在只会保留一个从IDR继承来的引用。但对new_handle的并发gem_close操作仍然可能抢走这个引用,因此我沿用create_tail使用的两阶段方案来修复这个问题。正如注释里写的,这个方案其实有点过度防御,但我也不确定自己是不是完全理清了这里的所有逻辑,所以为了最保险起见,还是沿用我们在其他ioctl里已经验证过的成熟模式。
调整了错误处理路径:我原本尝试把错误路径和成功路径改成统一的逻辑——其实除了要删除的句柄不同、以及要调用idr_replace重新安装对象的目标不同之外,两套逻辑完全一致。但统一之后代码可读性变得更差,所以我最终还是保留了更冗余的写法,可惜这样稍微掩盖了整个代码流程的对称性。
顺便把原来用7个空格的缩进改成了1个制表符缩进。
最后,因为我现在完全不敢相信自己对这块逻辑的判断了:暂时禁用该ioctl接口,直到我们补完IGT测试、所有问题都在公开邮件列表梳理清楚、并且取得全体共识之后,再重新启用。
v2版本更新:
佐木(Sashiko)发现我没有正确处理idr_replace的错误路径:这个调用必须像gem_handle_delete里那样,用IS_ERR_OR_NULL宏检查返回值。所以说,本来就应该1:1照搬现有的成熟路径,这块的逻辑实在太容易踩坑,怎么小心都不为过。
因此目前,DRM GEM修改句柄ioctl接口已因安全原因被禁用,只有等到补上合格的测试用例、并确认所有不同入参的处理逻辑都正确无误后,才会重新启用。

不过至少,该改动对用户的影响有限:只有在对AMD ROCm计算负载做检查点恢复的场景下才会受影响,普通用户不会遇到问题。
本次禁用接口的补丁也标记了需要向后移植到Linux稳定内核分支。
转自 Linux DRM Ioctl Developed By AMD Being Disabled Following Ongoing Security Issue – Phoronix
Linuxeden开源社区