LWN: KVM guest 里的私有内存!

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

Private memory for KVM guests

By Jonathan Corbet
April 7, 2022
DeepL assisted translation
https://lwn.net/Articles/890224/

云计算是很美妙的;它可以更加有效地利用好计算系统,并使虚拟机在点击鼠标或调用 API 时立即就准备好。但是云计算也会有问题;虚拟机的安全性取决于 host 系统的安全性。在当前世界上部署好的大多数系统中,host 可以随意查看其 guest 的内存,运行 guest 系统的那些用户只能寄希望于 host 不会去这么做。对于这个问题正在开发一些解决方案,包括 Chao Peng 等人准备的 KVM guest-private memory patch set,但仍有一些问题没有解决。

基于 KVM 的 hypervisor 在 host 系统上运行时是作为一个用户空间进程的。为了给 guest 提供内存,hypervisor 在 host 上分配内存,然后使用各种 KVM ioctl()调用将其映射到 guest 的 "物理" 地址空间。但是,hypervisor 也保留了它对这部分内存的 mapping,完全可以访问到这部分内存。有些情况下有必要使用这种访问权限,用来进行 guest 和 hypervisor 之间的通信,但是 guest 很可能想把大部分内存留给自己私人使用。

Providing private memory

这个问题的解决方案提案利用了内核的 memfd 机制。hypervisor 可以通过利用新增的 MFD_INACCESSIBLE 标志来调用 memfd_create(),为其 guest 设置一个私有内存区域。这创建了一个特殊类型的 memfd,创建者对它不能进行任何操作,也就是读取或写入都将失败,也无法将它映射到内存中。如果还使用了 MEMFD_SECRET 标志,那么 host 对受影响的这些 page 的 direct mapping 也将被移除,这意味着 host 内核也将没有对该内存的映射,即使 host kernel 被攻破,也很难访问到这部分内存内容。

还有一件事可以做,就是把它交给 KVM,让 KVM 来把这个区域映射到 guest 的虚拟地址空间。这样一来,guest 就可以拥有对这部分内存的完全访问权限,尽管 host(尽管它是对这部分内存进行配置的人)却无权访问。启用这个功能需要在 KVM 以及实际提供内存的 backing store(可能是 shmfs)之间设置双向 callback。第一组操作是在 KVM 方面提供的:

struct memfile_notifier_ops {
  void (*invalidate)(struct memfile_notifier *notifier,
                     pgoff_t start, pgoff_t end);
  void (*fallocate)(struct memfile_notifier *notifier,
                    pgoff_t start, pgoff_t end);
};

每当把内存被映射到这个内存区域时,fallocate() 函数就会被调用,如果 host 端是在使用 fallocate()系统调用时。值得注意的是,Dave Chinner 反对这个名字,所以这个 callback 很可能最终被命名为 notify_populate() 之类的。invalidate() callback 则是用来表明一系列 page 已经被删除了,不能再从 guest 那里访问到了。

其他 callback 是由 backing-store 具体实现方案来提供的,从而可以让 KVM 能够访问这个本来无法访问的 memfd 中的内存:

struct memfile_pfn_ops {
  long (*get_lock_pfn)(struct inode *inode, pgoff_t offset, int *order);
  void (*put_unlock_pfn)(unsigned long pfn);
};

KVM 将调用 get_lock_pfn() 来获取一个或多个 page 的 hostpage-frame number。当 KVM unmap page 时,它会调用 put_unlock_pfn()来通知 backing store,这些 page 不再被使用。

这种机制,再加上 KVM 中必要的一些基础设施,就足以为 guest 提供 private memory 了。hypervisor 将为 guest 分配该内存,但不能以任何方式访问到它。

Conversion

Quentin Perret 提出了一个相关的问题:当 guest 想与 host 共享一些 private memory 时,会发生什么?这种情况经常发生(例如,配置 I/O buffer 的时候),因此类似的大多数解决方案都提供了一种机制,用于把这些 memory page 的状态在 private 和 shared 状态之间来回切换(conversion)。Perret 问道,这种机制要是如何处理。

Sean Christopherson 提供的答案是,guest 通过以 KVM_EXIT_MEMORY_ERROR 状态退出 hypervisor 来表示希望转换 page 状态。这个返回值将被传回 hypervisor[更新:感谢 Paolo Bonzini 提供了下面这段解释的修订版本。]

Sean Christopherson 提供的答案是,guest 用一个 hypercall 来表示希望转换 page。KVM_RUN ioctl() 立即以 KVM_EXIT_MEMORY_ERROR 状态返回给用户空间的 hypervisor 进程,如果它同意该请求,就可以把不可访问的 memfd 的相关部分进行 unmap 来实现。这也是通过 fallocate(),使用 "hole-punch" 功能完成的。然后,hypervisor 可以将普通内存映射到新创建的空洞之中,从而产生一个双方都能访问的内存区域。

需要注意的一件事是,从设计上来说,将 page 共享给 host 是一个破坏性的操作,hole-punch 操作将导致存储在那里的数据消失。正如 Christopherson 所描述的那样,这种行为与一些硬件实现的行为很吻合,page 要先被 share 给 host,然后才能在其中填写需要共享的数据。Perret 正在为 Android 开发一个类似的机制("protected KVM " 或 pKVM),他更希望有一个 in-place conversion 机制。他说,如果没有这个机制,这个解决方案就 "可能不那么适合 pKVM"。他列举了一连串的理由,其中包括:

pKVM 的一个目标是将一些东西(如 DRM 等)从 Arm Trustzone 环境迁移到受保护的 VM 中。这将给 Linux 一个抵御来自这部分的攻击的机会,毕竟目前来说这些功能是可以访问到所有内存的。在 Linux 和 Trustzone(donations and shares)之间转换 page 是快速并且不是破坏性的,所以我们真的不希望 pKVM 通过要求 hypervisor 来 memcpy 东西来变得更差。

Christopherson 质疑是否必须要做非破坏性转换(non-destructive conversion),他认为就算是修改 pKVM 来处理破坏性转换(destructive conversions) "似乎并不太麻烦"。Andy Lutomirski 也不确定这个要求是否真的有好处,并担心正确实现会有不小的难度:

如果我们真的想支持同一个 page 在共享和私有状态之间的转换,那么我们的情况就有点尴尬了。从私有到共享在概念上是很容易的,做一些记录,重新创建 direct map 里的 entry,然后就完成了。但是反过来则是一团乱:所以之前正在使用该 page 的地方都需要先被停下来。如果该 page 最近被用于 DMA 的话,这就还要包括清理 IOMMU entry。

Perret 重申了他的想法,也就是 in-place conversion 会更加有用,但他承认他自己(也包括所有其他参与讨论的人)目前都还没有收集到数据来证明这一点。他也没有想清楚 in-place conversion 的细节问题,尽管他提出了一个关于这个功能如何实现的大概想法。

截至目前,讨论仍在进行,没有明确的解决方案。涉及到的开发者都有兴趣创建一个适用于所有使用场景的机制,他们都不愿意增加几种 private memory 的实现。但是他们都希望在避免过度复杂化的同时能获得最好的性能。协调并达成这样的目标,正是 system program 也就是系统编程(以及大多数其他类型编程开发)的核心,也是内核社区通常比较擅长的事情。至少,如果所有感兴趣的各方都能参与到讨论中的话。开发者们已经开始讨论,所以,运气好的话有望从这个讨论中得到一个可行的解决方案,但这可能还需要一段时间。

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

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

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

6fc9a6376f010544f5d2eaf39650d233.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值