MIT 6.1810 · 第 23 课 · rtm

Meltdown 攻击

Meltdown 攻击利用 CPU 的推测执行和缓存侧信道,允许恶意用户代码读取内核内存,绕过页保护。

授课教师:rtm (Robert T. Morris)

年份:2025 秋季

难度:困难

论文:Meltdown: Reading Kernel Memory from User Space (Lipp et al., 2018)

安全是操作系统的顶级目标

为什么研究 Meltdown?

攻击核心

// 用户代码
char buf[8192]

r1 = <一个内核虚拟地址>
r2 = *r1            // ← 这会 page fault
r2 = r2 & 1
r2 = r2 * 4096
r3 = *(buf + r2)    // ← 推测执行!

如果用户代码尝试使用内核地址会怎样?

加载会导致 page fault。但这只是故事的一部分...

前提假设

内核映射在每个用户页表中,PTE_U 清除。

微架构特性 1:推测执行 (Speculative Execution)

示例

r0 = <某个地址>
r1 = load x    // r1 是寄存器;x 是 RAM 中的变量
if(r1 == 1){
    r2 = *r0
    r3 = r2 + 1
} else {
    r3 = 0
}

如果 CPU 预测错误?

推测执行帮助性能

避免 CPU 等待慢内存时的停顿。

关键洞察

推测是透明的

微架构特性 2:CPU 数据和 TLB 缓存

核心
L1:va,pa | data / TLB:va | pa  (VIPT)
L2:pa    | data
RAM
操作时间
L1 命中几周期
L2 命中~20 周期
RAM~300 周期
TLB 未命中数百周期

系统调用在内核返回后在 L1 缓存中留下内核数据

为什么 L1 包含内核数据时执行用户代码是安全的?

实际上,微架构并非完全不可见

有用的技巧:感知某物是否在缓存中

Flush+Reload

想知道函数 f() 是否使用了地址 Z 的内存?

  1. 确保 Z 不在缓存中(clflush 指令)
  2. 调用 f()
  3. 记录时间(rdtsc 指令)
  4. 从 Z 加载一个字节
  5. 再次记录时间
  6. 如果时间差 < ~50,加载命中 → f() 可能使用了 Z

Meltdown 详细分析

// 在用户内存中
char buf[8192]

// Flush+Reload 的 Flush 部分
clflush buf[0]
clflush buf[4096]

1.  r1 = <一个内核虚拟地址>
2.  r2 = *r1            // ← page fault(延迟)
3.  r2 = r2 & 1         // 推测执行
4.  r2 = r2 * 4096      // 推测执行
5.  r3 = *(buf+r2)      // 推测执行——加载 buf[0] 或 buf[4096]

<最终来自 *r1 的 fault;r2 和 r3 回滚,但数据留在缓存中>
<处理 page fault>

// Flush+Reload 的 Reload 部分
a = rdtsc
r0 = buf[0]
b = rdtsc
r1 = buf[4096]
c = rdtsc
if c-b < b-a:
    低位可能是 1

关键洞察

  1. "r2 = *r1" 的 fault 被延迟到加载退役,给后续推测执行指令时间执行
  2. "r2 = *r1" 确实做了加载,即使 PTE 禁止,将结果放入 r2(临时的)
  3. "r3 = buf[r2]" 将 buf[] 的一部分加载到缓存中,即使 r3 的更改因推测错误被取消

为什么攻击常常不工作?

第 6.2 节:如果内核数据未缓存,每秒 10 字节。

为什么攻击者觉得有用(即使通常不工作)?

安全游戏的规则:

Meltdown 在实际攻击中如何使用?

防御

软件修复

KAISER / KPTI:不要在用户页表中映射内核

硬件修复

推测加载只返回允许的数据!

这些防御已部署并被认为有效,但...

要点总结

推测执行是攻击的基础

CPU 推测执行指令,fault 被延迟,但微架构状态(缓存)保留。

缓存是侧信道

Flush+Reload 技巧通过测量加载时间推断缓存内容。

页保护被绕过

推测加载忽略 PTE 权限,将内核数据放入临时寄存器。

防御需要软硬件结合

KPTI 隔离页表,硬件修复阻止推测加载返回禁止数据。

测验

Q1:Meltdown 攻击如何利用推测执行?

Q2:Flush+Reload 技巧如何工作?

Q3:KPTI(内核页表隔离)如何防御 Meltdown?

延伸阅读