Tock 核心团队 2024-07-12 例会深度解读:AppId 设计、存储权限 TRD 与 Rust 内存安全实践 操作系统嵌入式嵌入式OS【免费下载链接】tockA secure embedded operating system for microcontrollers项目地址https://gitcode.com/gh_mirrors/to/tock点击查看免费下载本篇基于 Tock 核心团队 2024-07-12 会议记录展开聚焦会上讨论的三个核心技术议题libtock-rs 802.15.4 原生支持中的 Rust 未定义行为UB风险与处理策略、AppIdPolicy应用标识策略的类型设计取舍、以及 Storage Permissions应用持久化存储权限TRD 的落地实现。结合 Tock 仓库中的 TRD 文档与内核源码本文给出这些议题的完整背景、设计脉络与源码级实现佐证读者可借此理解 Tock 在进程身份、凭据校验与存储隔离方面的安全架构。会议背景与议题概览Tock 核心团队例会于 2024-07-12 举行参会者包括 Branden Ghena、Amit Levy、Leon Schuermann、Brad Campbell、Alyssa Haroldsen、Johnathan Van Why 等内核与网络工作组核心成员。本次会议除常规的项目进展同步外主要围绕三个技术议题展开libtock-rs 802.15.4 原生支持的 Rust 内存安全争议一个已被整体认可但未通过 Miri 检查的 PR引发了关于未定义行为代码是否应被合并的讨论AppIdPolicy的类型化设计应用标识Application Identifier是保留为usize还是引入泛型类型的架构取舍Storage Permissions TRD 实现应用持久化存储权限架构的代码审查。会议同时确定了 PR 自动分配assignee策略并计划对核心团队例会时间安排再做一轮调查。议题一libtock-rs 802.15.4 原生支持与 Rust 内存安全问题背景libtock-rs 的 PR #551 增加了 802.15.4 原生raw帧收发支持。该 PR 整体获得认可但在 CI 中无法通过 MiriRust 的未定义行为检测工具检查因为代码存在未定义行为undefined behavior。Johnathan Van Why 指出贡献者本人似乎对 Rust 内存安全问题的定位与修复经验不足且该问题的解决并非三言两语可以交代清楚。技术实质缓冲区管理是问题核心会议讨论确认所有不安全代码均集中在 15.4 的缓冲区管理buffer management逻辑中。Amit Levy 分析认为贡献者合理地参考了 C 语言版本的实现而 C 版本天然不满足 Rust 的 soundness健全性要求。这与 Brad Campbell 的判断一致——Tock 内核团队此前在循环缓冲区cycling through buffers设计上投入了大量工作当时就担心 libtock-rs 会出现类似问题如今确实应验。Hudson Ayers 提出改用静态生命周期缓冲区的假设Johnathan 明确否定静态生命周期缓冲区只能让你共享一个缓冲区但永远不会再拿回来读取make a buffer you can share, but never get back to read因此无法解决本质问题。会议形成的策略共识针对是否接受不健全unsound的 PR会议最终形成如下共识允许带明确警告的临时方案代码可以暂时存在于 libtock-rs 之外用于实验但必须附带文档说明其 unsound 属性或允许合入但需带有醒目的警告标记和 TODO明确告知这是不健全、不安全的不要复制或在实际中使用Alyssa 的模块私有原则模块私有的、具体的不安全代码在文档充分的情况下是可以容忍的但绝不应当以公开的不安全 API 形式合入——至少应标记为unsafe两条独立问题该 PR 实际存在两个问题——Miri 检测到的 UB 与需要手动调用Drop跟进机制由网络工作组Networking WG在周一的例会上将问题形式化articulate the problem并制定修复计划若涉及较大改动可邀请 PR 作者一同参与。这一议题折射出 Tock 生态在用户态库的 Rust 安全性与内核设计约束之间的张力Tock 内核本身是嵌入式 Rust 安全编程的严格践行者而用户态库libtock-rs在引入底层协议支持时如何在不破坏 Rust 健全性的前提下建模缓冲区生命周期是一个普遍性问题。议题二AppIdPolicy 的设计取舍——usize还是泛型讨论缘起AppIdPolicy 的讨论源于 PR #4028。Brad Campbell 提出的核心问题是应用标识Application Identifier目前是一个裸的usize理想情况下希望引入泛型类型以表达更丰富的语义但这一改动会传播到内核的几乎所有位置propagateseverywherein the kernel。会议在三条路线间权衡保留usize接受其在语义表达上的贫乏引入泛型T接受类型参数在内核代码库中全面扩散寻找某种魔法的中间方案。设计背景AppID 与凭据模型要理解这一讨论需要先了解 Tock 的应用标识体系详见 doc/reference/trd-appid.md。Tock 区分两组概念Application Identifier应用标识符应用的数值标识可跨重启、跨二进制版本持久存在内核保证同时运行的进程中同一 AppID 至多一个以避免对同一应用资源的并发访问Application Credentials应用凭据存放在 TBFTock Binary Formatfooter 中的任意 k 字节序列通常承载签名、哈希等完整性信息用于将 AppID 密码学地绑定到具体二进制。Tock 内核通过process_checker模块中的三个核心 trait 实现这一体系见 kernel/src/process_checker.rs// 凭据检查策略决定内核接受哪些类型的凭据、是否接受某个具体凭据 pub trait AppCredentialsPolicya { fn set_client(self, client: a dyn AppCredentialsPolicyClienta); fn require_credentials(self) - bool; fn check_credentials(self, credentials: TbfFooterV2Credentials, integrity_region: a [u8]) - Result(), (ErrorCode, TbfFooterV2Credentials, a [u8]); } // 唯一性判定两个进程的 AppID 是否不同相同则不能并发运行 pub trait AppUniqueness { fn different_identifier(self, process_a: ProcessBinary, process_b: ProcessBinary) - bool; fn different_identifier_process(self, process_a: ProcessBinary, process_b: dyn Process) - bool; fn different_identifier_processes(self, process_a: dyn Process, process_b: dyn Process) - bool; } // 压缩将 AppID 压缩为 32 位 ShortId用于快速比较 pub trait Compress { fn to_short_id(self, process: ProcessBinary) - ShortId; }ShortId的定义见 kernel/src/process.rs其核心是使用core::num::NonZeroU32保证 32 位值非零将 0 保留给LocallyUniquepub enum ShortId { /// 抽象值保证与所有其他 ShortId 不同不可用于除唯一性外的任何用途 LocallyUnique, /// 32 位数值内核保证在所有运行进程中唯一 Fixed(core::num::NonZeroU32), }会议结论保留usize并考虑结构体包装Leon Schuermann 的观点很有代表性他并不担心usize的位数不足但无法直接传递一个语义更清晰的 enum 或 struct而只能传递一个魔数 usize确实令人遗憾不过让泛型类型扩散到整个代码库会显著损害可用性。他的具体提议是现阶段保留usize未来确有需要时再改为泛型尺寸generic size一个折中方案是将usize包装进一个结构体struct使其语义更明确more clear that its meaningful。Brad Campbell 补充了关键使用场景假设系统希望支持签名应用 双密钥的模型——一把密钥授予特权行为另一把仅用于签名但不授予特权。此时验证器verifier需要区分两把密钥并向内核其余部分传达权限信息此前设想通过 AppID 的 ShortId 来实现。由于验证器verifier与应用标识分配器assigner分时独立运行验证器必须保留用了哪把密钥的信息并持久化——这正是该 PR 所启用的能力。Alyssa Haroldsen 提到了zerocopy的TryFromBytes可安全地将字节转换为枚举是一种受检查的 transmute。Leon 认为它只解决了一小步底层仍然受限于某个预定尺寸因此不如直接保留usize。最终AppIdPolicy在仓库中以复合 trait 的形式存在将AppUniqueness与Compress合并为单一引用见 kernel/src/process_checker.rspub trait AppIdPolicy: AppUniqueness Compress {} implT: AppUniqueness Compress AppIdPolicy for T {}这与 TRD 中第 9 节的描述完全一致。TRD 同时给出了Compress的默认实现对于()类型to_short_id恒返回ShortId::LocallyUnique见 kernel/src/process_checker.rs即不配置任何 AppId 策略的系统默认给每个进程分配一个本地唯一标识。议题三Storage Permissions——应用持久化存储权限 TRD 的实现落地议题概述Brad Campbell 在会议上报告他撰写了 Storage Permissions 的 TRD已合入并完成了大部分实现代码PR #4031。与 TRD 相比实现中唯一的重大差异是新增了 capabilities能力机制——只有特权代码才能创建存储权限对象。该实现本质上是 TRD 的落地不再用单一方式为应用分配存储权限而是允许每个内核自行决定分配策略。存储权限架构完整的架构描述见 doc/reference/trd-storage-permissions.md。其核心要点存储标识Stored State Identifier所有共享持久化存储实现必须为每个存储对象附带 32 位标识标记创建该对象的应用。应用写入数据时使用其ShortId内核写入数据时标识必须为 0三类独立权限Write布尔权限决定应用能否写入新数据Read(权限类型, 存储状态标识)元组仅允许读取绑定特定存储标识的状态Modify同样为元组仅允许修改绑定特定存储标识的状态硬性要求无ShortId::Fixed的应用不能访问任何持久化存储读写改均禁止权限如何映射到应用必须可由不同 Tock 内核定制。内核强制 API由于不可能在内核可信代码中实现所有持久化存储 API内核提供接口供 capsule 查询指定进程的存储权限见 kernel/src/storage_permissions.rs/// 检查这些存储权限是否授予对标记为 stored_id 的状态的读访问 pub fn check_read_permission(self, stored_id: u32) - bool; /// 检查这些存储权限是否授予对标记为 stored_id 的状态的修改访问 pub fn check_modify_permission(self, stored_id: u32) - bool; /// 如果应用有写权限返回写入状态时应使用的标识无写权限则返回 None pub fn get_write_id(self) - Optionu32;StoragePermissions是一个具体类型而非 trait以便在存储 API 中直接传递而无需为系统中每个进程维护静态对象。其内部是一个私有枚举StoragePermissionsPrivate包含五种表示形式kernel/src/storage_permissions.rsenum StoragePermissionsPrivate { SelfOnly(core::num::NonZeroU32), // 仅访问自身状态NonZeroU32 即应用的 ShortId::Fixed FixedSize(FixedSizePermissions), // 固定大小至多 8 个读 ID 8 个改 ID Listed(ListedPermissions), // 任意长度的静态数组读/改 ID Kernel, // 仅供内核存取自身状态标识为 0 Null, // 无任何存储访问权限 }StoragePermissions结构体对枚举进行包装确保权限只能通过构造函数创建而构造函数要求持有 capability从而保证只有可信代码能创建存储权限。仓库中定义了两种 capability见 kernel/src/capabilities.rsKerneluserStorageCapability与ApplicationStorageCapability。Capsule 侧的强制逻辑TRD 给出了存储抽象如 FilingCabinet 文件柜抽象中必须遵循的三步检查逻辑读操作需check_read_permission通过写新数据需get_write_id返回Some覆盖已有数据需check_modify_permission通过——任一不满足均返回ErrorCode::NOSUPPORT。该设计已体现在内核 HIL 中kernel/src/hil/kv.rs的键值存储接口将StoragePermissions作为各操作如get、set、update等的参数传入每个存储对象以write_id基于StoragePermissions标记见 kernel/src/hil/kv.rs。权限分配策略board 级可定制内核通过ProcessStandardStoragePermissionsPolicytrait 允许各 board 自行决定权限分配见 kernel/src/process_policies.rspub trait ProcessStandardStoragePermissionsPolicyC: Chip, D: ProcessStandardDebug { /// 返回指定进程的存储权限 fn get_permissions(self, process: ProcessStandardC, D) - StoragePermissions; }每个进程创建时会存储一个StoragePermissions对象见 kernel/src/process_standard.rs在进程创建流程中调用storage_permissions_policy.get_permissions(process)完成赋值。capsules/systemcrate 提供了若干开箱即用的策略实现对应 TRD 第 7 节列出的三种指定方式capsules/system/src/storage_permissions/null.rs默认空策略所有进程均获得Null权限——()类型作为默认实现即返回new_null()capsules/system/src/storage_permissions/individual.rsIndividualStoragePermissions——凡拥有ShortId::Fixed的进程获得new_self_only只能访问自身状态LocallyUnique进程获得Nullcapsules/system/src/storage_permissions/tbf_header.rsTbfHeaderStoragePermissions——依据应用 TBF 头中的存储权限字段分配对应 TRD 中在 TBF headers 中指定权限的方式。它调用process.get_tbf_storage_permissions()见 kernel/src/process_standard.rs读取 TBF 头中的读写 ID 列表分别截断至最多 8 个构造FixedSize权限若头缺失或无固定 ShortId则授予Null。会议确认的现状Brad 在会议上表示该 PR 目前只需要大家过目just needs eyesLeon 主动承担了审查工作。从源码看该设计已完整落地TRD 定义的三种权限语义、内核强制 API、capability 保护的构造过程、以及多种可插拔的分配策略均已实现是TRD 驱动的内核功能开发的典型范例。会议的其他决议PR 管理与例会时间会议还通过了两项流程决议PR 自动分配同意对 PR 立即自动分配 assignee此前仅对 nightly 版本且在无评论时分配以避免 PR 因无人认领而长期滞留关于 last-call 后自动合入的机制仍在探讨Alyssa 建议 24 个营业小时且需确认 last-call 标签是否仅限核心团队成员添加例会时间再调度Amit 计划再发起一轮例会时间调查如无异议将发放问卷。总结2024-07-12 的核心团队例会展示了 Tock 在三个维度的工程实践内存安全红线对 libtock-rs 中不健全代码的态度是可临时、可标注、不可默许——unsound 代码必须文档化、必须警告、公开 API 必须标记unsafe并通过工作组机制推进根本性修复接口设计的务实主义AppIdPolicy 在类型安全与代码扩散之间选择保留usize辅以结构体包装的改进方向体现了嵌入式内核在抽象成本上的审慎权衡TRD 驱动的功能落地Storage Permissions 从 TRD 到 capabilities 保护的内核实现、再到多种可插拔的 board 级分配策略构成了完整可验证的权限架构。对于希望深入 Tock 安全机制的开发者建议按以下顺序阅读仓库资料先读 doc/reference/trd-appid.md 理解 AppID/凭据/ShortId 模型再读 doc/reference/trd-storage-permissions.md 理解存储权限语义最后对照 kernel/src/process_checker.rs 与 kernel/src/storage_permissions.rs 的源码以及 capsules/system/src/storage_permissions 下的策略实现即可完整掌握 Tock 应用身份与存储隔离的安全架构。赞分享操作系统嵌入式嵌入式OS【免费下载链接】tockA secure embedded operating system for microcontrollers项目地址https://gitcode.com/gh_mirrors/to/tock点击查看免费下载相关推荐Tock Core WG 2024-06-07 会议纪要解读流式缓冲交换、yield-wait 与基于 AppID 的存储权限Tock Core WG 2024 06 07 会议纪要解读流式缓冲交换、yield wait 与基于 AppID 的存储权限 本篇技术指南以 Tock 核心操作系统嵌入式嵌入式OSTock 应用持久化存储权限架构深度解析TRD 设计规范与内核、Capsule 源码实现Tock 应用持久化存储权限架构深度解析TRD 设计规范与内核、Capsule 源码实现 导读 本文以 Tock 官方参考文档《Application Per操作系统嵌入式嵌入式OSTock 内核内存安全与缓冲区抽象演进2022-05-27 核心团队会议纪要深度解读Tock 内核内存安全与缓冲区抽象演进2022 05 27 核心团队会议纪要深度解读 本文基于 Tock 项目官方核心团队会议纪要 core notes 20操作系统嵌入式嵌入式OS上一篇如何有效利用Second-Brain项目提升个人知识管理效率的7个技巧下一篇Webstudio Code Text 组件完全指南基于 Shiki 的语义化语法高亮实现与工程细节创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考