RFC-0155:可选的功能路由

RFC-0155:可选功能路由
状态已接受
领域
  • 组件框架
说明

优惠中添加了一个新字段,并使用组件清单的各个部分来指明给定功能可能不存在的情况。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2022-02-07
审核日期(年-月-日)2022-04-14

摘要

优惠中添加了一个新字段,并使用组件清单的各个部分来指明给定 capability 可能不存在的情况。

设计初衷

核心领域分片

使用核心领域分片时,会出现功能路由来源或目标不存在的情况。例如,

  • 组件 /core/wlancfg 使用协议 fuchsia.location.sensor.WlanBaseStationWatcher,但某些产品上不存在此协议。
  • 组件 /core/bt-a2dp 必须使用 fuchsia.power.battery.BatteryManager 功能(如果可用),但该功能不适用于所有产品。
  • /core/omaha-client-service 组件必须使用 fuchsia.update.config.OptOut 协议(如果可用),但该协议不适用于所有产品。
  • CommsAgent 软件包必须使用视频(如果可用),但某些产品没有视频。
  • 跟踪记录提供程序是一个提供 fuchsia.tracing.provider.Registry 功能的组件。这对于开发很有用,但不应将此组件添加到 user build 中。

以上场景是我们今后要支持的组件配置,但目前组件框架中有一些位置存在与此相关的粗糙边缘:

  • 如果通过清单中不存在的组件提供或来自该组件,会导致清单验证失败。
  • 如果通过未提供该功能的组件提供某项功能,则会导致审查验证失败。
  • 功能路由失败(例如,尝试使用未提供的功能)会导致组件管理器(正确)发出警告级日志,这看起来可能令人担忧,因此在调查故意省略该功能的产品的问题时具有误导性。

通过集合进行路由验证

审查是一种组件路由验证工具,可确保给定组件树中使用的所有功能都具有有效的功能路由。除了它提供的系统正确性断言之外,这是一款实用的开发工具,因为它可以将路线验证从运行时验证转移到构建时,从而缩短开发周期。

目前,维护的许可名单用于禁止对特定功能路由进行审查检查。这样,当组件使用某个不可用的 capability 时,构建就能成功。不过,如果相应功能可用的 build 上的功能路由配置有误,这也会导致构建成功。

此外,在 https://fxbug.dev/42174612 解决后,审查功能还将验证源自会话领域的功能路由。通常,会话组件依赖于来自会话外部的功能,如果其中有一个组件希望添加到审查许可名单中,这会增加阻力,因为这些组件可能是在 fuchsia.git 之外开发的。

利益相关方

谁能决定此 RFC 是否被接受?(此部分为可选内容,但我们建议您。)

教员:Hunter Freyer (hjfreyer@google.com)

由 FEC 指定的人员,负责通过 RFC 流程照管此 RFC。

审核者

  • Mark Dittmer (markdittmer@google.com) - 安全
  • Gary Bressler (geb@google.com) - 组件框架
  • Marc Khouri (mnck@google.com) - WLAN
  • Ani Ramakrishnan (aniramakri@google.com) - 蓝牙
  • Yaar Schnitman (yaar@google.com) - 产品

社交

收到来自 https://fxbug.dev/42168255 的要求并征集到一些特定人员的反馈后,利益相关方分享了此 RFC 的早期形式。

设计

系统将添加一个名为 availability 的新字段以使用声明。此字段的默认值为 required,这意味着该组件希望提供该功能,如果该组件不可用,则可能会发生故障。此 RFC 未建议对 required 功能的行为进行任何更改,它们的行为方式与目前所有功能的行为相同

{
    use: [
        {
            protocol: "fuchsia.logger.LogSink",
            availability: "required",
        },
        {
            directory: "config-data",
            path: "/config",
            rights: [ "r*" ],
            // The `availability` field defaults to `required` when omitted.
        },
    ],
}

使用声明中的 availability 字段也可设置为 optional,以反映在功能不可用时组件可以正常工作的时间(可能出现行为被修改)。

{
    use: [
        {
            // Emergency location is not present in all products.
            protocol: "fuchsia.location.sensor.WlanBaseStationWatcher",
            availability: "optional",
        },
    ],
}

系统会将一个名为 void 的新来源添加到优惠声明的可能来源列表中。

{
    offer: [
        {
            protocol: "fuchsia.update.config.OptOut",
            from: "void",
        },
    ],
}

使用声明是可选的,可以通过现有组件或“void”提供。需要声明使用声明的功能可能无法在无效时提供,因为这可能会导致组件出现故障。

