| RFC-0101: Dynamic components with numbered handles ||
|---|---|
| Status | Accepted |
| Areas | - Component Framework |
| Description | Provides a way to pass numbered handles to dynamic components. |
| Gerrit change | - [531642](https://fuchsia-review.googlesource.com/c/fuchsia/+/531642) |
| Authors | - [lindkvist@google.com](https://fuchsia-review.googlesource.com/q/owner:lindkvist@google.com) - [geb@google.com](https://fuchsia-review.googlesource.com/q/owner:geb@google.com) |
| Reviewers | - [bryanhenry@google.com](https://fuchsia-review.googlesource.com/q/owner:bryanhenry@google.com) - [geb@google.com](https://fuchsia-review.googlesource.com/q/owner:geb@google.com) - [cgonyeo@google.com](https://fuchsia-review.googlesource.com/q/owner:cgonyeo@google.com) - [shayba@google.com](https://fuchsia-review.googlesource.com/q/owner:shayba@google.com) - [thatguy@google.com](https://fuchsia-review.googlesource.com/q/owner:thatguy@google.com) |
| Date submitted (year-month-day) | 2021-05-18 |
| Date reviewed (year-month-day) | 2021-06-02 |

[Edit this RFC](https://ci.android.com/edit?repo=fuchsia/fuchsia/main&file=docs/contribute/governance/rfcs/0101_dynamic_components_with_numbered_handles.md)
[Edit RFC metadata](https://ci.android.com/edit?repo=fuchsia/fuchsia/main&file=docs/contribute/governance/rfcs/_rfcs.yaml&searchAndJump=-+name:+'RFC-0101')

## Summary

This document proposes adding a new parameter to
`fuchsia.sys2.Realm/CreateChild`. This parameter will contain a set of numbered
handles. The created component's [runner](https://fuchsia.dev/docs/concepts/components/v2/capabilities/runner) will receive these handles
when it is asked to run the component. The provided handles will only be
available to components that run in collections with a new type of durability:
`single-run`. Components in a collection with a [durability](https://fuchsia.dev/docs/concepts/components/v2/realms#collections) of
`single-run` are started when created and destroyed when stopped. This scopes
the provided handles to a single run of the component.

## Motivation

Starnix is a Component Framework v2 [runner](https://fuchsia.dev/docs/concepts/components/v2/capabilities/runner) that runs Linux binaries
on Fuchsia. Starnix implements the Linux system interface to run these binaries
without modification.

Starnix provides a [ffx plugin](https://fuchsia.dev/docs/development/tools/ffx/overview) that allows users to run Linux components
from the host command line. Starnix wants to connect the
`stdin`/`stdout`/`stderr` of the Linux component to a triple of socket handles.
This component does not interact with the system by exposing FIDL protocols.
Instead, it interacts with the user via the provided sockets.

## Design

### Background

There are two dimensions of lifecycle transitions in the component framework.
The first has to do with existence (`Created` and `Destroyed`). The second
relates to the component's execution (`Started` and `Stopped`). In other
operating systems, which only have processes and not components, these
dimensions are equivalent: creating a process is the same as starting it, and a
process is destroyed when it stops running.

The distinction between `Created` and `Started` is relevant when providing
arguments to components. Ordinarily a single component instance can be started
and stopped multiple times, so the component manager must store the arguments to
provide them each time the component runs. If the arguments are handles this can
be problematic since not all handles can be duplicated. Any non-duplicable
handles are thus "consumed" by a single run of the component.

### Protocol updates

As mentioned in [Background](https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs/0101_dynamic_components_with_numbered_handles.md.txt#background), creating a component is distinct from
starting it. Thus the handles provided to `CreateChild` must be either:

1. Available each time the component is started.
2. Scoped to a single run of the component.

Since not all handles can be duplicated (stored in the component manager for
subsequent runs), (1) would only be possible if new handles were fetched from
their source for each run. See [routing
handles](https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs/0101_dynamic_components_with_numbered_handles#routing-numbered-handles-as-capabilities) for why an approach that
fetches handles at startup was not chosen. Fortunately (2) is a viable solution
for the motivating use case.

A new table, `ChildArgs` is created and added as a parameter to
`Realm/CreateChild`:

    protocol Realm {
      /// If args contains numbered_handles, the collection must have a durability
      /// of type `single-run`.
      CreateChild(CollectionRef collection, ChildDecl decl, ChildArgs args)
          -> () error fuchsia.component.Error;
    }

    resource table ChildArgs {
      /// The numbered handles for the component instance.
      ///
      /// Only PA_FD and PA_USER* handles are valid arguments, and inclusion of any other
      /// handles will result in an error.
      1: vector<fuchsia.process.HandleInfo>:N numbered_handles;
    }

In addition, the `fuchsia.component.runner.ComponentStartInfo` table will be
updated to contain the numbered handles:

    resource table ComponentStartInfo {
      6: vector<fuchsia.process.HandleInfo>:N numbered_handles;
    }

The runner will provide those handles to the component, or close them and return
an error if the runner does not support providing numbered handles to
components.

### Collection durability

Components that are created via the `Realm` protocol live in collections.
Collections have a `durability` annotation that indicates what the lifecycle
semantics of components within the collection are.

A new collection `durability` value, `single-run`, will be added to indicate
that components in the collection are started immediately when they are created,
and destroyed when they are stopped. `ChildArgs.numbered_handles` can only be
used with collections that are marked `single-run`. This scopes the arguments in
`ChildArgs` to a single run of the component.

    collections: [
        {
             name: "playground",
             durability: "single-run",
        }
    ],

## Implementation

Component manager will be updated to store the `ChildArgs` until they are passed
to the runner, and to handle `single-run` collection semantics.

## Backwards Compatibility

The change is backwards compatible with regards to runners: runners are not
required to use the numbered handles that are provided. If a runner does not
support numbered handles it is expected to close the handles.

The change is not backwards compatible for `Realm` clients.

The change is backwards compatible with regards to CML: the only change is an
added durability enum.

## Performance

No performance impact is expected as the handles are provided directly to the
component manager.

## Security considerations

This change introduces a way for parents to pass arbitrary numbered handles to
children. The exchange of these handles is mediated by both the component
manager and the runner. Only components that are run in a collection marked
`single-run` can receive handles in this fashion.

## Testing

The existing tests for `CreateChild` will be exapnded to cover the new
arguments.

## Drawbacks, alternatives, and unknowns

### Drawbacks

There are a couple drawbacks with the proposed solution compared to the
alternatives listed below:

- It is a new way of starting a component: the component's lifecycle is not influenced by capability binding.
- The provided handles are opaque to the component framework's static CML analysis.
- Since the components receiving the handles are destroyed when stopped, their persistent storage is also wiped out. Thus components that use persistent storage are not a good candidate for this feature.

### Routing numbered handles as capabilities

Routing numbered handles could be done explicitly by the component framework.

There are several different ways that this could be done, but they all have the
following "shape."

Introduce a protocol that would be implemented by the source of the numbered
handle(s):

    protocol HandleProvider {
        Get(string handle_name) -> (fuchsia.process.HandleInfo handle);
    }

This protocol is then turned into a capability:

    capabilities: [
        {
            handle_provider: "stdin",
            path: "/svc/fuchsia.component.HandleProvider",
        },
    ],
    expose: [
        {
            handle_provider: "stdin",
            from: "self",
        },
    ],

This could then be routed, via CML, to the destination just like other
capabilities are.

#### Benefits

- Could be evolved to support more type information about the handles.
- Makes the routing of the handles explicitly visible to the component framework.

#### Drawbacks

The component would not be able to start until the handles have been fetched.
Even if the performance impact could be amortized by caching handles in
component manager, the motivating use case would not benefit from this because
the handles would be different for each run of the component. In the proposed
design, the host code could "fire and forget" the request to start a component
and continue executing as if the call was successful. In this alternative, the
host code would need to sit and spin, waiting for the associated handle request
to come back.

The handle provider is not always reachable via static routing. In the
motivating use case the handle provider is on the host machine, connected to the
Fuchsia device via Overnet. This could be solved by routing "as far as possible"
and then have the "edge" component fetch the handles via an ad hoc mechanism
before returning them to the component framework. This is an additional burden
on developers that want to use the feature.

A handle provider has no way of distinguishing between handle requests from a
given capability route. Specifically, consider the motivating use case:

1. The user starts two Linux components from the host, in different terminals.
2. The components are instantiated in a collection.
3. The handle provider receives two requests for the same handle.

At this point, there is no way for the handle provider to know which component
is associated with which handle request. This could be solved by introducing
additional mechanisms for client identification, but it is considerably more
complex than the proposed design.

This solution is a larger commitment and more complex than the proposed design.
That said, the proposed design does not prevent or conflict with explicit
routing of numbered handles in the future.

### StartChild

This alternative proposes adding the numbered handles as an argument to the
`StartChild` call on `Realm`. This is similar to the proposed design but has the
drawback of introducing a race between a component binding to a capability
offered by the component (which would start the component) and the `StartChild`
call being made. Specifically, the component would only receive the numbered
handles if the `StartChild` call won the race since it's not clear how the
handles would be delivered if the component was already started.

### Starter protocol

Passing numbered handles could be done by a `Starter` protocol. The starter
protocol can be used to start a component, is implemented by the component
manager on behalf of a component, and can be routed like any other protocol
(i.e., the component can be started by a component that is not its parent).

This protocol can be routed like any other protocol, so a client can use it to
start components that are located in arbitrary locations in the component
hierarchy.

A starter protocol contains a method that accepts numbered handles as arguments:

    [Discoverable]
    protocol StarterWithArgs {
        /// Start the component that is bound to this protocol.
        /// If the component is already running, the call returns an error.
        Start(StartArgs start_args) -> () error fuchsia.component.Error;
    };

This proposal is very similar to [StartChild](https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs/0101_dynamic_components_with_numbered_handles.md.txt#StartChild). A protocol exposed
in this fashion has the benefit of being easier to audit and allowlist, but also
introduces a race between calling `Start` and binding to any other capability
exposed by the component. In addition, the ordering concerns are worsened by the
fact that the client isn't always the parent, and thus can't necessarily restart
the component to provide the arguments. This could be solved by adding a stop
method to the protocol.

Clients would also need to coordinate child management between the starter
protocol and the realm protocol, instead of managing the child exclusively
through the realm protocol.
[](https://issues.fuchsia.dev/issues/new?component=1439230&template=1881640&description=Issue+on:+https://fuchsia.dev/docs/contribute/governance/rfcs/0101_dynamic_components_with_numbered_handles.md "Report a bug or suggest improvements on this page") [](https://cs.opensource.google/fuchsia/fuchsia/+/main:docs/contribute/governance/rfcs/0101_dynamic_components_with_numbered_handles.md "View the source code of this page") [](https://ci.android.com/edit?repo=fuchsia/fuchsia/main&file=docs/contribute/governance/rfcs/0101_dynamic_components_with_numbered_handles.md&cloneRepo=fuchsia/fuchsia:refs/heads/main&fullPath=fuchsia/docs/contribute/governance/rfcs/0101_dynamic_components_with_numbered_handles.md "Edit the source code of this page")