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  可以帮助开发人员更快的确认漏洞出现的位置及原因.
   22   23   24   25   26   27   28   29   30   31   32