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系统调用来获取新标识名h3,该标识名仅具有 权利MAPREAD

  5. 用户代码(接收方)

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