BoundsSanitizer (BoundSan) 将插桩添加到二进制文件,以插入对数组访问的边界检查。如果编译器在编译时无法证明访问将会是安全的,并且在运行时将会知道数组的大小,便会添加这些检查,以便对数组访问进行检查。Android 10 在蓝牙和编解码器中部署了 BoundSan。BoundSan 由编译器提供,在整个平台的各个组件中默认启用。
实现
BoundSan 使用 UBSan 的边界排错程序。该缓解功能是在各个模块级别启用,有助于确保 Android 关键组件的安全性,因此不应停用。
我们强烈建议您为更多组件启用 BoundSan。理想的候选组件是特权原生代码或可解析不可信用户输入的复杂原生代码。与启用 BoundSan 相关的性能开销取决于无法证明安全的数组访问次数。预计开销所占的平均百分比会较小,您可以测试性能是否存在问题。
在 blueprint 文件中启用 BoundSan
您可以在 blueprint 文件中启用 BoundSan,方法是将 "bounds"
添加到二进制文件和库模块的 misc_undefined
sanitize 属性:
sanitize: { misc_undefined: ["bounds"], diag: { misc_undefined: ["bounds"], }, blacklist: "modulename_blacklist.txt",
diag
diag
属性用于为排错程序启用诊断模式。只能在测试期间使用诊断模式。诊断模式不会在溢出发生时中止,而这会抹杀该缓解功能的安全优势并导致更高的性能开销,因此不建议在正式版中使用。
blacklist
blacklist
属性用于指定黑名单文件,开发者可使用该文件来防止函数和源文件成为排错对象。仅当您担心性能且目标文件/函数有很大的影响时,才能使用此属性。手动审核这些文件/函数以确保数组访问安全无虞。如需了解详情,请参阅问题排查。
在 makefile 中启用 BoundSan
您可以在 makefile 中启用 BoundSan,方法是将 "bounds"
添加到二进制文件和库模块的 LOCAL_SANITIZE
变量:
LOCAL_SANITIZE := bounds # Optional features LOCAL_SANITIZE_DIAG := bounds LOCAL_SANITIZE_BLACKLIST := modulename_blacklist.txt
LOCAL_SANITIZE
接受以英文逗号分隔的排错程序列表。
LOCAL_SANITIZE_DIAG
用于开启诊断模式。只能在测试期间使用诊断模式。诊断模式不会在溢出发生时中止,而这会抹杀该缓解功能的安全优势并导致更高的性能开销,因此不建议在正式版中使用。
LOCAL_SANITIZE_BLACKLIST
属性用于指定黑名单文件,以便开发者防止函数和源文件成为排错对象。仅当您担心性能且目标文件/函数有很大的影响时,才能使用此属性。手动审核这些文件/函数以确保数组访问安全无虞。如需了解详情,请参阅问题排查。
停用 BoundSan
您可以使用黑名单或函数属性在函数和源文件中停用 BoundSan。建议将 BoundSan 保留为启用状态,因此只有在函数或文件产生大量性能开销并且已手动审核源文件时才停用 BoundSan。
如需详细了解如何使用函数属性和黑名单文件格式设置停用 BoundSan,请参阅 Clang LLVM 文档。应将黑名单的作用范围限定为特定的排错程序,方法是使用区块名称指定目标排错程序,以免影响其他排错程序。
验证
没有专门针对 BoundSan 的 CTS 测试。因此请确保 CTS 测试在启用或未启用 BoundSan 的情况下均能通过,以证明它不会给设备带来影响。
问题排查
在启用 BoundSan 之后全面测试组件,以确保任何先前未检测到的出界访问得到解决。
BoundSan 错误可以轻松识别,因为此类错误包含以下 tombstone 中止消息:
pid: ###, tid: ###, name: Binder:### >>> /system/bin/foobar <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'ubsan: out-of-bounds'
在诊断模式下运行时,会将源文件、行号和索引值输出到 logcat
。默认情况下,此模式不会抛出中止消息。请查看 logcat
以检查是否存在任何错误。
external/foo/bar.c:293:13: runtime error: index -1 out of bounds for type 'int [24]'