Page 27 - 《软件学报》2024年第6期
P. 27
陈金宝 等: DBI-Go: 动态插桩定位 Go 二进制的非法内存引用 2603
本 (Go1.20.5). 在对漏洞覆盖率测试中, 我们会根据图 1 中每个 issue 所描述的 Go 版本来选取相应版本的 Go 编译
器. 最终结果显示 DBI-Go 在这些 Go 版本中均可正常运行 (Go1.11 至 Go1.20.5).
5 DBI-Go 的局限性讨论
综合来看, DBI-Go 仍有进一步优化的优化空间. 对此, 本节列出来了目前 DBI-Go 的一些局限性及一些可能
的解决思路.
• 动态分析工具的代码覆盖率问题. DBI-Go 是基于二进制插桩的动态检测器, 因此它无法检测 Go 程序中未
被执行的路径中的 store 指令. 这是所有动态分析工具不得不面对的问题. 为了改善这种情况, 可以使用基于代码
覆盖率的模糊测试等技术来提升代码覆盖率.
• 现有规则可进一步扩充. DBI-Go 的运行时验证部分基于规则 1 和规则 2. 其中规则 2 基于 Go 的逃逸不变式
2 “指向栈对象的指针生命期不可超出该栈对象”总结得到. 目前规则 2 所考虑的“生命期超出该栈对象”的情况分
为 5 种: 全局对象、堆对象、更深的栈对象、栈帧更深的栈对象以及其他 Goroutine 栈对象. 但实际上“生命期超
出该栈对象”还有其他情况, 比如更浅的作用域中的栈对象等. 由于这些情况在二进制中不好识别, 目前规则 2 未
无法在完全剥离符号信息的二进制上进行分析.
考虑. 因此规则 2 目前仍有进一步扩充的空间.
• 误报无法完全消除. 目前基于 gcWriteBarrier 的 store 语义恢复机制只适用于栈到堆或全局的 store 指令. 违
反逃逸不变式的栈到栈的 store 指令的检测目前仍需要插桩二进制中的所有 store 指令. 尽管目前已做了一些筛选,
比如不插桩运行时函数中的 store, 跳过汇编函数等. 但由于缺乏 Go 的高层语义信息, 比如类型描述符, 此时仍有
可能带来误报. 同时, gcWriteBarrier 是 Go 编译器在编译期间在源代码层级上增加的调用, 其以对象为粒度, 而非
二进制中的指令. 若某对象中既包含指针类型也包含非指针类型 (如 uintptr), 当该非指针类型的值等于某个栈指
针时, 由于 DBI-Go 假定其为指针, 此时仍有可能产生误报. 若要杜绝此类问题, 一种可能的方法是利用 Go 运行时
中的 bitmap. 该 bitmap 可以指示内存中何处是指针, 何处不是指针, Go 的 GC 会利用该 bitmap 进行标记和清扫.
但引入 bitmap 会导致 DBI-Go 和 Go 的不同版本运行时强绑定, 降低其可扩展性, 同时还会进一步增大 DBI-Go 的
额外开销. 因此, 综合考虑, 由于目前 DBI-Go 的误报率在可接受的范围中, 故没有引入 bitmap 机制.
• ABI 需要根据不同 Go 版本进行适配. DBI-Gos 实现的其中关键一点在于利用 Go 的 ABI 获得 Goroutine 运
行时栈信息. 目前 Go1.17 及以上版本采用现行的 ABI-Internal, 而 Go1.16.15 及以下则是另一套 ABI. 为了保证适
用性, 目前 DBI-Go 针对不同版本做了适配. 若未来 Go 的 ABI 发生进一步变化, 则 DBI-Go 也需要进行相应的适
配. 不过需要注意的是, Go 运行时中 Goroutine 的栈结构比较稳定, 其结构从 2014 年的 Go1.4 到 2023 年最新的
Go1.20 版本均未发生变化. 这意味着未来只需对 ABI 进行适配, 而使用 Goroutine 栈信息的运行时验证部分则无
需更改.
• 依赖 Go 的写屏障机制的正确性. DBI-Go 的分析目前依赖于 Go 编译器在编译期间插入的 gcWriteBarrier 函
数, 若 Go 编译器由于误判等原因没有对某 store 指针到堆的操作插入 gcWriteBarrier 函数, 则 DBI-Go 无法判断是
否有违反逃逸不变式的情况.
• 需要二进制上的符号信息. DBI-Go 需要二进制中的符号信息才能正常工作, 包括函数名、全局变量名等. 目
前 DBI-Go
• 额外开销可进一步降低. 在额外开销方面, 目前回调函数的额外开销仍有继续优化的空间. Pin 作为一个通
用的动态二进制分析框架, 没有针对 Go 的特定优化. DBI-Go 在设计实现时本着快速原型化的理念, 也未针对 Go
做大量优化. 目前 DBI-Go 所做的优化有使用基于匹配的方式跳过 Go 的运行时函数和一些非 Go 函数 (如一些汇
编函数、C 函数) 以及基于 gcWriteBarrier 机制只插桩可能 store 指针的指令. 未来基于 Go 语言的特性可以探索
更多的优化方式, 减少 Pin 插桩带来的额外开销.
• 无法自动修复漏洞. DBI-Go 只是一个 Go 二进制中的漏洞检测器, 它并不能自动修复检测到的漏洞. 漏洞产
生的原因有很多种, 比如逃逸分析的错误、编译优化的错误等. 通过 DBI-Go 检测出的漏洞需要 Go 编译器开发人
员的进一步分析和修复. 但 DBI-Go 的 log 可以帮助开发人员更快的确认漏洞出现的位置及原因.