深入浅出解析Meltdown与Spectre漏洞(上篇):计算机架构漏洞的隐秘利用

本文详解2018年曝光的Meltdown处理器设计缺陷,通过缓存时序侧信道攻击实现内核内存越权读取,涵盖推测执行、分支预测等关键计算机架构概念及其安全影响。

Meltdown与Spectre可访问性概述(上篇)

在过去几周,现代处理器中两个关键设计缺陷的细节终于向公众披露。关于Meltdown和Spectre的影响已有大量讨论,但关于这些攻击原理及运作方式的详细说明却很少。本文将尽力弥补这一空白。

本文旨在以未学过计算机架构课程者也能理解的方式,解释Meltdown和Spectre攻击的工作原理。部分技术细节会大幅简化或省略,但您将理解核心概念以及为何Spectre和Meltdown如此重要且技术上有趣。

本文分为两部分以便阅读。第一部分(即本文)从计算机架构速成课开始提供背景知识,并解释Meltdown的实际作用。第二部分说明Spectre的两种变体,并讨论为何这些存在25年的漏洞现在才被修复。

背景知识

首先快速概述一些重要的计算机架构概念,以及关于硬件、软件及其协同工作的基本假设。这些是理解漏洞及其原理的必要基础。

软件是指令和数据

您使用的所有软件(如Chrome、Photoshop、Notepad、Outlook等)都是由计算机处理器执行的一系列小型独立指令。这些指令对存储在内存(RAM)和称为寄存器的特殊存储位置小表中的数据进行操作。几乎所有软件都假设程序指令按顺序执行。这一假设既合理又实用——相当于假设时间旅行不可能——使我们能够编写功能正常的软件。

Windows上notepad.exe中的这些Intel x86-64处理器指令展示了软件在指令级别的样子。箭头从分支指令流向其可能的目的地。

处理器设计追求速度

处理器设计旨在追求速度。非常非常快。现代Intel处理器每秒可执行约3000亿条指令。速度推动新处理器销售。消费者需求速度。计算机工程师找到了一些非常聪明的方法来提高计算机速度。其中三种技术——缓存、推测执行和分支预测——是理解Meltdown和Spectre的关键。您可能已经猜到,这些优化与计算机硬件执行指令的顺序假设相冲突。

缓存

处理器执行指令非常快(每条约2纳秒)。这些指令需要存储在某个地方,它们操作的数据也是如此。这个地方称为主内存(即RAM)。读写RAM的速度比处理器执行指令的速度慢50-100倍(约100纳秒/操作)。

因为读写内存速度慢(相对于指令执行速度),现代处理器的关键目标是避免这种缓慢。实现这一目标的一种方法是假设大多数程序的共同行为:它们反复访问相同的数据。现代处理器通过将频繁访问的内存位置内容副本存储在“缓存”中,来加速对这些位置的读写。该缓存位于芯片上,靠近执行指令的处理器核心。这种接近性使得访问缓存内存位置比离片访问RAM中的主存储快得多。缓存访问时间因缓存类型及其位置而异,但大约在1纳秒到3纳秒之间,而访问RAM则需要约100纳秒。

Intel Nehalem处理器晶圆图像(第一代Core i7,取自新闻稿)。缓存有多个级别,按距离执行电路的远近编号。L1和L2缓存在核心本身(可能是核心图像的右下/左下部分)。L3缓存在晶圆上但在多个核心之间共享。缓存占用大量昂贵的处理器空间,因为性能提升值得这样做。

与主内存容量相比,缓存容量很小。当缓存填满时,任何放入缓存的新项目必须驱逐现有项目。由于内存和缓存访问时间存在显著差异,程序可以通过计时访问所需时间来告知请求的内存位置是否被缓存。我们稍后将深入讨论这一点,但这种基于缓存的时间侧信道正是Meltdown和Spectre用来观察内部处理器状态的方法。

推测执行

一次执行一条指令速度慢。现代处理器不会等待。它们同时执行一束指令,然后重新排序结果以假装所有指令按顺序执行。这种技术称为乱序执行。从性能角度来看很有意义:一次执行4条指令需要8纳秒(4指令 x 2纳秒/指令)。同时执行4条指令(在现代处理器上可实现)仅需2纳秒——速度提升75%!

