Intel 设计缺陷背后的原因是什么? | Linux 中国

版权声明:本文为博主原创文章。未经博主同意不得转载。 https://blog.csdn.net/F8qG7f9YD02Pe/article/details/79386769
640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1我们知道有问题。可是并不知道问题的具体情况。-- Peter Bright

实用的原文链接请訪问文末的“原文链接”获得可点击的文内链接、全尺寸原图和相关文章。

致谢编译自 | https://arstechnica.com/gadgets/2018/01/whats-behind-the-intel-design-flaw-forcing-numerous-patches/ 
 作者 | Peter Bright
 译者 | qhwdw ? ? ?

?

? 共计翻译:80 篇 贡献时间:119 天

我们知道有问题。可是并不知道问题的具体情况。

(本文发表于 1 月份)近期 Windows 和 Linux 都发送了重大安全更新。为防范这个尚未全然公开的问题。在最坏的情况下。它可能会导致性能下降多达一半。

在过去的几周。Linux 内核陆续打了几个补丁。Microsoft 自 11 月份開始也内部測试了 Windows 更新[1],而且它估计在下周二的例行补丁中将这个改进推送到主流 Windows 构建版中。

Microsoft 的 Azure 也在下周的维护窗体中做好了安排,而 Amazon 的 AWS 也安排在周五对相关的设施进行维护。

自从 Linux 第一个补丁 (參见 KPTI:内核页表隔离的当前的发展[2]) 明白描绘了出现的错误以后。尽管 Linux 和 Windows 基于不同的考虑。对此持有不同的看法,可是这两个操作系统 —— 当然还有其他的 x86 操作系统,比方 FreeBSD 和 macOS[3] — 对系统内存的处理採用了同样的方式。由于对于操作系统在这一部分特性是与底层的处理器高度耦合的。

保持地址跟踪

在一个系统中的每一个内存字节都是隐性编码的,这些编码数字是每一个字节的地址。早期的操作系统使用物理内存地址,可是,物理内存地址由于各种原因,它并不非常合适。比如,在地址中常常会有空隙,而且(尤其是 32 位的系统上)物理地址非常难操作,须要 36 位数字,甚至很多其他。

因此。如今操作系统全然依赖一个叫虚拟内存的概念。虚拟内存系统同意程序和内核一起在一个简单、清晰、统一的环境中各自去操作。而不是使用空隙和其他奇怪的东西的物理内存,每一个程序和内核自身都使用虚拟地址去訪问内存。这些虚拟地址是连续的 —— 不用操心有空隙 —— 而且合适的大小也更便于操作。

32 位的程序仅能够看到 32 位的地址。而不用管物理地址是 36 位还是很多其他位。

尽管虚拟地址对每一个软件差点儿是透明的,可是,处理器终于还是须要知道虚拟地址引用的物理地址是哪个。因此,有一个虚拟地址到物理地址的映射,它保存在一个被称为页面表的数据结构中。操作系统构建页面表,使用一个由处理器决定的布局。而且处理器和操作系统在虚拟地址和物理地址之间进行转换时就须要用到页面表。

这个映射过程是非常重要的,它也是现代操作系统和处理器的重要基础,处理器有专用的缓存 — Translation Lookaside Buffer(简称 TLB)—— 它保存了一定数量的虚拟地址到物理地址的映射,这样就不须要每次都使用全部页面。

虚拟内存的使用为我们提供了非常多除了简单寻址之外的实用的特性。当中最基本的是,每一个程序都有了自己独立的一组虚拟地址,有了它自己的一组虚拟地址到物理地址的映射。这就是用于提供“内存保护”的关键技术。一个程序不能破坏或者篡改其他程序使用的内存,由于其他程序的内存并不在它的地址映射范围之内。

由于每一个进程使用一个单独的映射,因此每一个程序也就有了一个额外的页面表,这就使得 TLB 缓存非常拥挤。TLB 并不大 —— 普通情况下总共能够容纳几百个映射 —— 而系统使用的页面表越多,TLB 能够包括的不论什么特定的虚拟地址到物理地址的映射就越少。

一半一半

为了更好地使用 TLB,每一个主流的操作系统都将虚拟地址范围一分为二。一半用于程序;还有一半用于内核。

