FIDL 形式的句柄的生命周期

本页逐步介绍了 FIDL 如何将 Zircon 句柄从一个进程转移到另一个进程。本文将重点介绍“句柄权限”的各种含义以及句柄权限的验证方式。

场景

假设有一个简单的客户端和服务器通过以下协议进行通信:

protocol LifeOfAHandle {
    Method(resource struct {
        h zx.Handle:<VMO, zx.Rights.MAP | zx.Rights.READ | zx.Rights.WRITE>;
    }) -> ();
};

假设我们从句柄权限中移除了 zx.Rights.WRITE,但仅重新编译了服务器,而未重新编译客户端。当客户端创建 VMO 并将其传递给 Method 时,会发生什么情况?

图表

在此场景中,客户端充当发送者,服务器充当接收者。我们在下文中使用了这些术语,因为这对于转移标识名至关重要。如果该方法返回了一个句柄,则会应用相同的步骤,但角色会反转。

如需详细了解此图表,请参阅下文

通过 FIDL 发送句柄的示意图

说明

  1. 用户代码(发送者)

    • 假设发送者使用 zx_vmo_create 系统调用获取 VMO。返回的句柄 h1 具有 VMO 的默认权限:DUPLICATETRANSFERREADWRITEMAPGET_PROPERTYSET_PROPERTY
    • 调用 Method,并向其传递 h1
  2. FIDL 绑定(发送方)

    • h1 封装在 handle disposition 中,指定 FIDL 类型中的权限:MAPREADWRITE。这会告知内核在转移 h1 时要提供哪些权限。绑定不知道 h1 实际 拥有哪些权限。(他们也不确定它是否引用了 VMO,但与权限不同,这通常在静态类型系统中表示,因此很难意外传递错误的句柄类型。)

      zx_handle_disposition{
          .operation = ZX_HANDLE_OP_MOVE,
          .handle = h1,
          .type = ZX_OBJ_TYPE_VMO,
          .rights = ZX_RIGHT_MAP | ZX_RIGHT_READ | ZX_RIGHT_WRITE,
      }
      
    • 调用 zx_channel_write_etc 系统调用(或类似调用)。

  3. 内核

    • 确保 h1 存在于发送方进程的句柄表中。
    • 确保 h1 指的是 VMO。
    • 确保 h1 至少拥有 MAPREADWRITE 权限。
    • 将权限限制为仅包含 MAPREADWRITE,移除权限 DUPLICATETRANSFERGET_PROPERTYSET_PROPERTY。 我们将此受限句柄称为 h2。这相当于调用 zx_handle_replace 系统调用。
    • 使用 h2 而不是 h1 将消息加入队列。
  4. FIDL 绑定(接收方)

    • 调用 zx_channel_read_etc 系统调用(或类似调用)。
    • 从返回的 句柄信息中解封装 h2。与句柄处置不同,句柄信息存储内核报告的句柄实际类型和权限。

      zx_handle_info{
          .handle = h2,
          .type = ZX_OBJ_TYPE_VMO,
          .rights = ZX_RIGHT_MAP | ZX_RIGHT_READ | ZX_RIGHT_WRITE,
      }
      
    • 从 FIDL 类型获取预期类型和权限:MAPREAD

    • 确保 h2 拥有(至少)这些权利。

    • 由于 h2 具有意外的权限 WRITE,因此调用 zx_handle_replace 系统调用来获取仅具有权限 MAPREAD 的新句柄 h3

  5. 用户代码(接收方)

    • 使用 h 实参(即 h3)的服务 Method