消毒用品

设计初衷

排错程序是用于检测代码中某些类型的 bug 的工具。通过 运行原理不尽相同,不过清理程序通常(但并不总是)依赖于 这种形式的编译时插桩 bug。 Fuchsia 利用各种消毒剂发现和诊断危险的虫子 通过其他方式难以找到的类别。

排错程序由构建时标志启用。Sanitizer 构建将持续进行 练习了 Fuchsia 的持续集成 (CI) 和提交队列 (CQ),以及 为 Fuchsia C/C++ 和 Rust 开发者提供服务。

开发者通常受益于排错程序,无需任何特殊操作, 而且只有在清理程序检测到错误时才需要留意。 不过,这也存在一些限制。继续阅读,了解什么是消毒程序 以及如何使用它们

支持的排错程序

Fuchsia 目前支持以下排错程序:

  • AddressSanitizer (ASan) 可检测 出界访问、在释放 / 返回 / 作用域之后使用以及双重释放。
  • LeakSanitizer (LSan) 可检测内存泄漏。 LeakSanitizer 就像一个保守的垃圾回收器,用于检查 是否有泄漏。无法从引用根到达的任何分配 (线程堆栈、线程寄存器、全局变量和 线程局部变量)视为已泄露。
  • ThreadSanitizer (TSan) 可检测数据争用(仅主机)。
  • UndefinedBehaviorSanitizer (UBSan) 可检测 依赖于未定义程序行为的具体问题。

Zircon 内核中具有以下排错程序行为:

  • 物理内存管理器 (PMM) 检查工具 (pmm_checker) 可检测 释放后使用 bug 和零散的 DMA。
  • Kernel AddressSanitizer (KASan) 将 AddressSanitizer 扩展到内核 与 PMM 协作开发代码。
  • Lockdep 是一种运行时锁定验证程序,用于检测锁定危险 例如死锁

默认情况下,会添加以下 C/C++ 编译选项,以检测或 防止在运行时出现错误:

  • -ftrivial-auto-var-init=pattern(请参阅 RFC)初始化 自动变量转换为非零模式,以公开与读取相关的 bug 未初始化的内存中
  • ShadowCallStackSafeStack 可强化 针对堆栈溢出来生成代码。

最后,Fuchsia 使用 libFuzzersyzkaller,用于执行覆盖率导向型 模糊测试。模糊测试工具与消毒程序的相似之处在于 尝试在运行时发现代码中的错误,这些错误通常用于 连词。模糊测试工具与排错程序的不同之处在于,模糊测试工具会尝试 强制将生产代码执行到可能暴露 bug 的路径中。

支持的配置

目前,本地 build 和 CI/CQ 中支持排错程序 以下配置:

  • bringup.x64
  • bringup.arm64
  • core.x64
  • core.arm64
  • zbi_tests.x64
  • zbi_tests.arm64

此外,排错程序适用于托管工具。

qemu 上通过 CI/CQ 进行上述所有测试 和 Intel NUC。其他平台没有使用排错程序进行测试,原因如下: 资源和容量问题,但您可以在本地在这些平台上进行测试 使用下文中的构建工作流程

//vendor 下定义的配置的其他 tryjobs 可能会显示在 某些已登录用户的 Gerrit 和 CI 控制台中。查找配置 名称中包含 -asan

上面列出的排错程序适用于 C/C++ 代码。此外,LSan 应用于 Rust 代码,用于检测 Rust 内存泄漏

排查排错程序问题

构建

Fuchsia 平台 build(树内)

如需重现排错程序 build,您可以使用 build 变体来启用排错程序:

fx set product --variant asan-ubsan --variant host_asan-ubsan

或者,您也可以选择仅对某些二进制文件进行插桩:

fx set product --variant asan-ubsan/executable_name

选择性插桩工作流有助于 完全插桩 build 不适合设备的硬件。

具体而言,要检测内核代码中的释放后使用 bug,您需要 启用内核 PMM 检查工具

树外构建

使用 Fuchsia 工具链进行编译时,只需传递 -fsanitize= 标志,用于指明要使用的排错程序。 请参阅编译器文档

创建包含插桩组件的 Fuchsia 软件包时,您需要执行以下操作: 请确保您的软件包包含所有运行时依赖项,包括 作为 Clang 工具链的一部分分发的排错程序运行时;以及 插桩 C 库,作为 Fuchsia SDK 的一部分进行分发 sysroot 下。

测试

像往常一样在本地工作流中或在具有 已启用排错程序(tryjob 的名称中包含 asan)。如果清理程序检测到 系统就会将消息输出到包含 以下字符串:

  • ERROR: AddressSanitizer
  • ERROR: LeakSanitizer
  • SUMMARY: UndefinedBehaviorSanitizer
  • WARNING: ThreadSanitizer

