NDIS 虚拟机(VM)共享内存的安全问题

本主题讨论了将共享内存从虚拟机分配给虚拟机队列 (VMQ) 接收缓冲区时所涉及的潜在安全问题。 本主题包含以下部分:

注意 在 Hyper-V 中,子分区也称为 VM。

VM 共享内存的安全问题概述

VM 不是受信任的软件实体。 也就是说,恶意 VM 不能干扰在 Hyper-V 父分区中运行的其他 VM 或管理作系统。 本部分提供背景信息和要求,以确保驱动程序编写者了解 VMQ 安全问题和共享内存的要求。 有关共享内存的详细信息,请参阅“写入 VMQ 驱动程序”部分中的“共享内存资源分配”主题。

在虚拟化环境中,VM 可以查看或修改 VM 共享内存。 但是,不允许查看或修改与其他 VM 关联的数据。 不允许虚拟机访问管理操作地址空间。

接收的数据包的标头部分必须受到保护。 不允许 VM 影响网络虚拟服务提供商(VSP)中 Hyper-V 可扩展交换机的行为。 因此,在网络适配器使用 DMA 将数据传输到 VM 共享内存之前,必须执行 VLAN(虚拟 LAN)筛选。 此外,交换机的媒体访问控制(MAC)地址学习功能不会受到影响。

如果连接到 VM Hyper-V 的可扩展交换机端口具有关联的 VLAN 标识符,则主计算机必须确保传入帧的目标 MAC 地址和 VLAN 标识符与主机将数据包转发到 VM 的虚拟网络适配器之前,端口的相应属性匹配。 如果帧的 VLAN 标识符与端口的 VLAN 标识符不匹配,则会丢弃数据包。 从主机内存中分配虚拟网络适配器的接收缓冲区时,主机可以检查 VLAN 标识符,并在必要时删除帧,然后使帧的内容对目标 VM 可见。 如果未将该帧复制到 VM 的地址空间,则无法由该 VM 访问该帧。

但是,当 VMQ 配置为使用共享内存时,网络适配器使用 DMA 将传入帧直接传输到 VM 地址空间。 此传输引入了一个安全问题,在此问题中,VM 可以检查接收的帧的内容,而无需等待可扩展交换机应用所需的 VLAN 筛选。

Windows Server 2008 R2 如何解决安全问题

在 Windows Server 2008 R2 中,VSP 将 VM 队列配置为使用从 VM 地址空间分配的共享内存之前,它会对队列使用以下筛选测试。

(MAC address == x) && (VLAN identifier == n)

如果网络适配器硬件可以在 DMA 传输到接收缓冲区之前支持此测试,则网络适配器可以删除具有无效 VLAN 标识符的帧,或将它们发送到默认队列,以便可由可扩展交换机筛选掉它们。 如果微型端口驱动程序在请求中成功设置队列中具有此测试的筛选器,可扩展交换机可以使用该队列的 VM 共享内存。 但是,如果网络适配器硬件无法基于目标 MAC 地址和 VLAN 标识符筛选帧,扩展交换机将使用该队列的主机共享内存。

可扩展交换机检查接收帧的源 MAC 地址,以配置传输帧的路由信息,也就是说,它类似于物理学习交换机。 可以在主机堆栈中安装防火墙筛选器驱动程序;例如,在网络适配器硬件的微型端口驱动程序上方和可扩展交换机驱动程序下方。 防火墙筛选器驱动程序可以在可扩展交换机之前访问接收帧中的数据。 如果每个帧的整个接收缓冲区是从 VM 地址空间分配的,则恶意 VM 可以访问由筛选器驱动程序或主机中运行的可扩展交换机检查的帧部分。

为了解决此安全问题,当在 VM 队列中使用 VM 共享内存时,网络适配器必须以至少为前视长度(这是一个预定的固定值)的字节偏移量进行数据包拆分。 任何超前数据——即超前大小指定的字节偏移量之后的数据——都必须通过 DMA 传输到为超前数据分配的共享内存中。 后视缓冲数据(帧负载的其余部分)应通过 DMA 传输到为后视缓冲数据分配的共享内存中。

