LWN:从KVM直接进行host系统调用!

关注了就能看到更多这么棒的文章哦~

Direct host system calls from KVM

By Jonathan Corbet
July 29, 2022
DeepL assisted translation
https://lwn.net/Articles/902585/

虚拟化机制的设计原则之一就是要在 host 和它所运行的 guest 系统之间提供强大的隔离。guest 是不可以被信任的,必须严格控制不能让它们访问或影响到自己虚拟机以外的任何东西。因此,如果有一个会允许 guest 在 host 环境下执行任意系统调用的 patch,肯定会引起网络讨论中的高度关注。Andrei Vagin 就发布了这样一组 patch,确实得到了这样关注。

Vagin 的使用场景是 gVisor,这是一个专注于安全的容器管理平台。gVisor 跟完整的虚拟化系统一样,也是在一个虚拟机内运行容器的(使用了 KVM),但其目的不是将这些容器与系统完全隔离。相反,使用了 KVM 来为容器内的进程提供地址空间隔离,但这些虚拟机并不会运行正常的操作系统内核。相反,它们运行的是一个特殊的 gVisor 内核,处理其中所包含进程的系统调用,从而在运行过程中做出安全决策。

该内核的工作方式很有趣;它将自己映射到每个虚拟机的地址空间,确保跟其在 host 上的布局一致,然后根据需要,在两者之间切换。进入虚拟机一侧的函数名称很特别,被称为 bluepill()。两边的执行环境基本上是一样的,有相同的内存布局,但是 guest 这边会受到虚拟机边界的限制。

许多应用程序的系统调用可以由 gVisor 在虚拟机中执行,但其中一些必须在 host 下处理,因为限制较少。gVisor 当然可以直接从虚拟机退出,让 host 端的控制进程来执行调用,然后将结果返回到虚拟机中,但这个 exit 的速度很慢。执行大量的 exit 操作会严重损害工作负载的整体性能;因为像 gVisor 这样的系统的部分目的就是提供比纯虚拟化更好的性能,因此性能损失被认为是不可取的。

这里提出的解决方案就是提供一个新的 hypercall 调用(KVM_HC_HOST_SYSCALL),guest 内核可以用它来直接在 host 上运行一个系统调用。它需要两个参数,分别是系统调用的编号以及一个包含了该系统调用参数的 pt_regs structure。在 host 上下文中执行调用后(实际上没有从虚拟机中退出),这个 hypercall 将把结果返回给调用者。这个接口只有在 guest 对 host 的内存布局有足够的了解并且可以提供合理的系统调用参数时,才能发挥作用;在 gVisor 的情况下,双方的内存布局是一致的,不需要特别处理。

这个功能的内部实现是通过新增了一个 helper 也就是 do_ksyscall_64(),它可以从内核中调用任何系统调用。鉴于以这种方式调用系统调用通常都是不受欢迎的,这个功能似乎肯定会成为吸引批评的焦点,事实上,Thomas Gleixner 也进行过适当的抱怨。事实上,Thomas Gleixner 正式抱怨说:“这将一个神奇的内核系统调用接口暴露给随便什么驱动程序的编写者。真的是不可以的”。虽然他承认这组 patch 总体上是 “一个聪明的想法”,但他明确表示,以这种方式来暴露系统调用的方案是不会成功的。

同时,从 KVM guest 中直接调用 host 端系统调用的功能相当于在虚拟机和 host 之间的隔离上捅了一个大窟窿。事实上,patch 封面上就将其描述为 “普通的虚拟机的一个后门”。因此,正如人们可以想到的,这个直接进行系统调用的功能在默认情况下是禁用的;想要使用它的进程必须在创建虚拟机时明确地启用。可以预见,大多数 hypervisor 都不会这样做。

在谷歌等公司内部运行的内核里经常包含上游代码中不存在的的重大改动;这个 patch set 描述了其中一个重大改动:

在谷歌内核中,我们有一个专门为 gVisor 设计的类似 kvm 的子系统。这组 patch 是将其整合到 KVM 代码库中并使其适用于所有 Linux 用户的第一步。

于是 Sean Christopherson 就询问接下来会要做什么。“如果不首先了解全局情况,实际上是无法 review 这一组 patch 的”。如果接下来的步骤确认是大家无法接受的,那么合并这第一步就是一个错误了。到那时,内核社区可能会发现自己在支持一个实际上不会被使用的不完整功能。事实证明,Vagin 说,这是唯一所需要的功能。他说,gVisor 现在可以在 KVM 上面工作,目前这组 patch 只是提高了它的性能。

Christopherson 还问了是否有其他一些替代方案,他指出 “在 KVM 内进行任意系统调用是有点可怕的”。Vagin 提供了几个方案,首先是目前的方案,即从虚拟机 exit 出来(性能很差)处理每个系统调用。另一种方法是在 host 上运行所有的 gVisor,每一个系统调用都从虚拟机中 exit。在这种模式下执行一个系统调用需要 2.1µs;直接进行系统调用可以将耗时减少到 1.0µs 左右。或者 gVisor 可以使用 BPF 来处理系统调用;这能提供类似的性能,Vagin 说,但需要一些很有争议的改动,比如要为 BPF 程序提供调用任意系统调用的能力。还有一种可能性是使用曾经提出的 process_vm_exec() 系统调用,但这在某些情况下可能性能仍然不好。

KVM 维护者 Paolo Bonzini 说,他最主要的反对意见是在 guest 和 host 之间缺乏地址转换。在目前这组 patch 的情况下,这种机制取决于双方的内存布局是否相同,否则系统调用参数中的任何地址在 host 端都是没有意义的。因此,新的机制是高度专注于 gVisor 这一种场景的,似乎不太可能被更广泛地使用。不过,并不清楚是不是每个人都认为这种专门定制是一种缺点。

总而言之,在这种工作模式下的 gVisor 展示了 host 和它所运行的容器之间的安全边界的一个有趣的转变。安全主要取决于虚拟机内的代码,host 这边的代码通常都是比较受到信任的。这是对 KVM 的虚拟化工作方式的一个新观点,但似乎运行结果很不错——至少在谷歌内部是这样。不过,这种机制是否能够进入 mainline,目前仍不清楚。在 host 和 guest 之间的墙壁上打个洞,可不是一件轻而易举的事情,所以相关的开发者很可能需要先确定真的没有更好的替代方案了。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

format,png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值