虽然乱序执行和推测执行有不同的技术定义,但出于本文目的,我们将两者都称为推测执行。我们觉得这是合理的,因为乱序执行本质上是推测性的。一束指令中的某些指令可能不需要执行。例如,像除以零这样的无效操作可能会停止执行,从而迫使处理器回滚同一束指令中后续指令执行的操作。

推测执行指令获得的性能可视化。假设有4个执行单元且指令互不依赖,顺序执行需要8纳秒,而乱序执行需要2纳秒。请注意,此图极度简化,故意未显示真实处理器中发生的许多重要事情(如流水线)。

有时,处理器对将执行哪些指令的猜测是错误的。在这些情况下,推测执行的指令必须“取消执行”。您可能已经猜到,研究人员发现未执行指令的一些副作用仍然存在。

导致推测执行猜测错误的注意事项很多,但我们将重点关注与Meltdown和Spectre相关的两个:异常和分支指令。当处理器检测到规则被违反时会发生异常。例如,除法指令可能除以零,或内存读取可能无权限访问内存。我们将在Meltdown部分进一步讨论这一点。第二个注意事项分支指令告诉处理器下一步执行什么。分支指令对于理解Spectre至关重要,并在下一节进一步描述。

分支预测

分支指令控制执行流程;它们指定处理器应获取下一条指令的位置。对于本次讨论,我们只对两种分支感兴趣:条件分支和间接分支。条件分支就像道路上的岔路口,因为处理器必须根据条件值(例如A > B;C = 0;等)选择两个选项之一。间接分支更像一个门户,因为处理器可以去任何地方。在间接分支中,处理器读取一个值,该值告诉它在哪里获取下一条指令。

条件分支及其两个潜在目的地。如果cmp指令的两个操作数相等,则采取此分支(即处理器将执行绿色箭头处的指令)。否则不采取分支(即处理器将执行红色箭头处的指令)。代码取自notepad.exe。

间接分支。此分支将重定向执行到内存位置0x10000c5f0处的任何地址。在这种情况下,它将调用initterm函数。代码取自notepad.exe。

分支发生非常频繁,并且妨碍推测执行。毕竟,处理器直到分支条件计算完成后才知道要执行哪些代码。处理器解决这一困境的方法称为分支预测。处理器猜测分支目的地。当猜测错误时,已执行的操作被取消执行,并从正确位置获取新指令。这种情况不常见。现代分支预测器在正常工作量下准确率轻松达到96%以上。

当分支预测器错误时,处理器会在错误上下文中推测执行指令。一旦发现错误,这些幽灵指令将被取消执行。正如我们将解释的,Spectre漏洞表明可以控制分支预测器并确定那些未执行指令的某些影响。

Meltdown

现在让我们应用上述计算机架构知识来解释Meltdown。Meltdown漏洞是(几乎)自1995年以来发布的每个Intel处理器中的设计缺陷。Meltdown允许特制程序读取其本应无权访问的核心操作系统内存。

处理器通常有两种特权模式:用户模式和内核模式。用户部分用于您日常交互的正常程序。内核部分用于操作系统的核心。内核在您机器上的所有程序之间共享,确保它们可以协同工作并与计算机硬件交互,并包含您可能不想暴露给所有运行程序的敏感数据(击键、网络流量、加密密钥等)。因此,不允许用户程序读取内核内存。确定内存哪些部分是用户部分、哪些部分是内核部分的表也存储在内存中。

想象一种情况:某些内核内存内容在缓存中,但其权限不在缓存中。检查权限将比简单读取内容值慢得多(因为它需要内存读取)。在这些情况下,Intel处理器异步检查权限:它们启动权限检查,无论如何读取缓存值,并在权限被拒绝时中止执行。因为处理器比内存快得多,在权限结果到达之前,可能已经推测执行了数十条指令。通常,这不是问题。权限检查失败后发生的任何指令都将被丢弃,就像从未执行过一样。

研究人员发现,可以推测执行一组指令,即使取消执行后也会留下可观察的迹象(通过缓存时间侧信道)。此外,根据内核内存的内容可能留下不同的迹象——这意味着用户应用程序可以间接观察内核内存内容,而无需有权读取该内存。

