地址消毒器

什么是地址消毒器?

地址消毒器 (ASan) 是一种快速内存错误检测器,用于检测 C/C++ 程序中的使用后释放和越界错误。它使用编译时插桩来检查执行期间的所有读写操作。此外,运行时部分替换了 mallocfree 函数以检查动态分配的内存。有关 ASan 工作原理的更多信息,请参阅 地址消毒器维基

一个名为 asan-maintenance 的元错误 用于跟踪使用 ASan 发现的所有错误。

下载构件版本

对于 Linux 和 Windows 用户,获取带有地址消毒器的 Firefox 版本的最简单方法是下载 mozilla-central 的持续集成 asan 版本(至少每天更新一次)

  • mozilla-central 优化版本:linux | windows(推荐用于测试)

  • mozilla-central 调试版本:linux | windows(如果优化版本无法解决问题,则推荐用于调试)

模糊测试团队还提供了一个名为 fuzzfetch 的工具来下载这些以及许多其他 CI 版本。它使下载和解压缩这些版本变得更加容易,不仅可以用于模糊测试,还可以用于需要 CI 版本下载的所有用途。

您可以从 Github通过 pip 安装 fuzzfetch

之后,您可以运行例如

$ python -m fuzzfetch --asan -n firefox-asan

以将上面提到的优化 Linux ASan 版本解压到名为 firefox-asan 的目录中。可以使用 --debug--os 开关获取上面列出的其他变体。

创建 Try 版本

如果由于某种原因您无法使用上一节中提到的预构建二进制文件(例如,您想要非 Linux 版本或需要测试补丁),您可以自己构建 Firefox(请参阅下一节)或使用 try 服务器 为您创建自定义版本。推送至 try 需要 L1 提交访问权限。如果您还没有此访问权限,您可以请求访问权限(请参阅 成为 Mozilla 提交者Mozilla 提交访问策略 以了解要求)。

该树包含 用于创建 asan 版本的多个 mozconfig 文件(“nightly-asan”文件创建发布版本,而“debug-asan”文件创建调试+opt 版本)。对于 Linux 版本,linux64-asan 目标使用相应的配置文件。如果要创建 macOS 或 Windows 版本,则需要在推送至 try 之前将相应的配置文件复制到常规调试配置上。例如

cp browser/config/mozconfigs/macosx64/debug-asan browser/config/mozconfigs/macosx64/debug

然后,您可以 以通常的方式推送至 Try,并在构建完成后下载相应的构建构件。

在 Windows 上创建本地版本

在 Windows 上,ASan 仅在 64 位版本中受支持。

运行 mach bootstrap 以在您的 ~/.mozbuild 目录中获取更新的 clang-cl,然后使用以下 mozconfig

ac_add_options --enable-address-sanitizer
ac_add_options --disable-jemalloc

export LDFLAGS="clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib"
CLANG_LIB_DIR="$(cd ~/.mozbuild/clang/lib/clang/*/lib/windows && pwd)"
export MOZ_CLANG_RT_ASAN_LIB_PATH="${CLANG_LIB_DIR}/clang_rt.asan_dynamic-x86_64.dll"
export PATH=$CLANG_LIB_DIR:$PATH

如果在 WinDbg 下启动 ASan 构建,您可能会看到虚假的首次机会访问冲突异常。这些来自 ASan 根据需要创建影子内存页面,可以忽略。运行 sxi av 以忽略这些异常。(如果确实崩溃,您仍然会捕获第二次机会访问冲突异常。)

LeakSanitizer (LSan) 在 Windows 上不受支持。

在 Linux 或 Mac 上创建本地版本

构建先决条件

LLVM/Clang

ASan 插桩作为 LLVM 传递实现,并集成到 Clang 中。任何能够编译 Firefox 的 clang 版本都具有执行 ASAN 构建所需的一切。

构建 Firefox

获取源代码

使用该版本或任何更高版本,您只需 获取 mozilla-central 的克隆

调整构建配置

在您的 mozilla-central 目录中创建具有以下内容的构建配置文件 mozconfig

# Combined .mozconfig file for ASan on Linux+Mac

mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/objdir-ff-asan

# Enable ASan specific code and build workarounds
ac_add_options --enable-address-sanitizer

# These three are required by ASan
ac_add_options --disable-jemalloc
ac_add_options --disable-crashreporter
ac_add_options --disable-elf-hack

# Keep symbols to symbolize ASan traces later
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debug-symbols
ac_add_options --disable-install-strip

# Settings for an opt build (preferred)
# The -gline-tables-only ensures that all the necessary debug information for ASan
# is present, but the rest is stripped so the resulting binaries are smaller.
ac_add_options --enable-optimize="-O2 -gline-tables-only"
ac_add_options --disable-debug

# Settings for a debug+opt build
#ac_add_options --enable-optimize
#ac_add_options --enable-debug

# MacOSX only: Uncomment and adjust this path to match your SDK
# ac_add_options --with-macos-sdk=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk

您可能还需要此内容,如 browser/config/mozconfigs/linux64/nightly-asan 中所示(用于自动化测试的地址消毒器构建使用的配置文件)

# ASan specific options on Linux
ac_add_options --enable-valgrind

开始构建过程

现在,您可以使用常规的 ./mach build 命令开始构建过程。

启动 Firefox

构建完成后,./mach run 以及在调试器中运行的常用选项(gdblldbrr 等)都可以正常工作,--disable-e10s 和其他选项也可以正常工作。

