# 22.3 Speculative execution(2)

![](https://1977542228-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MHZoT2b_bcLghjAOPsJ%2F-MYXTOksej7u2IGWw_94%2F-MYZrO2juNK0QIr_cO4h%2Fimage.png?alt=media\&token=d68d6383-36ae-4f4c-b92e-d340b7c411f9)

如果r0中的内存地址是无效的，且在Page Table中完全没有映射关系，那么我也不知道会发生什么。如果r0中的内存地址在Page Table中存在映射关系，只是现在权限不够，比如说pte\_u标志位为0，那么Intel的CPU会加载内存地址对应的数据，并存储在r2寄存器的临时版本中。之后r2寄存器的临时版本可以被代码第5行使用。所以尽管r0中的内存地址是我们没有权限的内存，比如说一个内核地址，它的数据还是会被加载到r2，之后再加1并存储在r3中。之后，当代码第4行Retired时，CPU会发现这是一个无效的读内存地址行为，因为PTE不允许读取这个内存地址。这时CPU会产生Page Fault取消执行后续指令，并回撤对于r2和r3寄存器的修改。

所以，在这里的例子中，CPU进行了两个推测：一个是CPU推测了if分支的走向，并选择了一个分支提前执行；除此之外，CPU推测了代码第4行能够成功完成。对于load指令，如果数据在CPU缓存中且相应的PTE存在于Page Table，不论当前代码是否有权限，Intel CPU总是能将数据取出。如果没有权限，只有在代码第4行Retired的时候，才会生成Page Fault，并导致预测执行被取消。

> 学生提问：我对CPU的第二个预测，也就是从r0中保存的内存地址加载数据有一些困惑，这是不是意味着r0对应的数据先被加载到了r2，然后再检查PTE的标志位？
>
> Robert教授：完全正确。在预测的阶段，不论r0指向了什么地址，只要它指向了任何东西，内存中的数据会被加载到r2中。之后，当load指令Retired时才会检查权限。如果我们并没有权限做操作，所有的后续指令的效果会被取消，也就是对于寄存器的所有修改会回滚。同时，Page Fault会被触发，同时寄存器的状态就像是预测执行的指令没有执行过一样。
>
> 学生提问：难道不能限制CPU在Speculative execution的时候，先检查权限，再执行load指令吗？看起来我们现在的问题就是我们在不知道权限的情况下读取了内存，如果我们能先知道权限，那么Speculative execution能不能提前取消？
>
> Robert教授：这里有两个回答。首先，Intel芯片并不是这样工作的。其次，是的，我相信对于Intel来说如果先做权限检查会更简单，这样的话，在上面的例子中，r2寄存器就不会被修改。
>
> 你们或许注意到论文中提到，尽管AMD CPU的手册与Intel的一样，它们有相同的指令集，Meltdown Attack并不会在AMD CPU上生效。普遍接受的观点是，AMD CPU在Speculative execution时，如果没有权限读取内存地址，是不会将内存地址中的数据读出。这就是为什么Meltdown Attack在AMD CPU上不生效的原因。最近的Intel CPU明显也采用了这种方法，如果程序没有权限，在Speculative execution的时候也不会加载内存数据。
>
> 这里使用哪种方式对于性能来说没有明显区别，或许在指令Retired的时候再检查权限能省一些CPU的晶体管吧。这里我要提醒一下，这里有很多内容都是猜的，不过我认为我说的都是对的。Intel和AMD并没有太披露具体的细节。

这里有一些重要的术语。你可以从CPU手册中读到的，比如说一个add指令接收两个寄存器作为参数，并将结果存放在第三个寄存器，这一类设计被称为CPU的Architectural，或者通告的行为。如果你读取一个你没有权限的内存地址，你会得到一个Page Fault，你不允许读取这个内存地址，这就是一种通告的行为。CPU的实际行为被称作Micro-Architectural，CPU的通告行为与实际行为是模糊不清的。比如说CPU会悄悄的有Speculative execution。

![](https://1977542228-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MHZoT2b_bcLghjAOPsJ%2F-MYZr_lRhTr0WhmG0xIW%2F-MYbZDXyL8yYuviU1NcT%2Fimage.png?alt=media\&token=2b7e0820-ddd5-4d22-85b4-49fe908cd4c6)

CPU设计者在设计Micro-Architectural时的初衷是为了让它是透明的。的确有很多行为都发生在CPU内部，但是结果看起来就像是CPU完全按照手册在运行。举个例子，在上面代码的第4行，或许Intel的CPU在读取内存时没有检查权限，但是如果权限有问题的话，在指令Retired的时候，所有的效果都会回滚，你永远也看不到你不该看到的内存内容。所以看起来就跟CPU的手册一样，你不允许读取你没有权限的内存地址。这里Architectural和Micro-Architectural的区别是Meltdown Attack的主要攻击点。这里的攻击知道CPU内部是如何工作的。