中间优惠(从父优惠到子优惠)也可以将 availability 字段设置为以下值之一:

  • required(默认值):无论子项是否可以处理其缺失行为,都必须获得对此功能的访问权限(不能在祖先实体中通过 void 提供该功能)。
  • optional:子级必须能够处理没有此功能的情况(优惠链末尾的使用声明必须是可选的)。
  • same_as_target:优惠将具有与目标相同的可选性。如果目标需要该功能,则此优惠会规定目标必须获得该功能。如果目标可以选择是否使用该功能,则此优惠会规定目标不一定获得此功能。
{
    offer: [
        {
            protocol: "fuchsia.power.battery.BatterManager",
            from: "parent",
            to: "bt-a2dp",
            // Emit a build-time error if this protocol is not correctly routed
            // to this child (including offers from void).
            availability: "required",
        },
        {
            protocol: "fuchsia.location.sensor.WlanBaseStationWatcher",
            from: "parent",
            to: "wlancfg",
            // Emit a build-time error if this child is unable to handle the
            // absence of this protocol.
            availability: "required",
        },
    ],
}

如果某个组件使用 optional,并且其父项以 required 的形式提供该 capability,那么如果该 capability 的路由无效或以“offer from void”结尾,则系统会发出构建时错误。如果某个组件的用例为 required,并且其父项以 optional 的形式提供该 capability,则会发出构建时错误。此字段可以未设置,在这种情况下,会被忽略。

核心领域组件

RFC-0089 中所述,核心领域是通过“核心分片”组合而成的,这些核心分片是在构建时与核心领域合并的 CML 代码段。确切的核心分片集由产品定义设定。

通常,我们建议将针对子级的优惠添加到与子级声明相同的核心分片中。这有助于确保,如果 build 中包含子级,该子级也会获得其正常运行所需的功能。

如果子项优惠希望某个源同时选择在 build 中包含,那么将子项优惠添加到该子项的分片就会变得很复杂。如果目标为子级 a 的优惠的来源为 b,但 b 也位于一个分片中,那么 a 的分片只能包含在也包含 b 分片的 build 中,或者在构建时因来自不存在的子级的优惠而发出清单验证错误。

为了允许子级的优惠与子级的声明位于同一核心分片中(无论其来源如何),系统将引入一个名为 source_availability 的优惠声明的新字段。此字段的默认值为 present,但可以设置为 unknown

source_availability 设置为 unknown 时,如果优惠声明引用的来源不在清单中,则优惠声明将通过清单验证。在清单编译期间,缺少的优惠声明来源将替换为 void 源代码,这使得路由以此优惠声明结尾的任何使用声明都能够通过将可用性设置为 optional 来通过路线验证,以反映该功能不存在于所有产品上。

{
    offer: [ {
        protocol: "fuchsia.examples.Echo",
        from: "#child-that-might-not-be-declared",
        to: "#echo-user",
        source_availability: "unknown",
    } ],
}

这样,平台配置维护者就可以将核心分片集作为单一可信来源,以确定核心领域中存在哪些功能和组件。省略核心分片不仅可以从核心领域中移除子系统,还可以从 void 准确更新包含该子系统来源的所有优惠,以便在该子系统上具有可选用法的任何组件在被系统特意排除时不会正常获得对这些子系统的访问权限(虽然在此情况下,任何必需用法仍然会导致错误)。

示例

核心领域中具有可选功能的组件

在此示例中,我们来看看 wlancfg 的清单和核心领域分片将如何根据提议的设计发生变化。

fuchsia.location.sensor.WlanBaseStationWatcher 协议的优惠将从 emergency.core_shard.cml 移至 wlancfg.core_shard.cml,并且 source_availability 字段设置为 unknown(因为紧急核心分片及其紧急组件可能包含在与 wlancfg 相同的平台配置中,也可能不包含)。

// src/connectivity/location/emergency/meta/emergency.core_shard.cml
{
    offer: [
        // This is removed
        {
            protocol: "fuchsia.location.sensor.WlanBaseStationWatcher",
            from: "#emergency",
            to: "#wlancfg",
        },
    ],
}
// src/connectivity/wlan/wlancfg/meta/wlancfg.core_shard.cml
{
    offer: [
        // This is added
        {
            protocol: "fuchsia.location.sensor.WlanBaseStationWatcher",
            from: "#emergency",
            to: "#wlancfg",
            source_availability: "unknown",
        },
    ],
}

将优惠声明移至 wlancfg 的核心分片是确保 source_availability 字段正常运行的必要条件,并且也符合核心分片最佳实践。

除了核心分片更改之外,wlancfg 中此协议的使用声明也将更新为 optional

// src/connectivity/wlan/wlancfg/meta/wlancfg.cml
{
    use: [
        {
            protocol: "fuchsia.location.sensor.WlanBaseStationWatcher",
            // This line is added
            availability: "optional",
        },
    ],
}

现在,当 build 中不包含 emergency.core_shard.cml 文件时,由于 wlancfg 无法访问 fuchsia.location.sensor.WlanBaseStationWatcher 协议,构建时将不会发生构建时错误。这意味着该协议可能会从审查的许可名单中移除,如果任何配置错误导致该协议在平台配置(若该协议可用)上无法供 wlancfg 使用,则会导致构建时错误。

