[LWN 新闻] Linux eBPF 程序暂无硬件内存隔离
简介
2026年2月12日,Yeoreum Yun 在内核邮件列表中发布了一项关于提升 BPF 安全性 的建议:使用内存保护密钥(Memory Protection Keys)来防止 BPF 程序对内存的未授权访问。Yun 希望将这个话题列入 2026年5月举行的 Linux Storage, Filesystem, Memory Management 和 BPF 峰会的讨论议程,但由于缺乏足够的关注度,这个可能性不大。Yun 还提交了一个实现相关修改的补丁集,但目前尚未在邮件列表中分享。Yun 的提案在目前的形式下似乎不太可能被接受,但内核在过去已经添加了基于硬件的加固选项,有时是在经过大量讨论之后。
内存保护密钥简介
传统内存访问控制的问题
现代 CPU 需要将虚拟地址转换为物理地址时,会查询页表(page table)。页表还规定了内存是否可读、可写、可执行、用户空间是否可访问等信息。
页表具有多级结构,需要多次指针间接转换才能找到实际内存页的条目。为了避免每次内存访问都进行这些间接转换的开销,CPU 会保留一个最近访问条目的缓存,称为 TLB(Translation Lookaside Buffer)。
当内核想要更改给定内存区域的访问权限时,需要更新页表然后刷新 TLB(这会导致性能下降)。更糟糕的是,如果内存区域很大,可能需要更新许多页表条目。即使只是跟踪需要更新哪些页表条目并遍历它们,也可能是一项耗时的操作。
内存保护密钥的工作原理
内存保护密钥是一种硬件功能,可以帮助避免大规模修改页表的开销,使得将内存权限更改作为常规操作变得可行。
内存保护密钥使用页表中的四位将内存中的每个页面与十六个密钥之一关联;有一个特殊的 CPU 寄存器将每个密钥与读/写权限相关联。
这避免了对单个页表条目进行实际修改的需要:只需更改相应密钥的权限位,内存就会变得不可访问,无需遍历页表或刷新 TLB。
内核中的内存保护密钥
内核自 2016 年以来就支持内存保护密钥,但该支持仅限于用户空间。相关的系统调用允许用户空间应用程序使用内存保护密钥来实现更快的 mprotect() 版本。
从理论上讲,没有理由不可以在内核内部使用内存保护密钥。实际上,内核已经进行了许多尝试将它们集成到内核中,但都未能实现。
Yun 建议添加新的 kmalloc_pkey() 和 vmalloc_pkey() 函数,用于在内核对象的内存分配中使用密钥保护。这样就可以让 BPF 程序只访问内核内存的特定子集。具体来说,由 BPF 程序拥有的内存,或内核子系统专门打算与 BPF 程序共享的内存,可以使用与其他内核分配不同的内存保护密钥进行分配。然后,在进入 BPF 代码时,可以快速禁用对一般内核内存的访问。
Yun 的消息中详细描述了这将如何工作,但没有包含任何实际实现代码——尽管他们打算很快分享他们的工作进度代码——所以很难判断这个修改的侵入性有多大。
开发者的不同意见
Dave Hansen 认为这个计划对于调度程序等只有有限可写数据的子系统可能是可行的,但内核的其他区域会有更多问题:
"网络不是我的强项,但数据包内存似乎相当动态分配,也需要由 eBPF 程序写入。我怀疑任何会即使只慢几个周期的数据包分配方案都是不可行的。"
"依我之见,任何以'我们只需要一个新的分配器或修改现有内核分配器来跟踪一种新内存类型'开头的解决方案都是死胡同。或者,最好的情况下,也是一个非常外科式的、针对性的解决方案。"
另一方面,Alexei Starovoitov 不仅认为这个建议难以实施,而且毫无意义。Yun 列举了 2020 年到 2023 年间的几个 CVE,作为 BPF 验证器本身不足以确保安全的证明。Starovoitov 不同意这个观点:
"它们都不是安全问题。它们只是 bug。"
Yun 同意它们是 bug,但不同意它们没有安全影响。Yun 认为,BPF 验证器中存在的导致过去内存损坏的 bug 的存在,足以成为在运行 BPF 程序和可利用的漏洞之间设置另一道屏障的理由。
前景展望
考虑到之前尝试在内核中使用内存保护密钥的失败经历以及实施的难度,Yun 将内存保护密钥引入 BPF 子系统的提案似乎不太可能取得进展。
另一方面,内核一直在缓慢添加加固措施,例如每个调用站点的 slab 缓存——也许将这些缓存与内存保护密钥关联起来是合乎逻辑的下一步。只有时间才能证明内存保护密钥是否是对内核各种加固工具的有用补充,还是一种笨拙的干扰。无论如何,它们可能必须通过其他子系统进入内核。
相关链接
编译原理: 本文基于 LWN.net 文章《No hardware memory isolation for BPF programs》翻译整理。
文章评论(0)