Page 23 - 《软件学报》2024年第6期
P. 23

陈金宝 等: DBI-Go: 动态插桩定位 Go 二进制的非法内存引用                                             2599


                 复现代码并可复现的        issue  共有  5  个, 分别为  issue#29000  [37] , issue#31573 [38] , issue#44614 [15] , issue#47276  [39] 和
                 issue#54247 [14] . 其涉及的  Go  版本为  Go1.11  至  Go1.17, 年份跨度为  2018–2022  年. 最终的结果显示, DBI-Go  的漏
                 洞覆盖率较高, 其可以检测出图          1  中所有可复现的例子中的违反         Go 逃逸不变式的     store 指令. 输出的  log  文件相
                 比于  Go  原始  GC panic 时产生的信息可以更清晰地展示出产生错误的指令及其所在的函数, 可以帮助更快定位原
                 因. 以  issue#44614  [15] 为例, 其简化版代码可见代码  3. 图  8(a) 为社区  issue 中  Go GC  的  log, 图  8(b) 为  DBI-Go  的
                 log. 相比于  GC  的  log, 其可以更精确的显示问题的产生地, 比如二进制指令地址以及所在的函数. 若能结合
                 DWARF  信息, 还可以找到对应的源代码的位置, 更便于问题定位.

















                                  (a) issue 中 GC log                    (b) DBI-Go log
                                                  图 8 GC log  与  DBI-Go log

                    Go  的标准库   (std) 和编译工具链   (cmd) 提供了大量测试用例和        Benchmark, 覆盖了其中的众多常用       API. Go
                 的标准库和编译工具链中共有           277  个包提供了测试用例. 我们使用最新版本的            Go (Go1.20.5), 将这些测试用例和
                 Benchmark  编译成可执行文件, 并随后使用       DBI-Go  进行检测. 结果表明, 在这     277  个包中的  276  个没有发现问题,
                 然而, 在  syscall 包中, DBI-Go  发现  Go  在处理切片的字面量时将栈上的数组地址存到了全局变量中. 它的简化版
                                                                                    中, 接下来的
                                                               0x8(SP) 存入了寄存器
                 示例如图    9  所示. LEAQ 0x8(SP), AX issue, 有待 Go 官方的进一步修复                               指令
                                              指令将栈对象的地址
                                                                                               MOVQ
                                                                                 AX
                 将该栈地址    store 到了某全局变量处. 该     store 违反了规则  1  并被  DBI-Go  所捕获.

                                                      经Go编译器
                                                        编译





                                        图 9 syscall 包中发现的违反 Go 逃逸不变式的 store

                    目前该问题已经在        golang-nuts 中得到  Go 官方维护人员的确认      (https://groups.google.com/g/golang-nuts/
                 c/YZVFzwnPixM), 并已向社区提交                              (https://github.com/golang/go/issues/61730).
                    除了上述对使用       Go  原生逃逸分析算法的编译器生成的二进制的测试, 我们还将                    DBI-Go  用于评测一个在
                 Gollvm (一个基于 LLVM 的 Go 编译器) 上重构的       Go  逃逸分析算法. 通过用     DBI-Go  检测经重构逃逸分析算法后
                 的  Gollvm  生成的二进制中是否有非法的内存引用, 来判断重构后的新逃逸分析算法的正确性, 并辅助开发人员进
                 行 Debug. 在实际评测中, 用    DBI-Go  可以发现新逃逸分析算法引起的内存分配问题. 以代码                 5  和代码  6  为例, 代
                 码  5  中的 New  函数将字面量   prefixError{}的地址返回出函数; 代码    6  中, 对象  b 的地址被全局对象    gm  获取, 根据
                 逃逸不变式    2  它们理应堆分配. DBI-Go    发现重构后的逃逸分析算法在特定场景下会将代码                  5  中本应在堆中分配
                 的  prefixError{}以及代码  6  中本应在堆中分配的    b  均分配在栈上. 通过这些例子, DBI-Go      帮助新逃逸算法的开发
   18   19   20   21   22   23   24   25   26   27   28