本文内容来自于Intel vol3 Chapter 36 Enclave Exiting Events。
第36章描述了在执行飞地代码时由事件引起的异步飞地退出(AEX)的行为。
当一些与执行enclave代码相关但异步发生的事件(如异常和中断)发生时,会导致控制权转移到enclave模式之外。(这些事件大多也导致特权级别的变化。)为了保护enclave的完整性和安全性,在处理此类事件之前,处理器会退出enclave(和enclave模式)。因此,这些事件被称为enclave退出事件(enclave-exiting events - EEE)。EEE包括外部中断、不可屏蔽中断、系统管理中断、异常和虚拟机退出。
响应EEE而离开enclave的过程称为异步enclave退出(asynchronous enclave exit - AEX)。为了保护enclave的机密性,AEX会保存enclave内存中某些寄存器的状态,并将这些寄存器加载为称为合成状态(synthetic state)的固定值。
在AEX中,寄存器会加载预先确定的合成状态。这些寄存器稍后可以以enclave退出事件定义的形式推送到适当的堆栈中。为了在调用处理程序处理完enclave退出事件后恢复enclave执行,异步enclave退出会将enclave之外的跳转代码的地址加载到RIP中。这个跳转代码最终通过ENCLU(ERESUME)叶函数返回到enclave中。在退出enclave之前,RSP和RBP寄存器被恢复为进入enclave之前的值。
在选择要使用的堆栈时,遵循与非SGX模式相同的规则:
(1)如果存在特权级别变化,堆栈将是与新特权级别相关联的堆栈。
(2)如果没有特权级别变化,将使用当前应用程序堆栈。
(3)如果使用了IA-32e IST机制,则使用该方法选择退出堆栈。
在所有情况下,退出堆栈的选择以及推送到堆栈上的信息与非SGX操作一致。图36-1显示了在具有堆栈切换的退出后的应用程序堆栈和退出堆栈。没有堆栈切换的退出使用应用程序堆栈。ERESUME叶索引值被放置在RAX中,TCS指针被放置在RBX中,AEP(见下文)被放置在RCX中,以便在退出后重新启动enclave。
在AEX时,AEP(异步退出指针)被加载到RIP中。AEP指向一个包含ERESUME指令的跳转代码序列,该指令稍后用于重新进入enclave。
在将RFLAGS推送到退出堆栈之前,会清除以下标志位:CF,PF,AF,ZF,SF,OF,RF。其余的标志位保持不变。
State Save Area(SSA)在AEX发生时保存处理器的状态。为了在enclave内处理事件并在AEX之后重新进入enclave,SSA可以是多个SSA帧的堆栈,如图36-2所示:
SSA帧的位置由TCS(Thread Control Structure)和SECS(Security Enclave Control Structure)中的以下变量控制:
(1)State Save Area中每个帧的大小(SECS.SSAFRAMESIZE):该变量定义State Save Area中单个帧的大小,以4KB页的数量表示。SSA帧的大小必须足够大,以容纳通用寄存器状态(GPR state)、XSAVE状态和MISC状态。
(2)Enclave的基地址(SECS.BASEADDR):该变量定义enclave的基线性地址,从该地址计算到SSA堆栈基址的偏移量。
(3)State Save Area Slots的数量(TCS.NSSA):该变量定义State Save Area堆栈中的总槽数(帧数)。
(4)当前State Save Area Slot(TCS.CSSA):该变量定义在下一次退出时要使用的槽位。
(5)State Save Area Offset(TCS.OSSA):该变量定义一组State Save Area槽位的基地址相对于enclave基地址的偏移量。
这些变量的设置确定了SSA帧的位置和使用情况,以便在AEX后正确保存和恢复处理器状态。
当发生AEX时,硬件通过检查TCS.CSSA来选择要使用的SSA帧。处理器状态被保存到SSA帧中(参见第4节),并加载具有合成状态的值(如第3.1节所述),以避免泄露机密信息,RSP和RBP被恢复为进入enclave之前的值,并且TCS.CSSA被递增。如后面所述,如果异常发生在最后一个槽位,将无法从enclave内部重新进入enclave处理该异常。随后的ERESUME从当前SSA帧中恢复处理器状态,并释放SSA帧。SSA的XSAVE部分的格式与XSAVE/XRSTOR指令使用的格式相同。在执行EENTER时,CSSA必须小于NSSA,以确保至少有一个可用于退出的State Save Area槽位。如果在执行EENTER时没有空闲的SSA帧,则入口将失败。
Table 36-1显示了在AEX时加载的合成状态。所示的值是处理器处于32位模式时的低32位,或者处理器处于64位模式时的64位。
当CR4.OSXSAVE = 1时,当设置SECS.XFRM的相应位时,扩展特性(由XCR0[63:2]控制)将设置为它们各自的INIT状态。INIT状态是如果XRSTOR指令的指令掩码和XSAVE头的XSTATE_BV字段分别包含值XFRM,则会加载的状态。(当在32位模式下发生AEX时,32位模式下不存在的特性保持不变。)
SECS.MISCSELECT所表示的状态在保存到SSA后也可能被合成状态覆盖。MISCSELECT[0]所表示的状态不会被覆盖,但如果退出事件是页面故障,则CR2的低12位将被清除。
在enclave退出事件(中断、异常、VM退出或SMI)发生时,处理器状态被安全地保存在enclave内部,加载合成状态,然后退出enclave。然后,EEE(Enclave Exiting Event)按照通常的退出定义方式继续进行处理。
以下部分详细描述了AEX的细节:
(1)将当前SSA帧中保存的处理器状态的精确信息取决于飞地是32位还是64位飞地。在32位模式下(IA32_EFER.LMA = 0 || CS.L = 0),保存低32位的传统寄存器(EAX、EBX、ECX、EDX、ESP、EBP、ESI、EDI、EIP和EFLAGS)。不保存传统寄存器的高32位和64位寄存器(R8 … R15)。
在64位模式下(IA32_EFER.LMA = 1 && CS.L = 1),保存通用处理器寄存器的所有64位(RAX、RBX、RCX、RDX、RSP、RBP、RSI、RDI、R8 … R15、RIP和RFLAGS)。
由SECS.ATTRIBUTES.XFRM指定的扩展特性的状态保存在当前SSA帧的XSAVE区域中。x87和XMM部分(前512字节)的布局取决于IA32_EFER.LMA和CS.L的当前值:
如果IA32_EFER.LMA = 0 || CS.L = 0,则使用与这些值相同的格式(32位),与XSAVE/FXSAVE使用的格式相同。
如果IA32_EFER.LMA = 1 && CS.L = 1,则使用与这些值相同的格式(64位),当REX.W = 1时与XSAVE/FXSAVE使用的格式相同。
AEX的原因保存在EXITINFO字段中。有关各个字段的详细信息和值,请参见Intel vo3 第34节表34-10。
由SECS.MISCSELECT指定的其他特性(参见Intel vo3 第34.7.2节)的状态保存在当前SSA帧的MISC区域中。
如果在飞地中启用了CET,则飞地的CET状态保存在CET状态保存区域中。如果在飞地中启用了影子堆栈,则SSP也保存在TCS.PREVSSP字段中。
(2)为了呈现飞地状态的不透明视图,为一些处理器寄存器创建了合成状态。表36-1显示了AEX中GPRs、x87、SSE、FS、GS、Debug和性能监视的值。其他扩展特性(由XCR0[62:2]控制)的合成状态在SECS.ATTRIBUTES.XFRM的相应位设置时设置为它们各自的INIT状态。INIT状态是当HEADER.XSTATE_BV[n]为0时,由XRSTOR指令的行为定义的状态。由SECS.MISCSELECT指定的其他杂项特性的合成状态取决于各自的杂项特性。对于由SECS.MISCSELECT[0]控制的杂项状态,不需要合成状态。
(3)在退出飞地时,任何在进入飞地时被禁止的代码和数据断点将被取消禁止。
(4)RFLAGS.TF被设置为最近一次进入飞地时的值(除非该进入是为了调试而选择的;参见Intel第39.2节)。在SSA中,RFLAGS.TF被设置为0。
(5)在合成状态下,RFLAGS.RF被设置为0。在SSA中,保存的值与在非SGX情况下(RF的体系结构值)在堆栈上保存的值相同。因此,由于中断、陷阱和代码断点而引起的AEX将未修改地将RF保存到SSA中,而由于其他故障引起的AEX将在SSA中将RF保存为1。
如果导致AEX的事件发生在REP前缀指令的中间迭代中,则无论其优先级如何,都会在SSA中保存RF=1。
(6)在退出线程时,由于进入线程时的飞地入口而被禁止的任何性能监视活动(包括PEBS)或分析活动(LBR,使用Intel PT进行跟踪)将被取消禁止。任何已从AnyThread计数降级为MyThread计数(在一个逻辑处理器上)的计数将重新提升为AnyThread计数。
(7)在退出飞地时,将恢复封闭应用程序的CET状态到最近一次飞地入口时的状态。如果启用了CET间接分支跟踪,则将取消禁止间接分支跟踪器并将其移至WAIT_FOR_ENDBRANCH状态。
Intel vol3