当进程切换时,仅有一半的页面表条目发生变化 —— 仅属于程序的那一半。内核的那一半是每一个程序公用的(由于仅仅有一个内核)而且因此它能够为每一个进程使用同样的页面表映射。

这对 TLB 的帮助非常大。尽管它仍然会丢弃属于进程的那一半内存地址映射;可是它还保持着还有一半属于内核的映射。

这样的设计并非一成不变的。在 Linux 上做了一项工作,使它能够为一个 32 位的进程提供整个地址范围,而不用在内核页面表和每一个进程之间共享。

尽管这样为程序提供了很多其他的地址空间。但这是以牺牲性能为代价的,由于每次内核代码须要执行时,TLB 又一次载入内核的页面表条目。因此。这样的方法并没有广泛应用到 x86 的系统上。

在内核和每一个程序之间切割虚拟地址的这样的做法的一个负面影响是,内存保护被削弱了。

假设内核有它自己的一组页面表和虚拟地址,它将在不同的程序之间提供同样的保护;内核内存将是简单的不可见。可是使用地址切割之后。用户程序和内核使用了同样的地址范围,而且从原理上来说,一个用户程序有可能去读写内核内存。

为避免这样的明显不好的情况,处理器和虚拟地址系统有一个 “Ring” 或者 “模式”的概念。

x86 处理器有很多 Ring。可是对于这个问题。仅有两个是相关的:“user” (Ring 3)和 “supervisor”(ring 0)。当执行普通的用户程序时,处理器将置为用户模式 (Ring 3)。当执行内核代码时,处理器将处于 Ring 0 —— supervisor 模式,也称为内核模式。

这些 Ring 也用于从用户程序中保护内核内存。页面表并不仅仅有虚拟地址到物理地址的映射;它也包括关于这些地址的元数据。包括哪个 Ring 可能訪问哪个地址的信息。内核页面表条目被标记为仅有 Ring 0 能够訪问。程序的条目被标记为不论什么 Ring 都能够訪问。

假设一个处于 Ring 3 中的进程去尝试訪问标记为 Ring 0 的内存,处理器将阻止这个訪问并生成一个意外错误信息。执行在 Ring 3 中的用户程序不能得到内核以及执行在 Ring 0 内存中的不论什么东西。

至少理论上是这样的。大量的补丁和更新表明,这个地方已经被突破了。这就是最大的谜团所在。

Ring 间迁移

这就是我们所知道的。

每一个现代处理器都执行一定数量的猜測执行。比如。给一些指令。让两个数加起来,然后将结果保存在内存中,在查明内存中的目标是否可訪问和可写入之前。一个处理器可能已经猜測性地做了加法。在一些常见案例中,在地址可写入的地方。处理器节省了一些时间,由于它以并行方式计算出内存中的目标是什么。假设它发现目标位置不可写入 —— 比如。一个程序尝试去写入到一个没有映射的地址或压根就不存在的物理位置 —— 然后它将产生一个意外错误,而猜測执行就白做了。

Intel 处理器,尤其是(尽管不是 AMD 的[4])同意对 Ring 3 代码进行猜測执行并写入到 Ring 0 内存中的处理器上。处理器并不全然阻止这样的写入,可是猜測执行轻微扰乱了处理器状态。由于。为了查明目标位置是否可写入。某些数据已经被载入到缓存和 TLB 中。这又意味着一些操作可能快几个周期,或者慢几个周期。这取决于它们所须要的数据是否仍然在缓存中。除此之外,Intel 的处理器还有一些特殊的功能,比方,在 Skylake 处理器上引入的软件保护扩展(SGX)指令,它改变了一点点訪问内存的方式。

同样的,处理器仍然是保护 Ring 0 的内存不被来自 Ring 3 的程序所訪问,可是同样的,它的缓存和其他内部状态已经发生了变化,产生了可測量的差异。

我们至今仍然并不知道具体的情况。究竟有多少内核的内存信息泄露给了用户程序,或者信息泄露的情况有多easy发生。

以及有哪些 Intel 处理器会受到影响?也或者并不全然清晰。可是,有迹象表明每一个 Intel 芯片都使用了猜測执行(是自 1995 年 Pentium Pro 以来的全部主流处理器吗?),它们都可能会因此而泄露信息。

