推测返回栈溢出 (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