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 封装在句柄处置中,以指定来自 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