关注了就能看到更多这么棒的文章哦~
mseal() and what comes after
By Jonathan Corbet
October 20, 2023
ChatGPT translation
https://lwn.net/Articles/948129/
Jeff Xu提出了新增一个名为 mseal()
的系统调用的提案,该系统调用将允许应用程序阻止对选定内存映射(memory mapping)的修改。这有助于阻止用户空间应用程序对某些类型的攻击;一些其他操作系统已经具备了这种功能。在Linux内核中也有添加此类机制的支持,但是目前明确下来 mseal()
不会以其当前形式进入主线。相反,从多个角度来说,它已经成为了内核开发中应该不要做什么的一个反面案例。
Xu描述了新系统调用的目的如下:
“内存密封可以额外防止对映射本身进行修改。这有助于减轻已损坏的指针传递给内存管理系统调用的情况下的内存破坏问题。例如,这样的攻击方式可以破坏控制流完整性(control-flow integrity)的保证,因为应该受信任的只读内存可能会改变为可写,或者.text page可以重新映射。”
这项功能的目标用户是Chrome浏览器,其中包含了JavaScript代码的即时(JIT)编译引擎。由于它会即时生成可执行代码,因此JIT编译必须小心进行,以免创建(和运行)有问题的代码。如Stephen Röttger在这篇博客文章中所述,已经付出了大量努力来实现控制流完整性,以防止JIT系统成为攻击者的工具。但是,如果攻击者以某种方式强制执行更改内存权限的内存管理系统调用,那么一切预防措施都不再成立。因此,Chrome开发人员希望有一种机制,使那些系统调用不适用于内存的特定区域,以增强浏览器对此类攻击的防护。
封面信件指出, mseal()
类似于最近添加到OpenBSD的 mimmutable()。但是,所提议的系统调用的原型与 mimmutable()
完全不同:
int mseal(void *addr, size_t len, unsigned int types, unsigned int flags);
要受影响的内存范围由 addr
和 len
指示。必须将 flags
设置为零,而 types
控制要在该地址范围上阻止哪些系统调用:
MM_SEAL_MPROTECT
: mprotect()和pkey_mprotect()MM_SEAL_MMAP
: mmap()MM_SEAL_MUNMAP
: munmap()MM_SEAL_MREMAP
: mremap()MM_SEAL_MSEAL
: 未来的mseal()
调用
Linus Torvalds迅速对该补丁系列提出了异议,他表示:“我对添加某种“锁定内存映射”模型没有异议,但对当前方案不满意”。他对实现的细节提出了许多投诉,但后来明确表示系统调用的设计是错误的。例如,阻止 munmap() 在其他可以导致地址取消映射的操作(例如mmap()和mremap())仍然被允许,是没有多少意义的。他说,投入的努力只阻止特定系统调用操作,这显然是错误的做法;如果阻止了一个范围的内存被取消映射(例如),它必须从所有方向阻止,否则提供的保护将只是一个幻想。
Matthew Wilcox质疑接口的复杂性,建议仅添加到 mprotect()
的几个标志即可。他表示,内存区域应该是不可变的(可能还可以进一步减少访问权限),或者不可变,无论是使用哪个系统调用。他后来补充道:
“这就是seccomp的毛病,并且更糟,因为你试图拒绝单个系统调用,而不是建立一个可以允许的系统调用列表。如果我们明天引入了一个新的系统调用,可以影响VMA,那么问题将归咎于应用程序未禁用新的系统调用。那是可怕的设计!”
甚至在linux-kernel上还出现了OpenBSD维护者Theo de Raadt,他赞同了Torvalds的观点,并建议Linux应该简单地添加 mimmutable()
,而不是以更复杂的形式重新发明该功能。Torvalds 同意了这个想法 ,尽管他建议添加一个用于未来更改的 flags参数,Theo de Raadt不喜欢 这个想法。这反映了一个事实,就是OpenBSD可以控制其用户空间,因此如果有必要的话它可以在今后添加一个flags参数;Linux没有这种奢侈的条件,因此如果要存在的话,该参数必须从一开始就存在。
Xu抵制了这个想法,引发了一种典型的(相对温和的)de Raadt回复。实际上,即使在收到了评论后,Xu仍然坚持他提出的设计,这就引出了来自Wilcox的回复,Wilcox在其中试图把讨论引导回这组补丁系列实际试图实现的内容之上:
“让我们从目的开始。mimmutable/mseal/任何东西的目的是修复地址范围到其基础对象的映射,无论它是特定的文件映射还是匿名内存。调用成功后,不得可能使该虚拟范围中的任何地址指向任何其他对象。
次要目的是锁定该范围的权限。在那里修复它们可能在那里修复它们,可能允许RW->RO转换。
在这些目的的基础上,您应该能够判断任何一个系统调用或任何madvise()…是否应该被允许。”
Wilcox最后得出的结论是,Xu需要更好地倾听那些试图帮助他的开发人员。
目前,很明显 mseal()
不会以其当前形式进入内核。这引出了一个问题,就是接下来应该做些什么。Röttger加入了讨论,指出纯 mimmutable()
解决方案并不满足Chrome开发人员想要看到的一切;他们有一些情况下希望阻止做取消映射的动作(unmapping),但仍然需要能够使用 mprotect()
更改内存保护。De Raadt描述了这种情况,称其为“部分密封”,这意味着受影响的内存实际上没有得到保护。
在保留这种功能的同时,将从 mseal()
中删除由 mseal()
提供的更复杂选项的一些后续提案将来可能会提出。但是,这个提案是否是 mimmutable()
或其变体的方式是否还有待观察。
可以指出,这里出了一些问题。许多人认为最初的提案只是实现了Chrome开发人员所说他们想要的东西,而没有深入研究真正的需求(对于Chrome和任何其他潜在用户公共的需求)是什么。Google拥有许多有经验的开发人员,他们可以在将其公开发布之前审查此提交,但似乎没有发生这种情况,导致相对经验不足的开发人员陷入了困境。对提案的反馈受到了抵制,而不是得到了认真的听取。这就导致一系列不让任何人满意的交互。
尽管如此,每个人都似乎认为这里是一个合理的使用场景。因此,问题的解决方法是找到正确的方式,希望现在更好地理解了该问题。如果下一次尝试看起来更像 mimmutable()
,并反映了已经给出的反馈,那么内核可能会获得这个密封功能以满足Chrome场景,并提供更广泛的用户空间强化。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~