Life of a handle in FIDL

This page gives a step-by-step explanation of how FIDL transfers a Zircon handle from one process to another. In particular, it focuses on the various meanings of "handle rights" and how handle rights are validated.

Scenario

Consider a simple client and server communicating over the following protocol:

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

Suppose that we remove zx.rights.WRITE from the handle rights, but only recompile the server, not the client. What happens when the client creates a VMO and passes it to Method?

Diagram

In this scenario, the client is acting as sender and the server is acting as receiver. We use those terms below because this is what matters for the purposes of transferring handles. If the method returned a handle, then the same steps would apply with the roles reversed.

See below for a detailed explanation of the diagram.

Diagram of sending a handle over FIDL

Explanation

  1. User code (sender)

    • Assume the sender obtains the VMO using the zx_vmo_create syscall. The returned handle h1 has default rights for a VMO: DUPLICATE, TRANSFER, READ, WRITE, MAP, GET_PROPERTY, and SET_PROPERTY.
    • Call Method, passing h1 to it.
  2. FIDL bindings (sender)

    • Wrap h1 in a handle disposition specifying the rights from the FIDL type: MAP, READ, and WRITE. This tells the kernel what rights to provide when transferring h1. The bindings don't know what rights h1 actually has. (They don't know for sure that it references a VMO either, but unlike rights this is usually represented in the static type system, making it hard to pass the wrong handle type by accident.)

      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,
      }
      
    • Invoke the zx_channel_write_etc syscall (or similar).

  3. Kernel

    • Ensure that h1 exists in the sender process's handle table.
    • Ensure that h1 refers to a VMO.
    • Ensure that h1 has (at least) the rights MAP, READ, and WRITE.
    • Restrict the rights to only include MAP, READ, and WRITE, removing the rights DUPLICATE, TRANSFER, GET_PROPERTY, and SET_PROPERTY. We'll refer to this restricted handle as h2. This is equivalent to invoking the zx_handle_replace syscall.
    • Enqueue the message with h2 instead of h1.
  4. FIDL bindings (receiver)

    • Invoke the zx_channel_read_etc syscall (or similar).
    • Unwrap h2 from the returned handle info. Unlike the handle disposition, the handle info stores the handle's actual type and rights as reported by the kernel.

      zx_handle_info{
          .handle = h2,
          .type = ZX_OBJ_TYPE_VMO,
          .rights = ZX_RIGHT_MAP | ZX_RIGHT_READ | ZX_RIGHT_WRITE,
      }
      
    • Get the expected type and rights from the FIDL type: MAP and READ.

    • Ensure that h2 has (at least) those rights.

    • Since h2 has the unexpected right WRITE, invoke the zx_handle_replace syscall to get a new handle h3 that only has the rights MAP and READ.

  5. User code (receiver)

    • Service Method using the h argument, which is h3.