跳转到内容

RowHammer 2014 — 反复读一行内存也能翻转邻居比特

待复核

RowHammer 是一种只靠反复访问某些 DRAM 行,就可能让旁边行里的比特翻转的硬件可靠性问题。日常类比:你没有碰隔壁家的门,但一直重重拍自己家的墙,震动多了,隔壁墙上挂的画可能掉下来。

这篇论文第一次系统证明:这不是实验室传说,而是大量商品 DDR3 内存模块里真实存在的现象。作者在 129 条内存模块里测试,110 条能被诱发错误;最少约 139K 次行激活就可能让一个邻近单元出错。

安全上最吓人的点是:软件表面上只是在读自己能访问的地址,却可能改坏自己无权访问的物理邻居。内存隔离本来像楼房里的防火门,RowHammer 说明墙体材料本身也会互相影响。

不理解 RowHammer,下面这些事都很难解释:

  • 为什么“读操作不会改数据”这个基础假设,在真实硬件上也要重新审视。
  • 为什么云服务器和浏览器里的软件漏洞,有时要追到 DRAM 物理排列和刷新策略。
  • 为什么 ECC 不是银弹:单比特能纠,多个比特落在同一个 64-bit word 里就可能失守。
  • 为什么后来硬件厂商做 target row refresh、内存控制器限速、云厂商强制 ECC,都和这篇论文有关。

RowHammer 可以拆成 三件事

  1. DRAM 单元像会漏水的小杯子。每个 bit 靠电容里有没有电荷表示 0/1,电荷会慢慢漏,所以 DRAM 必须定期 refresh。论文默认的 DDR3 刷新窗口是 64ms。

  2. 反复开关同一行会扰动邻居。访问某一行时,wordline 会升压再降压;如果一行被高速打开、关闭很多次,邻近行里某些单元会加速漏电。类比:你每次开关大铁门,旁边薄门框都会被震一下。

  3. 问题同时是可靠性问题和安全问题。可靠性视角看,这是 bit flip;安全视角看,这是越过地址权限边界的“间接写”。论文的贡献在于把物理现象、用户态演示、FPGA 大规模表征和低开销防御放在一起。

案例 1:用玩具模型理解“邻居漏电”

Section titled “案例 1:用玩具模型理解“邻居漏电””
const charge = [64, 64, 64, 64, 64]
function activate(row: number) {
if (row > 0) charge[row - 1] -= 1
if (row + 1 < charge.length) charge[row + 1] -= 1
}
for (let i = 0; i < 140_000; i++) activate(2)
console.log(charge)

逐部分解释

  • charge 不是论文里的真实电路,只是把“电荷余量”画成数组。
  • activate(2) 表示第 2 行被反复打开,左右邻居每次都被轻微影响。
  • 循环次数变大后,邻居累积损伤;RowHammer 的关键也在“单次很小,累积很大”。

案例 2:为什么只读缓存不够,必须打到 DRAM

Section titled “案例 2:为什么只读缓存不够,必须打到 DRAM”
function readLoop(useCache: boolean) {
for (let i = 0; i < 1_000_000; i++) {
read(addressX)
if (!useCache) evictFromCache(addressX)
}
}

逐部分解释

  • 如果数据一直留在 CPU cache,后续读不会真的打开 DRAM 行。
  • 论文的用户态演示用 cache flush 让访问落回 DRAM,这样内存控制器才会反复发 ACT/PRE。
  • 这段是概念伪代码,不是攻击教程;重点是区分“读了变量”和“真的激活 DRAM 行”。
function closeRow(row: number) {
if (Math.random() < 0.001) {
refresh(randomAdjacentRow(row))
}
}

