CPU Bugs

来自osdev
跳到导航 跳到搜索

计算机是由人类制造的,因此天生容易出错。 本页描述了各种型号和品牌的已知错误。

影响几乎所有现代架构

Spectre

SPECTE漏洞会影响1995年后制造的大多数现代CPU,这些CPU实现了无序执行(x86、x86_64、ARM、AMD,可能还有更多),并允许用户代码读取物理内存。 没有针对此问题的最佳软件修复程序。 有关更多详细信息,请参阅(https://spectreattack.com/spectre.pdf 这里)。

x86功能不全

ESP未清除

当返回到16位模式时,x86 IRET不会清除堆栈寄存器的高位(32:16)。 因此,ESP的内核高16位可能会泄漏到用户空间。 对于64位内核到16位用户空间过渡也是如此。

缓解措施

一种缓解措施是简单地不允许用户模式下的16位代码。 但是,Linux可以执行以下操作来解决此问题: 在32位模式下,它保留了一个用于固定堆栈指针的段。 如果它检测到有16位堆栈返回用户空间,它会设置该段的基址,这样就可以在ESP中设置泄漏的位,而无需移动实际的堆栈指针,然后将该段作为堆栈段加载。 因此,泄漏的比特将等于原始比特,因此不会造成任何损害。

在64位模式下,由于缺乏分段支持,这是不可能的。 因此,Linux采用了另一种方法,并且总是在引导时泄漏随机选择的位。 在引导期间,Linux为每个CPU分配48字节作为ESPFIX堆栈(实际上,只要同一页上有剩余空间,所有CPU都将使用同一页,只是其上的偏移量不同)。 这48个字节被映射到内核空间中,因此ESP的泄漏位是完全随机的。 如果检测到返回到16位堆栈,则在将堆栈切换到该地址并执行返回之前,将返回帧复制到ESPFIX堆栈中。 由于这可能会失败,因此ESPFIX堆栈被映射为只读(对于副本,使用同一页的可写映射)。 因此,如果IRET成功,则不会将任何内容写入ESPFIX堆栈,并且一切正常。 但是,如果出现问题,因为CPU已经在Ring 0中运行,它不会将堆栈切换回原来的位置。 它会尝试将中断帧写入堆栈,但由于堆栈是只读的,因此该操作将失败。 因此,会导致双重故障,这时使用双重故障处理程序 (使用IST堆栈运行) 会更改信息,使其看起来像是在IRET指令上发生的一般保护故障。 然后GPF处理程序就可以正常工作了。

这样,泄漏的位将不等于用户空间在中断开始时拥有的位,但它们在每次引导时都是随机的。 由于ESPFIX堆栈如此之小,大量cpu将具有相同的泄漏ESP位,因此没有重要数据泄漏。

空选择器加载可能无法清除MSR_GS_BASE

如果加载了空选择器,英特尔CPU会不指定MSR_GS_BASE会发生什么情况。 英特尔cpu似乎将其加载为零,AMD cpu保留了以前的值 (现在记录在AMD64架构程序员手册第2卷: 系统编程中)。 如果内核试图优化缓慢的MSR操作,那么上下文切换需要考虑这个细节。

缓解措施

不要将空选择器加载到GS中,以为它会更改GS.base寄存器。 始终使用MSR操作,除非在具有WRFSGSBASE功能的CPU上运行,在这种情况下,GS.base地址可以使用WRGSBASE指令设置。

FXSAVE/FNSAVE

英特尔和AMD在保存/恢复的上下文方面有所不同。AMD CPU不保存/恢复某些部分 (FIP/FOP) 只有在异常挂起时 (见CVE-2006-1056)

SYSRET

英特尔CPU不能正确处理非规范返回地址。 如果执行SYSRET时RCX中存在非规范地址,则在带有CPL3寄存器的CPL0中将会出现一般保护错误。(见CVE-2006-0744)

缓解措施

要执行syscall指令以使其跳转到具有非规范地址的内核空间,唯一可能的方法是将该指令放在地址0x00007ffffffffffe。 这是无效的,因为CPU无处可返回 (返回地址无效)。 为了防止这种情况发生,可以禁止在地址0x00007FFFFFF000处映射可执行页面。 或者,可以直接处理该问题:如果返回地址设置了前16位中的任何一位,则使用iret指令返回到用户空间。 或者,构造必要的堆栈框架,然后直接跳转到GPF处理程序。

SS选择器

在AMD CPU上,当内核中断到达(将SS设置为空)并且线程被切换并通过SYSRET返回到用户空间时,SS选择器可能变得不可用。 数值SS值是正确的,但是描述符缓存是错误的。 这只影响SS的32位兼容模式使用。

缓解措施

在内核中断后不要切换线程 (即: 返回用户空间或处理系统调用时,仅从最外层堆栈层切换线程)。 或者,如果在内核中断后切换线程,请始终通过IRET返回。 在某些情况下,您无论如何都需要在系统调用之后跳转到较慢的IRET返回路径,所以您也可以将此作为一种情况。

PUSH选择器

在英特尔CPU上,在32位保护模式下运行时,PUSH只会修改堆栈的低16位,并在那里写入选择器。 高16位保持不变。 AMD cpu不这样做。 堆栈的某些部分未初始化可能会产生一些安全影响。

注意:这适用于每次将选择器推送到堆栈时。 因此,在某种中断之后,推送指令和隐式推送都会受到影响。

缓解措施==

从堆栈中读取选择器值时,始终屏蔽掉您感兴趣的位。 很多时候,重要的信息只是选择器的RPL (已经包含了信息,无论选择器是内核还是用户空间),或者TI位。 很少需要实际的表索引。 这样做还可以加强内核中读取选择器的所有部分,防止以后对GDT的修改。

NMI中断的嵌套

如果CPU正在执行NMI中断处理程序,CPU保证在执行IRET之前保持NMI屏蔽。 然而,如果出于某种原因,NMI触发了执行IRET的其他异常,则NMI可能会再次触发,可能会覆盖其自己的堆栈,因为在AMD64上,它与IST堆栈一起运行(如果SMI出于某种原因触发IRET,则有趣问题就会发生)。

Intel

Transactional Synchronization eXtensions (TSX) Bug

2014年8月,英特尔宣布在Haswell、Haswell-E、Haswell-EP和早期Broadwell CPU上的TSX实施中存在错误,导致通过微码(microcode)更新在受影响的CPU上禁用TSX功能。 该错误已在启用vPro的核心M-5Y70 Broadwell CPU的2014年11月F-0中修复。

Extended Page Table (EPT) Bug

启用EPT时,从MOV到CR3可能会导致意外的页面错误或错误的页面转换。

受影响的处理器:

  • Intel Xeon E5-#### v2,其中####是一个4位数字,后跟一个可选字母。
  • Intel Xeon E7-#### v2,其中#是4位数字。
  • Intel Xeon E3-12## v2,其中##是2位数字,后跟一个可选字母。

F00F Bug

影响:英特尔i586系列(奔腾1、奔腾MMX、奔腾OverDrive、奔腾MMX OverDrive)

此错误是由于执行锁定CMPXCHG8B eax (F0 0F C7 C8) 而导致的,该错误包含两个操作代码错误,一个不允许的锁定和一个非内存目标,并试图缓存结果,它使cpu进入死锁状态,锁定了所涉及的整个计算机。

要修复此错误,应将包含无效操作码的IDT条目标记为不可缓存或可写,以消除一个必要的因素,或者将同一页标记为不可写,这会进一步混淆处理器,而将其标记为pagefault处理程序,而不是死锁。 如果要禁用分页,唯一的解决办法是禁用CPU的缓存,这还远远不是很有效。 对各种解决方案的进一步讨论在 这里

我们可以通过CPUID指令检查处理器是否为奔腾。 在EAX=1的情况下调用它将返回EAX中的CPU签名。 我们可以从CPU签名中提取族号,并将其与5进行比较,因为奔腾属于族5。

FDIV bug

奔腾FDIV错误是英特尔P5奔腾浮点单元(FPU)中的错误。 由于该错误,处理器可能会返回不正确的十进制结果,这对于数学和科学等领域所需的精确计算来说是一个麻烦的问题。 由Lynchburg学院的托马斯·R·奈利教授发现,英特尔将该错误归因于浮点除法电路使用的查找表中缺少条目。

此问题仅在原始奔腾处理器的某些型号上发生。 任何一款时钟速度至少为120MHz的奔腾系列处理器都足够新,不会有这种缺陷。

Buggy HLT

最初的100 MHz Intel DX芯片中的一些具有错误的HLT状态,促使Linux的开发人员实现了在这些芯片上运行时使用的 “no-hlt” 选项,但这已在以后的芯片中修复。

Core-microarchitecture Bugs

[1]

'Meltdown' Page Table Bug

现代(1995年及以上)的英特尔x86芯片在无序执行硬件中存在一个缺陷,当内核映射到userland地址空间时,该缺陷允许未经授权的userland软件访问内核内存。 为避免漏洞,建议将内核页表和用户页表分开(即:PTI,页表隔离)。 有关更多详细信息,请访问本页

AMD

DragonFly BSD Heavy Load Crash

AMD已确认其某些处理器包含一个错误,该错误在某些特定条件下可能导致程序错误。 该漏洞最初是由DragonFly BSD开发人员马特·狄龙(Matt Dillon)发现的。

连续的背靠背弹出(back-to-back pops)和(near) return指令会造成处理器错误地更新堆栈指针的情况。 DragonFly的具体表现是重负荷下的随机分段故障。

在前几代AMD Opteron处理器中发现了一个程序异常,该异常发生在某些使用非常特定的GCC编译器构建的环境中。 已为这一小部分客户确定了一种解决方法,这可能会对其产生潜在影响。 此外,这种marginal erratum影响了前四代AMD Opteron处理器,其中包括AMD Opteron 2300,8300 (“Barcelona” 和 “Shanghai”),2400,8400 (“Istanbul”) 和4100,6100 (“Lisbon” 和 “Magny-Cours”) 系列处理器。

Ryzen Bug

AMD已经证实,它的一些处理器存在漏洞,在某些特定条件下,当在规范地址边界附近执行代码时,可能会导致程序错误。 需要在规范地址边界之前插入保护页 (未映射的4k页或更大的页)。

CPUID Bugs

对于较旧的K5 CPU,由edX中的“CPUID 0x00000001”返回的功能标志不可靠-位9用于指示对PGE的支持(而不用于指示对本地APIC的支持)。

Cyrix

Coma Bug

影响:Cyrix 6x86系列

当几个隐式锁定的指令被流水线成无限循环时,会导致此错误。 实际上,当一条指令完成时,随后直接执行以下锁定指令,以保持总线锁定并抑制中断。 在无限循环中,这将锁定处理器上的所有中断,使其毫无用处。

要修复此错误,必须写入cyrix寄存器并在CCR1中设置无锁位,这将禁用除最基本的总线锁之外的所有锁。 这样做的缺点是,在多处理器系统上不再能保证读修改写原子性。 应防止此情况的源代码:(未经测试)

MOV AL, 0xC1   ; 0xC1指CCR1
OUT 0x22, AL   ; 选择寄存器
IN 0x23, AL    ; 加载内容
OR AL, 0x10    ; 设置No-Lock位
MOV AH, AL     ;
MOV AL, 0xC1   ; 0xC1指CCR1
OUT 0x22, AL   ; 选择寄存器
MOV AL, AH     ; 加载新内容
OUT 0x23, AL   ; 使用No-Lock set写新的CCR1