推测返回栈溢出 (SRSO)¶
这是针对 AMD 处理器上发现的推测返回栈溢出 (SRSO) 漏洞的缓解措施。 该机制现在是众所周知的 CPU 功能单元中毒场景——在这种情况下是分支目标缓冲区 (BTB) 和返回地址预测器 (RAP)——然后欺骗提升的权限域(内核)泄漏敏感数据。
AMD CPU 使用返回地址预测器(又名返回地址堆栈/返回堆栈缓冲区)来预测 RET 指令。 在某些情况下,非架构 CALL 指令(即,预测为 CALL 但实际上不是 CALL 的指令)可以在 RAP 中创建一个条目,该条目可用于预测后续 RET 指令的目标。
导致这种情况的具体情况因微架构而异,但令人担忧的是,攻击者可以错误地训练 CPU BTB 来预测内核空间中的非架构 CALL 指令,并使用它来控制后续内核 RET 的推测目标,从而可能导致通过推测侧信道泄露信息。
该问题在 CVE-2023-20569 下进行跟踪。
受影响的处理器¶
AMD Zen,第 1-4 代。 也就是说,所有系列 0x17 和 0x19。 尚未调查旧的处理器。
系统信息和选项¶
首先,需要加载最新的微码才能使缓解措施生效。
显示 SRSO 缓解状态的 sysfs 文件是
/sys/devices/system/cpu/vulnerabilities/spec_rstack_overflow
此文件中的可能值是
“未受影响”
处理器不受漏洞影响
“易受攻击”
处理器易受攻击,并且未应用任何缓解措施。
“易受攻击:没有微码”
处理器易受攻击,没有应用扩展 IBPB 功能以解决漏洞的微码。
“易受攻击:安全 RET,没有微码”
已应用“安全 RET”缓解措施(见下文)以保护内核,但尚未应用扩展 IBPB 的微码。 用户空间任务可能仍然容易受到攻击。
“易受攻击:微码,没有安全 RET”
已应用扩展 IBPB 功能的微码补丁。 它不解决用户->内核和来宾->主机转换保护,但它确实解决用户->用户和 VM->VM 攻击向量。
请注意,用户->用户缓解措施受 Spectre v2 缓解措施中 IBPB 方面的选择方式控制
有条件的 IBPB
其中每个进程都可以选择是否需要在其周围发布 IBPB PR_SPEC_DISABLE/_ENABLE 等,请参阅 幽灵侧信道
严格
即,始终开启 - 通过在内核命令行上提供 spectre_v2_user=on
(spec_rstack_overflow=microcode)
“缓解:安全 RET”
组合的微码/软件缓解。 它通过解决用户->内核和来宾->主机转换保护来补充扩展的 IBPB 微码补丁功能。
默认选择或通过 spec_rstack_overflow=safe-ret 选择
“缓解:IBPB”
与上面的“安全 RET”类似的保护,但在权限域交叉(用户->内核,来宾->主机)上采用 IBPB 屏障。
(spec_rstack_overflow=ibpb)
“缓解:VMEXIT 上的 IBPB”
解决云提供商场景的缓解措施 - 仅针对来宾->主机转换。
(spec_rstack_overflow=ibpb-vmexit)
为了利用漏洞,攻击者需要
获得对机器的本地访问权限
破坏 kASLR
在正在运行的内核中查找小工具,以便在漏洞利用中使用它们
根据微架构(在 fam 0x19 上不是必需的),可能需要在兄弟线程上创建并固定额外的工作负载
运行漏洞利用程序
考虑到每种缓解类型的性能影响,默认类型是“缓解:安全 RET”,它应该处理大多数攻击向量,包括本地用户->内核攻击向量。
与往常一样,建议用户通过定期应用软件更新来保持其系统最新。
当需要时,特别是当出现新的攻击向量时,将重新评估默认设置。
正如人们可以推测的那样,“缓解:安全 RET”确实会带来一些性能成本,具体取决于工作负载。 如果信任其用户空间并且不想承受性能影响,则始终可以使用 spec_rstack_overflow=off 禁用缓解措施。
类似地,“缓解:IBPB”是另一种完整的缓解类型,在为系统应用所需的微码补丁后,采用间接分支预测屏障。 这种缓解措施也会带来性能成本。
缓解:安全 RET¶
缓解措施的工作原理是确保所有 RET 指令都推测到受控位置,类似于在 retpoline 序列中如何控制推测。 为了实现此目的,__x86_return_thunk 强制 CPU 使用“安全返回”序列错误预测每个函数返回。
为了确保此缓解措施的安全性,内核必须确保安全返回序列本身不受攻击者干扰。 在 Zen3 和 Zen4 中,这是通过在非训练函数 srso_alias_untrain_ret() 和安全返回函数 srso_alias_safe_ret() 之间创建 BTB 别名来完成的,这会导致驱逐潜在中毒的 BTB 条目并为所有函数返回使用该安全条目。
在旧的 Zen1 和 Zen2 中,这是使用类似于 Retbleed 的重新解释技术来完成的:srso_untrain_ret() 和 srso_safe_ret()。
检查安全 RET 缓解措施是否实际有效¶
如果有人想要验证 SRSO 安全 RET 缓解措施是否在内核上工作,则可以使用两个性能计数器
PMC_0xc8 - 已退役的 RET/RET lw 的计数
PMC_0xc9 - 已退役的 RET/RET lw 的错误预测计数
并比较内核模式下正确退役的 RET 数量与错误预测退役的 RET 数量。 指定这些事件的另一种方法是
# perf list ex_ret_near_ret
List of pre-defined events (to be used in -e or -M):
core:
ex_ret_near_ret
[Retired Near Returns]
ex_ret_near_ret_mispred
[Retired Near Returns Mispredicted]
使用事件助记符的命令
# perf stat -e ex_ret_near_ret:k -e ex_ret_near_ret_mispred:k sleep 10s
或使用原始 PMC 数字
# perf stat -e cpu/event=0xc8,umask=0/k -e cpu/event=0xc9,umask=0/k sleep 10s
应该给出相同的数量。 即,每个退役的 RET 都应该被错误预测
[root@brent: ~/kernel/linux/tools/perf> ./perf stat -e cpu/event=0xc8,umask=0/k -e cpu/event=0xc9,umask=0/k sleep 10s
Performance counter stats for 'sleep 10s':
137,167 cpu/event=0xc8,umask=0/k
137,173 cpu/event=0xc9,umask=0/k
10.004110303 seconds time elapsed
0.000000000 seconds user
0.004462000 seconds sys
与禁用缓解措施 (spec_rstack_overflow=off) 或无法正常工作的情况相比,通常显示错误预测的退役 RET 数量比工作负载期间退役的 RET 总数小得多
[root@brent: ~/kernel/linux/tools/perf> ./perf stat -e cpu/event=0xc8,umask=0/k -e cpu/event=0xc9,umask=0/k sleep 10s
Performance counter stats for 'sleep 10s':
201,627 cpu/event=0xc8,umask=0/k
4,074 cpu/event=0xc9,umask=0/k
10.003267252 seconds time elapsed
0.002729000 seconds user
0.000000000 seconds sys
此外,还有一个自测程序可以执行上述操作,请转到 tools/testing/selftests/x86/ 并执行
make srso
./srso