推测性返回栈溢出(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 功能的微码补丁。它不解决用户->内核和访客->主机转换保护问题,但解决了用户->用户和虚拟机->虚拟机攻击向量问题。
请注意,用户->用户缓解受 Spectre v2 缓解中 IBPB 方面如何选择的控制
条件 IBPB
每个进程可以选择是否需要围绕其发出 IBPB(PR_SPEC_DISABLE/_ENABLE 等),详见Spectre 旁道
严格
即,始终开启——通过在内核命令行中提供 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)
“缓解:推测减少”
当选择了上述“VMEXIT 时的 IBPB”且 CPU 支持 BpSpecReduce 位时,此缓解措施会自动启用。
在具有 SRSO_USER_KERNEL_NO=1 CPUID 位的机器上,它会自动启用。在这种情况下,代码逻辑是切换到上述 =ibpb-vmexit 缓解,因为用户/内核边界不再受影响,因此不需要“安全 RET”。
启用 VMEXIT 时的 IBPB 缓解选项后,会检测到 BpSpecReduce 位(所有此类机器都具有此功能),这实际上会覆盖 VMEXIT 时的 IBPB,因为它对性能影响小得多,并且也能处理访客->主机攻击向量。
为了利用此漏洞,攻击者需要
在机器上获得本地访问权限
打破 kASLR
在运行中的内核中找到小工具以用于利用
可能在同级线程上创建并固定额外的工作负载,具体取决于微架构(在 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