会话中具有可选功能的组件

在此示例中,我们来看看根据提议的设计,将 fuchsia.power.battery.BatteryManager 协议从 /core/battery_manager 转换为 /core/session_manager/session:session/workstation_session/login_shell/ermine_shell 所涉及的清单和核心领域分片将如何变化。

workstation.core_shard.cml 文件中的协议优惠将更新为将 source_availability 设置为 unknown

// src/session/bin/session_manager/meta/workstation.core_shard.cml
{
    offer: [
            protocol: "fuchsia.power.battery.BatteryManager",
            from: "#battery_manager",
            to: "#session-manager",
            // This line is added
            source_availability: "unknown",
    ],
}

无需更改 coreermine_shell 之间的优惠,并且系统会更新 ermine_shell 清单,将协议的 availability 字段设置为 optional

// src/experiences/session_shells/ermine/shell/meta/ermine.cml
{
    use: [
        {
            protocol: "fuchsia.power.battery.BatteryManager",
            availability: "optional",
        },
    ],
}

完成这些更改后,workstation.core.shard.cml 文件可能会包含在平台配置中,而不提供 battery_manager.core_shard.cml,而不会造成构建时审查错误,也不会将此功能添加到许可名单中。这意味着,可以在无电池的硬件上运行工作站会话的平台配置安全地排除电池管理器组件。

会话所有者希望确保某个可选 capability 始终始终

可用

以前面的示例为基础,考虑一个名为 workstation-on-laptops 的虚构会话,该会话与 workstation 会话相同,只是它具有额外的笔记本电脑特有的功能。如果此会话的所有者希望使用 workstation 会话中存在的同一 ermine 组件,但想确保自己始终有权访问 fuchsia.power.battery.BatteryManager 协议(由构建时错误强制执行),会话所有者可能会将此协议作为 required 提供给 ermine

// src/experiences/session_shells/ermine/session/meta/workstation_on_laptops_session.cml
{
    offer: [
            protocol: "fuchsia.power.battery.BatteryManager",
            from: "parent",
            // login_shell offers the capability to ermine
            to: "#login_shell",
            availability: "required",
    ],
}

在此示例中,如果将 workstation-on-laptops 会话与提供来自 voidfuchsia.power.battery.BatteryManager 协议的平台配置(即不包含 battery_manager.core_shard.cml)的平台配置进行比较,则会发出构建时错误,尽管 ermine.cml 可以选择使用该协议。

会话所有者想确保组件能够处理缺少

可选功能

同样在“ermine”示例的基础上进行构建。如果工作站会话的所有者希望确保 ermine 始终能够处理缺少 fuchsia.power.battery.BatteryManager 协议(无论协议当前是否可用)的情况,他们可以将此协议作为 optional 提供给 ermineBatteryManager

// src/experiences/session_shells/ermine/session/meta/workstation_session.cml
{
    offer: [
            protocol: "fuchsia.power.battery.BatteryManager",
            from: "parent",
            // login_shell offers the capability to ermine
            to: "#login_shell",
            availability: "optional",
    ],
}

此可选优惠对采用指定安排的构建或运行时行为没有任何影响,因为 ermine 还可以选择使用该功能。但是,如果 ermine 被更改为需要此功能,那么审查时将会发出构建时错误,无论功能路由是否有效或是否以“void 提供的优惠”结尾。

实现

为了实现这些更改,必须更新 fuchsia.component.decl.Component FIDL 表和 CML JSON 架构以包含新字段,同时更新 CMC、cm_fidl_validator 和审查,以正确处理“设计”部分所述的 optional 语义。

性能

建议的更改不会对构建时间或大小产生任何显著影响,因为在这些清单中携带额外枚举的成本很低。此外,在组件管理器中提议的运行时行为更改不会对性能产生任何显著影响,因为在此项工作开始时,实现新命名空间组合逻辑所需的信息就全部存储在内存中。

工效学设计

对于当前需要与许可名单互动的任何组件作者来说,这项变更应该会显著提升工效学设计,以抑制审查验证错误,因为我们将改进预期验证错误的上下文,使审查完全不会发出这些预期错误。

向后兼容性

此更改纯粹是对 FIDL 表和 JSON 格式的补充,因此没有向后兼容性问题。

安全注意事项

通过这项变更,组件作者无需获得安全团队的批准即可停用未路由功能的审查错误,因为许可名单将不再用于控制对未路由功能的审查错误抑制。

这种做法被认为是可以接受的,因为此提案没有更改创建或更改组件路由本身的审核流程。

隐私注意事项

建议的更改不会影响用户数据的收集、处理或存储方式,因此没有任何隐私问题。

测试

已经有广泛的测试,涵盖组件清单流水线。这些测试将改为包含这项新功能的覆盖范围。

文档

我们将更新组件清单文档,以说明新字段及其对验证的影响。

缺点、替代方案和问题

现有艺术和参考资料

TODO