技术细节

Meltdown核心问题的图形表示,使用以下步骤描述的术语。可以无权限推测读取核心系统内存。这些临时推测读取的效果在指令中止和取消执行后本应不可见。事实证明,推测执行的缓存效应无法撤销,这创建了一个基于缓存的时间侧信道,可用于无权限读取核心系统内存。

高层次上,攻击工作原理如下:

  1. 用户应用程序请求一大块内存(称为bigblock),并确保其中没有任何部分被缓存。该块逻辑上分为256片(bigblock[0]、bigblock[1]、bigblock[2]、…、bigblock[255])。
  2. 进行一些准备以确保内核地址的内存权限检查需要很长时间。
  3. 乐趣开始!程序将从内核内存地址读取一个字节——我们称此值为secret_kernel_byte。提醒一下,字节可以是0到255范围内的任何数字。此操作启动了权限检查和处理器之间的竞赛。
  4. 在权限检查完成之前,硬件继续推测执行程序,该程序使用secret_kernel_byte读取bigblock的一片(即x = bigblock[secret_kernel_byte])。即使指令后来被撤销,对bigblock一片的使用也会缓存该片。
  5. 此时权限检查返回权限被拒绝。所有推测执行的指令被取消执行,处理器假装从未在bigblock[secret_kernel_byte]处读取内存。只有一个问题:bigblock的一片现在在缓存中,而之前不在。
  6. 程序将计时读取bigblock每一片所需的时间。由于推测执行而缓存的片读取速度将比其余部分快得多。
  7. bigblock中的片索引就是secret_kernel_byte的值。例如,如果bigblock[42]的读取速度比任何其他条目快得多,则secret_kernel_byte的值必为42。
  8. 程序现已通过缓存时间侧信道和推测读取了内核内存中的一个字节。

程序现在可以继续读取更多字节。Meltdown论文作者声称使用此技术可以以503 KB/s的速度读取内核内存。

影响是什么?

恶意软件可以使用Meltdown更轻松地在您的桌面上获得永久立足点,并监视您的密码和网络流量。这绝对很糟糕。您应该去应用修复程序。然而,恶意软件已经可以做到这些事情,尽管需要更多努力。

如果您是云提供商(如Amazon、Google或Microsoft)或拥有主要互联网基础设施的公司(如Facebook),那么这个漏洞绝对是一场灾难。很难强调这个漏洞有多糟糕。问题在于:云的工作原理是将大型数据中心划分为许多可按分钟租用的虚拟机。一台物理机器可以有数百个不同的虚拟租户,每个都运行自己的自定义代码。Meltdown打破了租户之间的墙:每个租户都可能看到其他租户所做的一切,如他们的密码、加密密钥、源代码等。注意:物理硬件虚拟化的方式很重要。Meltdown在某些情况下不适用。细节超出本文范围。

Meltdown的修复会带来性能损失。有些消息来源说是5-30%的性能损失,有些说可以忽略不计,其他说个位数到明显。我们确切知道的是,旧Intel处理器受到的影响比新处理器大得多。对于桌面机器,这稍微不便。对于大型云提供商或互联网公司,整个基础设施5%的性能损失是巨大的代价。例如,Amazon估计有200万台服务器。5%到30%的减速可能意味着购买和安装10万台(5%)到60万台(30%)额外服务器以匹配先前能力。

我应该做什么?

请安装操作系统(即MacOS、Windows、Linux)的最新更新。所有主要软件供应商已发布应由自动更新程序应用的修复程序。

所有主要云提供商已在内部部署修复程序,您作为客户无需担心。

待续…

我们希望您对计算机架构概念和Meltdown背后的技术细节有更好的理解。在本博客文章的后半部分,我们将解释Spectre V1和Spectre V2的技术细节,并讨论这些漏洞为何能在过去25年中隐藏。技术背景将变得更加复杂,但漏洞也更有趣。

最后,我们想提醒读者,本文旨在让没有计算机架构背景的人也能理解,我们真诚希望成功解释了一些困难概念。Meltdown和Spectre论文以及Project Zero博客文章是了解更多血腥细节的更好来源。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计