下图展示了网络数据结构的关系,当传入数据被拆分为前视(lookahead)和后视(post-lookahead)共享内存缓冲区时。

展示 VMQ 数据包结构的图示,其中展示了单独共享内存缓冲区中的 lookahead 数据和 post-lookahead 数据。

VMQ 共享内存的摘要要求如下:

  • 网络适配器可以在大于前视大小的网络头边界处分割接收到的帧。 但是,当 NDIS 请求且无例外时,所有被接收并分配给 VMQ 的帧必须拆分到或超过 NDIS 请求的预测大小边界。

  • 必须使用 DMA 将 lookahead 数据传输到微型端口驱动程序分配的共享内存。 微型端口驱动程序必须在分配调用中指定内存将用于前视数据。

  • 后置展望数据必须通过 DMA 传输到由微型端口驱动程序分配的共享内存中。 微型端口驱动程序必须在分配调用中指定内存将用于后查找数据。

  • 小端口驱动程序不应依赖于 NDIS 将选择哪个地址空间来完成共享内存分配请求。 也就是说,预读取或后预读取数据的共享内存地址空间是与实现相关的。 在许多情况下,NDIS 或可扩展交换机可能会满足主机内存地址空间中的所有请求,包括那些用于预读取的请求。

  • 当 VMQ 接收队列中的帧被上报到驱动程序堆栈时,必须保留接收帧的顺序。

  • 网络适配器必须在每个后头缓冲区中分配足够的回填内存空间。 此分配允许将查找头数据复制到后望头缓冲区的回填部分,并允许帧传送到连续缓冲区中的 VM。

如果硬件中没有满足 VMQ 共享内存这些要求的机制,则支持接收端散点收集 DMA 的硬件可以通过为每个接收帧分配两个接收缓冲区来实现相同的结果。 在这种情况下,第一个缓冲区的大小限制为所请求的查找头大小。

如果网络适配器无法通过任何方法满足 VMQ 共享内存的这些要求,VSP 将从主机地址空间分配 VMQ 接收缓冲区的内存,并将接收的数据包从网络适配器接收缓冲区复制到 VM 地址空间。

Windows Server 2012 及更高版本如何解决安全问题

从 Windows Server 2012 开始,VSP 不会为 VMQ 接收缓冲区分配 VM 的共享内存。 相反,VSP 从主机地址空间为 VMQ 接收缓冲区分配内存,然后将收到的数据包从网络适配器接收缓冲区复制到 VM 地址空间。

以下几点适用于在 Windows Server 2012 及更高版本的 Windows 上运行的 VMQ 微型端口驱动程序:

  • 对于 NDIS 6.20 VMQ 微型端口驱动程序,无需更改。 但是,当 VSP 通过发出 OID_RECEIVE_FILTER_ALLOCATE_QUEUE 的 OID(对象标识符)方法请求来分配 VM 队列时,它将NDIS_RECEIVE_QUEUE_PARAMETERS结构的 LookaheadSize 成员设置为零。 这将强制微型端口驱动程序不要将数据包拆分为前查头缓冲区和后查头缓冲区。

  • 从 NDIS 6.30 开始,VMQ 微型端口驱动程序不得声明支持将数据包数据拆分为前瞻缓冲区和后瞻缓冲区。 当微型端口驱动程序注册其 VMQ 功能时,它必须在初始化 NDIS_RECEIVE_FILTER_CAPABILITIES 结构时遵循以下规则:

    • 微型端口驱动程序不得在 Flags 成员中设置NDIS_RECEIVE_FILTER_LOOKAHEAD_SPLIT_SUPPORTED标志。

    • 微型端口驱动程序必须将 MinLookaheadSplitSizeMaxLookaheadSplitSize 成员设置为零。

    有关如何注册 VMQ 功能的详细信息,请参阅 确定网络适配器的 VMQ 功能