Skip to content

Latest commit

 

History

History
289 lines (218 loc) · 13.8 KB

File metadata and controls

289 lines (218 loc) · 13.8 KB

Intel SGX AEX(Asynchronous Enclave Exit)

本文从 SGX 的 AEX 机制出发,补足理解它所需的 CPU 背景(微码、流水线、中断时序、微码注入),并贯穿防御/检测视角。


一、AEX 是什么

AEX 是 SGX enclave 在执行过程中被动退出的机制。区别于主动退出的 EEXIT,AEX 由外部事件触发,CPU 需要在保护 enclave 机密状态的前提下,跳出 enclave 模式去处理这些事件。

触发场景

  • 硬件中断 / NMI:时钟中断、设备中断
  • 异常 / Fault:#PF(缺页)、#GP、#UD、除零等
  • VM Exit:虚拟化环境下被 hypervisor 抢占
  • SMI(系统管理中断)

硬件做的事(关键流程)

  1. 保存状态到 SSA(State Save Area)
    • 通用寄存器、XSAVE 状态写入 enclave 内部的 SSA 帧(由 TCS.CSSA 索引)
    • CSSA++,如果超过 NSSA#GP
  2. 擦除/合成寄存器(Synthetic State)
    • 通用寄存器被替换为合成值,避免泄漏 enclave 内部状态
    • RIP/RSP 切到 AEP(Asynchronous Exit Pointer)
  3. TLB 处理:enclave 相关 TLB 项被清理,下次进入会重新检查 EPCM
  4. 退出到 AEP:跳到 urts(untrusted runtime)预先注册的 AEP stub

ERESUME 恢复

AEP 通常处理完外部事件后调用 ERESUME:

  • CPU 从 SSA[CSSA-1] 恢复寄存器,CSSA--
  • 继续从中断点执行,enclave 内部对此透明(除非主动检查 SSA)

二、Background 1:AEX 不是独立硬件单元

AEX 不是一个独立硬件单元或协处理器,它是 CPU 在处理中断/异常时执行的一段微码(microcode)流程,本质上是 SGX 扩展给现有 x86 中断处理路径打的补丁。

  • 微码实现的状态机:Intel 在 CPU 微码里加了 SGX 相关的处理逻辑,AEX 是其中一条路径
  • 复用现有硬件:走的还是常规中断/异常的分发链路(IDT、APIC、MMU 等),只是在 enclave 模式下额外插入了"保护机密 → 合成寄存器 → 清 TLB → 跳 AEP"这些步骤
  • 依赖的硬件设施:MEE(老平台的内存加密引擎)、PMH/MMU(EPCM 检查)、APIC(中断送达)、内部模式位

所以 AEX 没有专属芯片面积,它是已有中断逻辑 + SGX 模式判断 + 微码序列的组合。


三、Background 2:什么是微码,它在哪一层

直觉类比

把 CPU 理解成"硬件 + 一层固件":

  • 你写的汇编指令(ADDMOVENCLU)是对外接口
  • CPU 内部真正干活的电路只懂更原始的操作
  • 微码就是"把一条汇编指令翻译成一串内部小动作"的程序,跑在 CPU 内部的微码引擎上
汇编指令(ISA)        ← 你写的、文档里有的
   ↓ 微码翻译
微操作 μops 序列       ← CPU 内部真正执行的
   ↓
硬件电路(ALU/寄存器/缓存)

简单指令 vs 复杂指令

  • 简单指令(ADD eax, ebx):1 条 μop,直接硬连线,不走微码 ROM
  • 复杂指令(ENCLUVMENTERWRMSR、字符串指令):展开成几十甚至上百条 μop,从微码 ROM 读出来执行

SGX 的指令(EENTEREEXITERESUME 等都是 ENCLU 的 leaf)就是典型的复杂指令,全部由微码实现。AEX 也是微码流程。

微码在系统层级中的位置

┌─────────────────────────────┐
│ 应用程序 / OS 内核         │  ← 软件
├─────────────────────────────┤
│ x86 指令集 (ISA)            │  ← CPU 对外承诺
├─────────────────────────────┤
│ 微码 (microcode)            │  ← 介于 ISA 和电路之间的"固件"
├─────────────────────────────┤
│ 硬件电路(执行单元/缓存)   │  ← 真正的晶体管
└─────────────────────────────┘

微码物理上存在两个地方:

  • 片上 Microcode ROM:出厂烧死
  • Microcode Patch RAM:开机时 BIOS/OS 加载的"补丁",可以覆盖 ROM 里的实现 → 这就是 microcode update,Intel 修 Spectre/Foreshadow 就是发新微码

