对 Retbleed(CVE-2022-29900 / CVE-2022-29901) 的动手复现——这是 2022 年的微架构攻击,它打破了“Retpoline”防御,证明了长期以来被认为安全的 ret 指令在 CPU 的返回栈缓冲区(RSB)下溢时可以被劫持。
本项目在 gem5 中的 x86 乱序 CPU 模型上模拟完整的攻击生命周期:触发 RSB 下溢,通过 Flush+Reload 缓存侧信道逐字节泄露机密数据,然后验证一种关闭该泄露的软件缓解措施(lfence)。
课程项目——高级计算机体系结构,2025 年秋季,CUNY City College 作者:Abdul Kalam Mansoor 与 Rebiha Selmani
Spectre 类攻击表明,即使 CPU“撤销”了错误猜测,推测执行仍会留下缓存级别的副作用。业界的修复方案——Retpoline——用 ret 指令替换了危险的间接跳转,其假设是返回地址可以由一个小型硬件栈(RSB)安全预测。Retbleed 表明这一假设是错误的:用深度递归耗尽 RSB,CPU 就会悄悄回退到 Retpoline 本应避免的同一个不安全预测器。
| 阶段 | 发生了什么 |
|---|---|
| 1. 触发 | rsb_deep_call() 递归 32 层深,溢出 16 项的 RSB。当递归展开时,RSB 为空。 |
| 2. 回退 | RSB 为空时,CPU 回退到分支目标缓冲区(BTB)来预测 ret 目标——而攻击者可以污染它。 |
| 3. 小工具 | 被劫持的推测路径执行 gadget(),它读取机密密码的一个字节,并用它索引 probe_array,将该数组的一页拉入缓存。 |
| 4. Flush+Reload 间谍 | 攻击前,probe_array 的每一页都从缓存中刷新(_mm_clflush)。推测窗口关闭后,程序对每个可能的字节值计时访问(__rdtscp)——返回快的那个(缓存命中)揭示了机密字节。 |
对 ROOT_PASSWORD 的每个字节重复此过程,就能在从未架构性地调用 gadget() 的情况下重建整个机密。
| 模式 | 访问延迟 | 结果 |
|---|---|---|
易受攻击(未定义 SECURE_MODE) | 约 49 个周期(缓存命中) | 机密逐字节泄露,由红色 HIT! 指示器确认 |
已修补(定义了 SECURE_MODE,注入 _mm_lfence()) | >150 个周期(缓存未命中 / 噪声) | 攻击失败——输出显示 SAFE / Found: ??? |
lfence 强制 CPU 在任何后续指令执行之前解析返回地址,在小工具触及依赖机密的内存之前就关闭了推测窗口。
src/
retbleed.c # 完整 PoC:触发、小工具、Flush+Reload 间谍和 lfence 缓解(通过 SECURE_MODE 切换)
docs/
Retbleed_Report.pdf # 完整书面报告:方法论、相关工作、gem5 设置、结果
Retbleed_Attack_Demonstration.pptx # 用于展示项目的幻灯片
该 PoC 在 gem5 下构建和测量(DerivO3CPU,一种乱序模型——这是必需的,因为顺序模型不实现推测执行):
gcc -O0 -static -o retbleed src/retbleed.c
./build/X86/gem5.opt configs/deprecated/example/se.py \
--cpu-type=DerivO3CPU --caches --l2cache \
--l1d_size=64kB --l1i_size=64kB --cmd=retbleed
要在易受攻击和已修补行为之间切换,请注释/取消注释 retbleed.c 顶部的这一行:
#define SECURE_MODE
代码使用真实的 x86 内建函数(
_mm_clflush、__rdtscp、_mm_lfence),也可以直接在 x86 硬件上编译和运行(gcc -O0 -o retbleed src/retbleed.c)以进行更快的演示——结果会因主机 CPU 自身的缓解措施(eIBRS、微码补丁等)而异,这本身就很好地说明了自 2022 年以来这类漏洞已被修补得多么彻底。
lfence)在受影响的硬件上测得 14–39% 的性能开销。