逐部分解释

  • PARA 的想法很朴素:每次关闭一行时,用很小概率刷新邻近行。
  • 攻击者如果疯狂敲同一行,随机事件会重复很多次,邻居迟早被刷新。
  • 它不需要给每一行都放计数器,所以硬件开销比“记录所有 hot rows”低。
  1. 把 RowHammer 理解成普通软件 bug:错在忽略 DRAM 单元之间的电气耦合;软件只是触发器,根因在硬件物理层。

  2. 以为“读”一定无副作用:错在把 ISA 语义等同于芯片行为;论文证明大量 ACT/PRE 会让邻居行失去电荷。

  3. 以为 ECC 一开就万事大吉:错在 SECDED 通常只保证纠一个、检两个;论文观察到同一 64-bit word 里可能有多位 victim。

  4. 以为提高全局 refresh 就是最好方案:错在性能和能耗代价很高;论文估算某些模块要把刷新频率提高 4.3 到 7.8 倍才安全。

适用

  • 理解 DRAM 可靠性、内存隔离、硬件安全交叉问题。
  • 评估云服务器、浏览器沙箱、虚拟化环境里的物理内存攻击面。
  • 学“系统安全不是只看代码权限,还要看硬件副作用”这条线。

不适用

  • 不适合当成具体攻击复现指南;真实利用需要地址映射、内存布局和平台细节。
  • 不适合直接推断所有现代 DDR4/DDR5 都同样脆弱;后续标准和厂商防御已经变化。
  • 不适合用单次实验结果判断某条内存绝对安全;论文也强调 victim cell 搜索可能要多轮。
  • 1970s:DRAM 从早期商用芯片开始就有 disturbance 类问题,厂商主要靠电路改进和出厂测试压住风险。
  • 2012 年:RAIDR 等工作说明 DRAM refresh 的性能和能耗成本越来越重要,不能简单“全体更频繁刷新”。
  • 2014 年:Kim 等人在 ISCA 发表这篇论文,把“row hammer”从测试术语变成系统安全问题。
  • 2015 年以后:研究者陆续做出提权、浏览器、ECC、侧信道等方向的 RowHammer 攻击与防御。
  • 后来:target row refresh 一类硬件方案进入厂商实践,但后续论文也继续研究绕过和参数不足的问题。
  1. 抽象有边界:操作系统说“你不能写这里”,但 DRAM 物理邻居可能仍被你的访问节奏影响。
  2. 可靠性会变成安全性:一次 bit flip 看似只是硬件错误,落到页表、密钥、对象指针上就可能变成越权。
  3. 防御要看成本曲线:ECC、全局 refresh、坏行退休、hot row 计数都能缓解,但各自有成本或覆盖缺口。
  4. 好论文会给出闭环:这篇不是只报漏洞,而是给了实机演示、FPGA 表征、原因假设、结果数字和 PARA 方案。
  • 论文 PDF:Kim et al. 2014(原论文,读摘要、Figure 6、Section 8 最划算)
  • 回顾文章:Onur Mutlu 2023 retrospective(解释这篇论文之后十年的影响)
  • raidr —— 图谱里的 DRAM refresh 前置工作,帮助理解为什么“少刷新”本来是性能目标
  • rambleed-2020 —— RowHammer 从完整性问题进一步变成读秘密的侧信道问题
  • eccploit-2019 —— 继续追问 ECC 内存到底能挡住多少 RowHammer
  • kocher-spectre-2019 —— 同样利用硬件微结构细节突破软件安全抽象。
  • lipp-meltdown-2018 —— 同样说明 CPU/内存系统的优化会改变隔离边界。
  • moesi-cache-coherence-1986 —— cache 会挡住真实 DRAM 访问,论文演示必须让读请求落到内存行。
  • gpu-cache-coherence-2013 —— 都在讨论多级存储系统里“谁真的看见了访问”。
  • persistent-memory-2014 —— 都关心内存介质的可靠性,只是 RowHammer 关注易失 DRAM。
  • aes —— 如果密钥或查表状态被 bit flip 影响,密码算法的数学安全也可能被工程层破坏。
  • diffie-hellman-1976 —— 密钥协商产出的秘密最终要落在内存里,硬件错误会进入威胁模型。
  • flush-reload-2014 —— FLUSH+RELOAD 2014 — 用缓存时间偷看程序访问了哪行内存
  • sanctum-2016 —— Sanctum 2016 — 用少量硬件改动做强隔离 enclave