"AEX 是微码实现的"= AEX 的那套动作(保存 SSA、擦寄存器、清 TLB…)写在 CPU 内部的微码里,不是独立芯片,也不是软件


四、Background 3:中断到达时,谁在检查,执行的指令从哪来

中断不是"立刻切到 normal 再 AEX"

顺序是反的:AEX 本身就是那个切换过程,流程是原子的。

                  中断/异常信号到达
                          │
                          ▼
        ┌─────────────────────────────────┐
        │ ① CPU 在指令边界检查待处理中断 │ ← 硬连线退休单元
        └─────────────────────────────────┘
                          │
              当前在 enclave 模式?
                   │            │
                  是            否
                   │            │
                   ▼            ▼
        ┌────────────────┐   走常规中断流程
        │ ② AEX 微码序列 │   (压 CS:RIP、查 IDT…)
        │  ─ 保存 GPR/   │
        │    XSAVE→SSA   │
        │  ─ 合成寄存器  │
        │  ─ 清 TLB      │
        │  ─ RIP←AEP     │
        │  ─ 清 enclave  │
        │    模式位      │
        └────────────────┘
                   │
                   ▼
        ③ 此时已是非 enclave 模式
           走常规中断路径进内核
                   │
                   ▼
        OS 处理 → IRET → AEP → ERESUME → 回到 enclave

整个 ①→③ 是原子的微码流程,中间没有"已在 normal 模式但还没擦干净"的窗口——这是 SGX 安全模型必须保证的。

"在指令边界检查中断"的主体:退休单元(硬连线)

主体是 CPU 的退休单元(Retirement Unit / ROB),不是微码本身。

现代 x86 流水线大致:

┌────────┐  ┌────────┐  ┌─────────┐  ┌─────────┐  ┌────────┐
│ Fetch  │→ │ Decode │→ │ Rename/ │→ │ Execute │→ │ Retire │
│ (取指) │  │ (译码) │  │ Dispatch│  │ (乱序)  │  │ (按序) │
└────────┘  └────────┘  └─────────┘  └─────────┘  └────────┘
                │                                      │
                │ 复杂指令在这里                       │ 中断在这里
                │ 展开成 μop(从微码 ROM)             │ 被识别和插入

中断处理逻辑(硬件)持续监听:

  • 外部中断引脚 / APIC 消息(异步,随时到)
  • 指令执行中产生的异常(同步,执行时触发)

不能在指令执行到一半就跳走——指令必须要么完全执行、要么完全没执行(架构状态原子性)。所以:

  1. 中断信号到达后被锁存在等待标志里
  2. 退休单元按序退休 μop;每次退休一条完整 x86 指令时检查"有没有待处理中断"
  3. 如果有,不退休下一条,而是在这个"指令边界"上插入中断处理

"指令边界"= 上一条 x86 指令已经退休、下一条还没退休的那个点。架构状态在这个点是干净自洽的。

走 AEX 还是常规中断,由谁决定

也是硬连线的退休/异常控制逻辑。它知道一个 CPU 内部的"enclave 模式"状态位(类似 CR_ENCLAVE_MODE):

  • 该位 = 1 → 异常/中断走 AEX 路径
  • 该位 = 0 → 走常规 IDT 路径

它的工作本质就是**"分发到哪段微码"**。

关键疑问:此时执行的"指令"从哪取

中断不是一条 x86 指令,AEX 的 μop 从哪来?

答案:微码不只翻译指令,它还提供"事件处理程序"。

微码 ROM 里的 μop 序列有两类被触发方式:

① 由 x86 指令译码触发(常规)

取指拿到 ENCLU 字节 → 译码识别为复杂指令
→ 查微码 ROM 入口表(MSROM entry point)
→ 执行对应 μop 序列

② 由硬件事件触发:微码注入 / microcode assist

退休单元检测到事件(中断/异常/AEX/VMexit/...)
→ 硬件控制逻辑根据事件类型查"事件入口表"
→ 把对应微码序列起始地址送进流水线
→ 流水线"假装"执行一条隐形指令,跑那段 μop

这种机制 Intel 内部叫 microcode assistucode flow injection。对软件完全不可见——反汇编里看不到任何对应指令,但 CPU 确实在跑代码。

所以 AEX 的"指令"从哪来

不是从内存取的,是从 CPU 内部的微码 ROM/Patch RAM 来的。

  • AEX 微码序列出厂时烧在 microcode ROM 里
  • 有一个固定的事件入口地址(类似中断向量表,但在微码层)
  • 退休单元看到"enclave 模式 + 中断待处理"→ 查表 → 注入入口 → 流水线开始跑 AEX 微码
  • 不经过内存取指、不走 L1I cache、不查 ITLB——CPU 内部的封闭执行流

