Post

探究 | Intel FRED 实现

探究 | Intel FRED 实现

概述

FRED(Flexible Return and Event Delivery) 是 Intel 引入的新型特权级切换与事件处理架构,用于替代传统的 IDT 事件交付(IDT event delivery)和 IRET 返回机制,同时 AMD 也宣布在即将到来的 Zen6 中采用此功能,所以我认为有必要借此来简单介绍一下。

在阅读本篇内容前,需要读者对 x64 架构有一定的了解。

枚举

功能支持描述
FREDCPUID.(EAX=07H, ECX=1H):EAX.FRED [Bit 17]支持 FRED 指令和寄存器
LKGSCPUID.(EAX=07H, ECX=1H):EAX.LKGS [Bit 18]支持 LKGS 指令

这是两个独立的功能,任何支持 FRED 的处理器都将支持 LKGS。

启用

操作系统可以通过设置 CR4.FRED[bit 32] 来启用 FRED,启用后,传统 IDTSYSCALLSYSENTER 事件,都将统一转换成 FRED 事件。

开启与否,并不会影响 LKGS 指令,也不会影响 RDMSRWRMSR 对于 FRED MSR 的访问。

FRED 仅适用于 64 位操作系统(IA-32e 模式),而 AMD 在设计之初取消了 IA-32e 模式下的 SYSENTER 指令,所以猜测未来 FRED 事件中也不会存在 SYSENTER。

MSR

FRED MSR地址功能
IA32_FRED_CONFIG1D4H配置 FRED 的功能
IA32_FRED_STKLVLS1D0H异常向量各自的最低栈级别
IA32_FRED_RSPn1CCH - 1CFHn= 0 - 3,各栈级别对应的 RSP
IA32_FRED_SSPn1D1H - 1D3Hn= 1 - 3,各栈级别对应的 SSP
IA32_FRED_SSP06A4H复用了原本 KCET.SS 的 IA32_PL0_SSP
  • IA32_FRED_CONFIG
    • Bits 1:0:当前栈级别(CSL)。

    • Bit 3:设置后,如果不进行栈切换,则将 SSP 递减 8。

    • Bits 8:6:不进行栈切换时 RSP 的递减量。

    • Bits 10:9CPL = 0 时可屏蔽中断的栈级别。

    • Bits 63:12:事件处理入口,要求 4K 对齐。

  • IA32_FRED_STKLVLS
    • 代表的是 32 个异常向量在 CPL = 0 时使用的栈级别,每个向量占 2 位。
  • IA32_FRED_RSPn
    • 代表的是每个栈级别对应的 RSP
  • IA32_FRED_SSPn
    • 代表的是每个栈级别对应的 SSP

事件的交付

首先,如果事件发生在 CPL = 3,则根据 IA32_STAR [47:32] 设置新的 CSSS,然后交换 GS.BaseIA32_KERNEL_GS_BASE

下一步为设置新的 RIP

RIP来源描述
IA32_FRED_CONFIG & ~FFFHCPL = 3使用 ERETU(返回用户态)
(IA32_FRED_CONFIG & ~FFFH) + 256CPL = 0使用 ERETS(返回内核态)

RIP 不符合分页标准,则先后尝试通过 #GP#DF 产生 VM Exit,如果都没能产生 VM Exit 则会产生 Triple fault。 设置好 RIP 以后,会将 RFLAGS 设置为 2,随后设置 RSPSSPCSL 进行栈切换。


传统 IDT 的栈切换是依赖于 TSS 的,当中断或异常发生时,会根据以下因素决定是否进行栈切换:

  • 如果中断 / 异常向量对应的 IDT EntryIST 非零,则会无条件将 RSP 切换为 TSSIST 字段对应的值。
  • 否则,如果存在权限跃迁,就将 RSP 切换为 TSSRSP 字段对应的值。
  • 否则,不会进行栈切换。

而当开启 FRED 以后,栈切换方式会随之改变,并引入 栈级别 这个概念,FRED 事件发生时,首先会根据事件类型和 CPL 来确定 eventSL

场景eventSL
CPL = 3,既不是嵌套异常,也不是双重异常0
CPL = 3,是嵌套异常,或是双重异常IA32_FRED_STKLVLS[2v+1:2v]
CPL = 0,异常 / NMIIA32_FRED_STKLVLS[2v+1:2v]
CPL = 0,外部中断IA32_FRED_CONFIG[10:9]
CPL = 0,INT n / SYSCALL / SYSENTER0

