go cmd/asm, cmd/compile: 添加对控制流完整性的支持

sshcrbum  于 2个月前  发布在  Go
关注(0)|答案(9)|浏览(38)

现代CPU通过Intel的间接分支追踪(IBT)和AARCH64的分支追踪识别(BTI)提供分支追踪控制流完整性(BTCFI)的支持。BTCFI有助于减轻基于流控制的漏洞,例如通过Jump Oriented Programming(JOP)实现的漏洞。

一些操作系统(例如,OpenBSD 7.4及更高版本)开始在硬件上强制执行IBT和BTI(从Intel Tigerlake及更高版本,到Apple的M2等)。Go二进制文件需要标记为PT_OPENBSD_NOBTCFI,以允许它们执行。其他操作系统支持程度不同(Linux在内核中启用了IBT,但用户空间AIUI似乎没有启用- https://lwn.net/Articles/889475/)。

理想情况下,Go应该支持BTCFI - 在IBT/BTI的情况下,实际上是提供一个“着陆垫”指令(endbr64amd64bti c上,以及arm64上的DUFFCOPYDUFFZERO),在函数入口点和其他控制流预期着陆的位置。对于不支持这些指令的机器(或在用户空间中启用/强制执行BTCFI),它们实际上是无操作。这确实可能影响有意跳入某个偏移量的函数的代码 - 例如,duff设备(DUFFCOPYDUFFZERO)和跳转表。这还需要在每个潜在的入口点处放置着陆垫,或者重新审视/禁用。

nhaq1z21

nhaq1z211#

https://go.dev/cl/568435提到了这个问题:cmd/link,debug/elf: mark Go binaries with no branch target CFI on openbsd

tct7dpnv

tct7dpnv2#

你要求这个程序有多好?为了比较,甚至不要默认启用ASLR。(-buildmode=pie大大增加了构建大小)
由于goroutine预设,我的第一猜测是所有指令都需要标记为有效的跳转目标,这使得值变得非常可疑。即使我们只标记安全点和预期的跳转目标,仍然有很多地方。

64jmpszr

64jmpszr3#

BTCFU是C和C++的临时解决方案,由于它们的设计缺陷和对未定义行为的允许,导致了ROP漏洞。如果不使用unsafe,那么在Go中就不应该出现ROP漏洞。这可能会影响Go程序的性能,并再次证明非C编程语言需要支付“C税”。这个特性应该继续保持可选。

此外,我认为现在我们不得不引入一个“来自”汇编指令,仅仅是因为C的关键设计缺陷,这是非常疯狂的。相反,我们应该致力于消除C的所有地方,包括操作系统中。

gab6jxml

gab6jxml4#

我有一个branch,它为arm64(以BTI的形式)和amd64(以IBT的形式)的Go添加了分支跟踪控制流完整性(BTCFI)。

hgc7kmma

hgc7kmma5#

由于goroutine预处理,我的第一猜测是所有指令都需要标记为有效的跳转目标,这使得值变得非常可疑。即使我们只标记安全点和预期的跳转目标,仍然有很多地方。
BTCFI通常只跟踪间接分支。在异步抢占的情况下,对于amd64没有变化,因为它通过RET恢复,该地址从堆栈中提取(在强制执行IBT的情况下是可以允许的)。在arm64的情况下,当前代码使用JMP (R27),这是一个间接分支,在强制执行BTI的情况下是不允许的-但可以通过使用具有特定寄存器的RET轻松修复。

mwg9r5ms

mwg9r5ms6#

@4a6f656c 感谢你的回答,我之前从你的代码中无法理解。
我认为 RET 会被认为是一种间接分支。
但是这是否意味着 IBT 对面向返回编程没有任何影响?

z18hc3ub

z18hc3ub7#

如果不使用unsafe,那么在Go中就不应该出现ROP。这可能会对Go程序的性能产生负面影响,并再次证明了非C编程语言需要支付“C税”。这当然应该保持为可选。
这也意味着Go的编译器或运行时没有错误,C代码永远不会被调用(提示:不要在macOS上使用Go),并且Go永远不会从C代码中被调用(因为这意味着C代码无法强制执行分支跟踪,因为Go代码不支持它)。
此外,我认为现在我们不得不引入一个“来自”汇编指令,仅仅是因为C的关键设计缺陷,这是疯狂的。相反,我们应该努力消除C的所有地方,包括操作系统中。
这不是一个“来自”,而是一个“允许间接分支到这里”的指令。

ogq8wdun

ogq8wdun8#

将汇编例程添加到不可使用的事项列表中,以避免ROP攻击。这些例程在运行时和一些第三方软件中被所有平台使用。

m0rkklqb

m0rkklqb9#

@4a6f656c 感谢你的回答,我之前从你的代码中无法理解。我原以为 RET 会被认为是一种间接分支。但这是否意味着 IBT 对面向返回编程没有任何影响?
是的,它是一种前向边缘流控制,而不是反向边缘流控制(原始描述本应该说的是面向跳转编程,我已经修复了这个问题)。

相关问题