完整时序图

       中断到达(异步信号)
              │
              ▼
       ┌─────────────────┐
       │ 中断锁存器      │ ← 硬件,持续监听 APIC/引脚
       └─────────────────┘
              │ (等待指令边界)
              ▼
       ┌─────────────────────────────┐
       │ 退休单元在指令边界检查      │ ← 硬连线控制逻辑
       └─────────────────────────────┘
              │
              ▼
       ┌─────────────────────────────┐
       │ 决策:enclave 模式?        │
       │   是 → AEX 微码入口        │
       │   否 → 常规中断微码入口    │
       └─────────────────────────────┘
              │
              ▼
       ┌─────────────────────────────┐
       │ 微码引擎从 ROM/PatchRAM    │
       │ 读取 μop,注入流水线        │
       │ (不经过 L1I / 内存)        │
       └─────────────────────────────┘
              │
              ▼
       μop 执行(保存 SSA、擦寄存器、清 TLB…)
              │
              ▼
       最后一条 μop:RIP←AEP,清 enclave 模式位
              │
              ▼
       现在 = 普通用户态,走常规 IDT 路径进内核

五、防御视角:AEX 是 SGX 侧信道的核心攻击面

1. 攻击者如何滥用 AEX

  • 可控触发:OS/hypervisor 不可信,可以任意频率触发 AEX(高精度 APIC timer)
  • 单步执行(SGX-Step):把 APIC timer 配置成 enclave 一执行就触发,实现指令级单步
  • 页错误通道(Controlled-Channel, Xu et al. 2015):取消 enclave 页 present 位,通过 #PF 序列推断访问模式
  • 结合微架构侧信道:每次 AEX 之间观察 cache / BTB / TLB 状态(Foreshadow、LVI、Plundervolt 的前置条件)

2. AEX-Notify 缓解(SGX2 扩展)

Intel 在新 SGX 上引入 AEX-Notify(CPUID feature,SDM Vol.3D):

  • enclave 注册一个 handler,在 AEX 发生时被通知
  • 让 enclave 在敏感代码段做常数时间补偿:统一执行路径、刷新可观察状态、检测异常高频 AEX
  • 缓解 SGX-Step 这类单步攻击的关键能力

3. 微码相关的检测要点

因为 AEX 是微码实现的,几个直接影响:

  • 没有公开的 AEX 计数 MSR:想统计 AEX 频率,要么 enclave 内用 AEX-Notify handler 计数,要么用 PMU 的 HW_INTERRUPTS.RECEIVED / TLB flush 事件旁敲侧击(噪声大)
  • 微码可更新 → 安全行为可变:Foreshadow 后 Intel 用微码更新让 AEX 时额外刷 L1 cache;分析时必查 microcode revision(cat /proc/cpuinfo | grep microcodeIA32_BIOS_SIGN_ID MSR)
  • 微码注入耗周期但不出现在指令流里 → 攻击者用 RDTSC 能看到"凭空消失的几百周期",这是判断 AEX/微码 assist 发生的信号之一
  • 微码执行会扰动微架构状态(分支预测器、TLB、cache),但不改可见 RIP 序列 → Spectre/L1TF 等利用 AEX 的物理基础

4. Runtime 防护落点

  • enclave 内:AEX-Notify handler(最直接,需 CPU 支持 + SDK 启用)
  • enclave 外可信侧:观察 SSA 的 EXITINFO(enclave 主动检查)
  • 平台侧:PMU 上的中断/TLB flush 事件,作为辅助信号

5. 开发层建议

  • 写敏感代码段时假设 AEX 在任何指令边界都会发生,不能依赖时序
  • 敏感分支、内存访问做常数时间 / oblivious 处理
  • 密钥操作避免按 secret 索引内存(经典 cache-line 粒度泄漏)

六、一句话总结

  • AEX = SGX enclave 被外部事件打断时,由 CPU 微码执行的"安全退出"流程;不是独立硬件,是中断路径上的微码分支
  • 微码 = 介于 ISA 和电路之间的固件,把复杂 x86 指令翻译成 μop;也用于实现硬件事件(中断/异常/AEX)的处理流程
  • 中断时序:退休单元(硬连线)在指令边界检查中断 → 根据 enclave 模式位分发 → 通过"微码注入"从 CPU 内部微码存储调出 AEX μop 序列(不经过内存) → 执行完才进入常规 IDT 路径
  • 防御核心:AEX 是 SGX 信任模型(信 CPU、不信 OS)与 x86 中断架构(中断必给 OS)叠加的攻击面;现代防御 = AEX-Notify + 常数时间代码 + 频率/模式监控 + 跟踪 microcode revision