仅构建 JavaScript shell

如果只想构建 JavaScript shell 而不是执行完整的 Firefox 构建,以下构建脚本可能会帮助您做到这一点。在 js/src/ 子目录中执行此脚本,并将目录名称作为第一个参数传递。然后,构建将在具有该名称的新子目录中创建。

#! /bin/sh

if [ -z $1 ] ; then
     echo "usage: $0 <dirname>"
elif [ -d $1 ] ; then
     echo "directory $1 already exists"
else
     autoconf2.13
     mkdir $1
     cd $1
     CC="clang" \
     CXX="clang++" \
     CFLAGS="-fsanitize=address" \
     CXXFLAGS="-fsanitize=address" \
     LDFLAGS="-fsanitize=address" \
     ../configure --enable-debug --enable-optimize --enable-address-sanitizer --disable-jemalloc
fi

在地址消毒器跟踪中获取符号

默认情况下,ASan 跟踪未进行符号化,仅打印二进制文件/库和内存偏移量。为了获得包含符号的更有用的跟踪,有两种方法。

使用 asan_symbolize.py 后处理跟踪

除了使用 llvm-symbolizer 二进制文件之外,您还可以将输出通过 LLVM 附带的 asan_symbolize.py 脚本($LLVM_HOME/projects/compiler-rt/lib/asan/scripts/asan_symbolize.py)传递,LLVM 发行版中通常包含此脚本。缺点是该脚本将需要使用 addr2line 来获取符号,这意味着每个库都必须加载到内存中(包括``libxul``,这需要一些时间)。

但是,在某些情况下,使用此脚本是有意义的。例如,如果您有/收到了未符号化的跟踪,那么您仍然可以使用该脚本将其转换为符号化跟踪,前提是您可以获取生成未符号化跟踪的原始二进制文件。为了使脚本在这些情况下工作,您需要确保跟踪中的路径指向实际的二进制文件,或相应地更改路径。

由于 asan_symbolize.py 脚本的输出仍然被混淆,因此您可能希望随后也通过 c++filt 传递输出。

故障排除/已知问题

生成多个输出文件时无法指定 -o

如果从 clang 收到错误“cannot specify -o when generating multiple output files",请在您的 mozconfig 中禁用 elf-hack 以解决此问题

ac_add_options --disable-elf-hack

优化版本

由于 与 -O2/-Os 和 ASan 的问题 已解决,Firefox 使用的常规优化应该可以正常工作。优化版本仅存在几乎可以忽略不计的速度损失,并且似乎甚至比常规调试版本更快。

运行 ./mach run 后未显示“AddressSanitizer: libc interceptors initialized”

$ ASAN_OPTIONS=verbosity=2 ./mach run

请改用以下命令

需要“管理员用户名和密码”才能进入开发者模式

请通过以下方式启用**开发者**模式

$ /usr/sbin/DevToolsSecurity -enable
Developer mode is now enabled.

调试 ASan 发现的问题

当 ASan 发现问题时,它只会打印错误消息并退出应用程序。要在 ASan 退出之前在调试器中停止应用程序,请在 __asan::ReportGenericError 上设置断点。有关使用 ASan 和调试其发现的问题的更多信息,请参阅上游维基上的页面 地址消毒器和调试器

在调试器提示符处或甚至直接在代码中发出__asan_describe_address(pointer) 可以输出关于此内存地址的大量信息(分配的线程和堆栈、释放的线程和堆栈、它是否位于已知缓冲区的外部、此缓冲区的分配线程和堆栈等)。例如,在执行 SIMD 工作时,这对于理解分配的一些未对齐缓冲区的位置很有用。

rr(仅限 Linux x86)与 ASan 配合使用效果极佳,并且结合起来,这种组合允许执行一些非常强大的调试策略。

LeakSanitizer

LeakSanitizer (LSan) 是常规 ASan 的一种特殊执行模式。它利用 ASan 如何在任何给定时间跟踪活动块集来打印出在关闭时仍然存活的任何块的分配堆栈,但根据保守扫描,这些块无法从堆栈访问。这对于检测诸如char*之类的泄漏非常有用,这些泄漏不参与通常的 Gecko 关闭泄漏检测。LSan 在 x86_64 Linux 和 OS X 上受支持。

从更新版本的 Clang 开始,LSan 在 ASan 构建中默认启用。要使 ASan 构建不运行 LSan,请将环境变量ASAN_OPTIONS设置为detect_leaks=0(或者如果它已设置为某些内容,则将其添加为:分隔列表中的一个条目)。如果您想在它由于某种原因未启用时启用它,请将其设置为 1 而不是 0。如果启用了 LSan 并且您使用的是非调试构建,您还需要设置环境变量MOZ_CC_RUN_DURING_SHUTDOWN=1,以确保我们运行关闭 GC 和 CC 以避免虚假泄漏。

如果 LSan 报告的对象有意从未释放,则可以将一个符号添加到build/sanitizers/lsan_suppressions.txt以使 LSan 忽略它。

有关 LSan 的更多信息,请参阅Leak Sanitizer wiki 页面

一个名为 lsan 的元错误用于跟踪使用 LSan 发现的所有错误。

关于 ASan 的常见问题

ASan 的工作原理是什么?

有关 ASan 工作原理的更多信息,请参阅Address Sanitizer wiki