在这些消息之后,您会看到可表明 找出问题所在并找出根本原因您可以在以下位置找到这些消息: fx log

请注意,触发排错程序的测试可能仍会显示为已通过。 排错程序问题不会表现为测试失败。

排错程序检测到的问题通常具有类似的根本原因。您可能是 通过搜索 Fuchsia 错误来找到以前工作的参考资料 出现了错误。

已知问题

#[should_panic]

Fuchsia 的 Rust panic! 上构建了中止。这与 减小二进制文件的大小。遗憾的是,使用 #[should_panic] 属性可能会错误地检测内存泄漏。这些测试会发出 在预期之内的 panic,然后在未展开的情况下退出,这意味着它们 堆分配。对于 LeakSanitizer 而言,无法辨别 内存不足导致的

如果此问题影响您的测试,您可以在排错程序 build 中停用它,方法是 请参阅此示例

请参阅:问题 88496:Rust 测试应该触发 leaksanitizer

最佳做法

确保通过测试来演练您的代码

清理程序会在运行时公开错误。除非您的代码在运行,例如在测试中运行 或者一般通过 CI/CQ、排错程序执行的方式在 Fuchsia 上进行 并不能暴露代码中的错误

确保排错程序覆盖率的最佳方法是确保测试 它们的覆盖范围请查阅有关测试的指南 覆盖率

请勿在代码中抑制排错程序

对于某些构建目标,排错程序可能会抑制。最常见的是 用于排错程序支持推出之前的问题, 针对不属于 Fuchsia 项目的第三方代码中的问题。

被禁排毒程序应被视为技术债务,因为它们不仅隐藏了老化剂, 但会阻止您发现在代码中引入的新 bug。 理想情况下,不应添加新的限制,而应采用现有的抑制 并修复底层错误

您可以通过修改 BUILD.gn 文件来抑制排错程序,该文件定义了 您的可执行目标,如下所示:

executable("please_fix_the_bugs") {
  ...
  # TODO(https://fxbug.dev/42074368): delete the below and fix the memory bug.
  deps += [ "//build/config/sanitizers:suppress-asan-stack-use-after-return" ]
  # TODO(https://fxbug.dev/42074368): delete the below and fix the memory bug.
  deps += [ "//build/config/sanitizers:suppress-asan-container-overflow" ]
  # TODO(https://fxbug.dev/42074368): delete the below and fix the memory leak.
  deps += [ "//build/config/sanitizers:suppress-lsan.DO-NOT-USE-THIS" ]
}

以上示例展示了如何抑制所有排错程序。不过,您应该在 大多数情况下都会抑制导致故障的排错程序。请跟踪抑制情况 方法是提交 bug 并在评论中引用该 bug,如上文所示。

停用排错程序的另一种常见方法如下所示:

executable("too_slow_when_built_with_asan") {
  ...
  exclude_toolchain_tags = [ "asan" ]
}

上述两个示例都按整个可执行文件的粒度来抑制。 您可以检测 代码。这对于在特定测试中抑制排错程序非常有用 但不是宽泛的范围。例如,特意使用此参数的测试 引入内存错误并测试排错程序运行时本身。

对于 C/C++,请参阅:

对于 Rust,您可以遵循以下模式:

#[cfg(test)]
mod tests {
    #[test]
    // TODO(https://fxbug.dev/42074368): delete the below and fix the leak
    #[cfg_attr(feature = "variant_asan", ignore)]
    fn test_that_leaks() {
        // ...
    }
}

测试不稳定性

如果被测代码的行为 不确定性。例如,内存泄漏可能仅在某些竞态条件下发生, 条件。如果清理程序错误看起来不稳定,请参阅有关测试 CQ 不稳定

提交优质错误

遇到排错程序问题时,请提交包含所有 问题排查信息

示例:问题 73214:ASAN use-after-scope in blobfs

bug 报告包含:

  • 排错程序(在本例中为 ASan)提供的错误。
  • 有关如何构建和以便重现错误。
  • 随后的调查详情,其中特定代码指针为 所需的资源。
  • 引用相关更改,例如,在本例中,为解决 导致错误的根本原因

路线图

正在进行的工作:

未来工作领域:

  • ThreadSanitizer (TSan):检测数据争用。
  • 对检测并发 bug 的内核支持。
  • 扩展对 Rust 的排错程序支持,例如检测内存安全 bug 在 Rust unsafe {} 代码块中或在 FFI 调用之间,或者 来检测未定义的行为 bug。
  • MemorySanitizer (MSan):检测 未初始化内存。

另请参阅:2021 年路线图中的排错程序