随后将 CSL 设置为 MAX(CSL, eventSL),并根据以下因素决定是否进行栈切换:

  • 如果事件发生在 CPL = 3,或 CSL 产生了变化,则将 RSP 切换为对应的 IA32_FRED_RSP,如果当前启用了 KCET,则同时将 SSP 切换为对应的 IA32_FRED_SSP
  • 否则,不进行栈切换,但会根据 IA32_FRED_CONFIG 中的 [8:6][3] 的配置来递减 RSPSSP。手册并未提及这些字段的具体用途,猜测是在为 Red Zone 预留空间。

指令的变动

启用 FRED 后,某些指令的行为则会发生变动,以下指令会被禁用,尝试执行会产生 #UD 异常:

  • CLRSSBSY,SETSSBSY:这两条指令将随着 Supervisor Shadow Stack Tokens 一起废除。
  • SYSEXIT,SYSRET:改为使用 ERETUERETS 处理 SYSENTERSYSCALL
  • SWAPGS:如果 FRED 转换的过程中 CPL 产生变化,处理器会自动交换 GS.Base

并且调用门也随着 IDT 一起被废除,FRED 转换将成为唯一可以修改 CPL 的方式,当 far CALLfar JMPfar RETIRET 试图修改 CPL 的时候,将产生 #GP 异常。

Windows FRED 配置与启用

Windows 10.0.29531.1000 版本内核中,FRED 的探测与启用时机,主要集中在系统初始化阶段,执行路径如下:

  • KiSystemStartup
    • KiInitializeBootStructures
      • KiSetProcessorSignature
        • RtlDetectProcessorFeatures
      • KiSetFeatureBits

系统初始化调用 KiInitializeBootStructures 时,会调用 RtlDetectProcessorFeatures 来枚举处理器支持的能力,并将枚举的结果分别存放到 Prcb->FeatureBits 和全局变量 KeFeatureBits2,我们可以从 KiCpuFeatureTable 的表项中找到我们需要的两项:

1
2
KI_CPU_FEATURE_ENTRY <7, 1, 20000h, 0, 14h, 0, 4000000000h, 0> [FRED]
KI_CPU_FEATURE_ENTRY <7, 1, 40000h, 0, 14h, 0, 8000000000h, 0> [LKGS]

FREDKeFeatureBits2 中对应的掩码是 4000000000hLKGS 对应的是 8000000000h,如果同时处于置位状态,是否应该启用还取决于以下三个字段:

  • LoaderBlock->Extension->BootDebuggerActive
  • LoaderBlock->Extension->EnableFred
  • LoaderBlock->Extension->VsmConfigured

只有当 EnableFred 处于置位,并且 VsmConfigured 处于复位的时候,KiFredEnabled 全局变量才会被设置,具体原因稍后会讲到,,真正启用 FRED 的地方是在 KiSetFeatureBits 里,在启用前,首先会先进行配置:

1
2
3
4
5
__writemsr(IA32_FRED_CONFIG, KiEnterUserModeEvent & 0xFFFFFFFFFFFFF834uLL | 0x40);
__writemsr(IA32_FRED_RSP0, TssBase->Rsp0);
__writemsr(IA32_FRED_RSP1, TssBase->Ist[3]);
__writemsr(IA32_FRED_RSP2, TssBase->Ist[1]);
__writemsr(IA32_FRED_STKLVLS, 0x1000020010uLL);

但是否启用,还需要通过 BootDebuggerActive 来检测系统是否存在调试器:

  • 当存在调试器的时候,系统会不启用 FRED,继续使用传统的 IDT
  • 当不存在调试器的时候,则设置 CR4.FRED[bit 32] 启用 FRED,并将 IDTLimit 清零。

由此可见,当前版本内核的 FRED 实现可能并没有支持双机调试,并且在配置 FRED 的时候,并未配置 IA32_FRED_SSPn,前面提到了 VsmConfigured 复位的要求,VsmConfigured 代表系统是否存在 SecureKernel,而 KCETSecureKernel 的功能之一,所以猜测当前的 FRED 实现也没有支持 KCET

Windows FRED 事件的处理

闲下来慢慢补。

This post is licensed under CC BY 4.0 by the author.