这个问题第一次被披露是由来自 奥地利的 Graz Technical University[5] 的研究者。他们披露的信息表明这个问题已经足够破坏内核模式地址空间布局随机化(内核 ASLR,或称 KASLR)。ASLR 是防范 缓冲区溢出[6] 漏洞利用的最后一道防线。启用 ASLR 之后。程序和它们的数据被置于随机的内存地址中。它将使一些安全漏洞利用更加困难。KASLR 将这样的随机化应用到内核中。这样就使内核的数据(包括页面表)和代码也随机化分布。

Graz 的研究者开发了 KAISER[7],一组防范这个问题的 Linux 内核补丁。

假设这个问题正好使 ASLR 的随机化被破坏了,这也许将成为一个巨大的灾难。

ASLR 是一个非常强大的保护措施,可是它并非完美的。这意味着对于黑客来说将是一个非常大的障碍。一个无法逾越的障碍。整个行业对此的反应是 —— Windows 和 Linux 都有一个非常重要的变化,秘密开发 —— 这表明不仅是 ASLR 被破坏了。而且从内核泄露出信息的更普遍的技术被开发出来了。

确实是这样的,研究者已经 在 Twitter 上公布信息[8],他们已经能够任意泄露和读取内核数据了。还有一种可能是。漏洞可能被用于从虚拟机中“越狱”,并可能会危及 hypervisor。

Windows 和 Linux 选择的解决方式是非常类似的,将 KAISER 分为两个区域:内核页面表的条目不再是由每一个进程共享。在 Linux 中,这被称为内核页面表隔离(KPTI)。

应用补丁后,内存地址仍然被一分为二:这样使内核的那一半差点儿是空的。当然它并非非常的空。由于一些内核片断须要永久映射,不论进程是执行在 Ring 3 还是 Ring 0 中,它都差点儿是空的。这意味着假设恶意用户程序尝试去探測内核内存以及泄露信息。它将会失败 —— 由于那里差点儿没有信息。而真正的内核页面中仅仅有当内核自身执行的时刻它才干被用到。

这样做就破坏了最初将地址空间切割的理由。如今,每次切换到用户程序时,TLB 须要实时去清除与内核页面表相关的全部条目,这样就失去了启用切割带来的性能提升。

影响的具体大小取决于工作负载。每当一个程序被调入到内核 —— 从磁盘读入、发送数据到网络、打开一个文件等等 —— 这样的调用的成本可能会添加一点点,由于它强制 TLB 清除了缓存并实时载入内核页面表。不使用内核的程序可能会观測到 2 - 3 个百分点的性能影响 —— 这里仍然有一些开销,由于内核仍然是偶尔会执行去处理一些事情。比方多任务等等。

可是大量调用进入到内核的工作负载将观測到非常大的性能损失。在一个基准測试中,一个除了调入到内核之外什么都不做的程序,观察到 它的性能下降大约为 50%[9];换句话说就是,打补丁后每次对内核的调用的时间要比不打补丁调用内核的时间添加一倍。基准測试使用的 Linux 的网络回环(loopback)也观測到一个非常大的影响。比方,在 Postgres 的基准測试中大约是 17%[10]。真实的数据库负载使用了实时网络可能观測到的影响要低一些,由于使用实时网络时,内核调用的开销基本是使用真实网络的开销。

尽管对 Intel 系统的影响是众所周知的。可是它们可能并非唯一受影响的。其他的一些平台,比方 SPARC 和 IBM 的 S390,是不受这个问题影响的,由于它们的处理器的内存管理并不须要切割地址空间和共享内核页面表;在这些平台上的操作系统一直就是将它们的内核页面表从用户模式中隔离出来的。可是其他的,比方 ARM。可能就没有这么幸运了。适用于 ARM Linux 的类似补丁[11] 正在开发中。


PETER BRIGHT[12] 是 Ars 的一位技术编辑。他涉及微软、编程及软件开发、Web 技术和浏览器、以及安全方面。

它居住在纽约的布鲁克林。


via: https://arstechnica.com/gadgets/2018/01/whats-behind-the-intel-design-flaw-forcing-numerous-patches/

作者:PETER BRIGHT[14] 译者:qhwdw 校对:wxy

本文由 LCTT 原创编译。Linux中国 荣誉推出


posted @ 2018-11-05 20:49  ldxsuanfa  阅读(240)  评论(0编辑  收藏  举报