Enclave OCALL或退出

SGX中,OCALL和Enclave正常退出一样通过EEXIT退出Enclave。

Enclave正常退出Routine:

【sgx_ocall(tRTS)】->【do_ocall(tRTS)】->【__morestack(tRTS, AS)】->【do_ocall(tRTS,AS)】->【__morestack(uRTS, AS)】->【.Leret(uRTS, AS)】->退出__morestack(uRTS, AS)

OCALL执行Routine:

【sgx_ocall(tRTS)】->【do_ocall(tRTS)】->【__morestack(tRTS, AS)】->【do_ocall(tRTS,AS)】->【__morestack(uRTS, AS)】->【stack_sticker(uRTS, AS)】->【sgx_ocall(uRTS)】->【CEnclave::ocall(uRTS)】->【do_ocall(uRTS)】

下面具体展开描述

sgx_ocall(tRTS)

//
// sgx_ocall
// Parameters:
//      index - the index in the ocall table
//      ms - the mashalling structure
// Return Value:
//      OCALL status
//
sgx_status_t sgx_ocall(const unsigned int index, void *ms)

do_ocall(tRTS)

#define do_ocall __morestack

__morestack(tRTS, AS)

/*
 * ------------------------------------------------------------------
 * this function is the wrapper of do_ocall, which is used to
 * stick ocall bridge and proxy frame together
 * ------------------------------------------------------------------
 */
DECLARE_LOCAL_FUNC __morestack

do_ocall(tRTS,AS)

/* 
 * -------------------------------------------------------------------------
 *  sgx_status_t do_ocall(unsigned int index, void *ms);
 *
 *  Function: do_ocall
 *        The entry point of the enclave
 *  Parameters:
 *        func_addr - target function address
 *        ms - marshalling structure
 *
 *  Stack: (same as do_oret)
 *        bottom of stack ->
 *                            -----------------
 *                           | ECALL/OCALL     |
 *    previous TD.last_sp -> |   frames        |
 *                            -----------------
 *                           |   ECALL frame   |
 *                           | do_ocall param 2| 3
 *                           | do_ocall param 1| 2
 *                           |do_ocall ret_addr| 1
 *                           |     xbp         | 0 + xbp
 *                           |     ....        |
 *                           |   xsave buffer  |
 *                           |     ....        |
 *                           |  xsave pointer  | 19
 *                           |   ocall_depth   | 18
 *                           |   reserved      | 17
 *                           |   reserved      | 16
 *                           |   reserved      | 15
 *                           |     rbx         | 14
 *                           |     rsi         | 13
 *                           |     rdi         | 12
 *                           |     rbp         | 11
 *                           |     r12         | 10
 *                           |     r13         | 9
 *                           |     r14         | 8
 *                           |     r15         | 7
 *                           | prev TD.last_sp | 6
 *                           |  ocall_index    | 5
 *                           |   OCALL FLAG    | 4
 *                           |    shadow       | 3
 *                           |    shadow       | 2
 *                           |    shadow       | 1
 *             TD.last_sp -> |    shadow       | 0 + xsp
 *                            -----------------
 * -------------------------------------------------------------------------
 */
DECLARE_LOCAL_FUNC do_ocall

压栈内容主要是ocall_context_t结构体。

typedef struct _ocall_context_t
{
    uintptr_t shadow0;
    uintptr_t shadow1;
    uintptr_t shadow2;
    uintptr_t shadow3;
    uintptr_t ocall_flag;
    uintptr_t ocall_index;
    uintptr_t pre_last_sp;
    uintptr_t r15;
    uintptr_t r14;
    uintptr_t r13;
    uintptr_t r12;
    uintptr_t xbp;
    uintptr_t xdi;
    uintptr_t xsi;
    uintptr_t xbx;
    uintptr_t reserved[3];
    uintptr_t ocall_depth;
    uintptr_t ocall_ret;
} ocall_context_t;

上下文存到栈上,X属性存在xsave buffer,从SYNTHETIC_STATE恢复X属性供Enclave外使用,xdi、xsi沿用原来的,代表了传参。更新last_sp。然后从Thread Data恢复Enclave外bp、sp。从上一个last_sp(如下所示)找到ret_addr存到rbx,将SE_EEXIT存到rax。此外,对各种通用寄存器进行清零。调用EEXIT退出Enclave。

/*
 * set EEXIT registers
 * return address can be read from the ECALL frame:
 *       TD.last_sp ->
 *                     -------------
 *                    | ret_addr    |
 *                    | xbp_u       |
 *                    | xsp_u       |
 *                    | ...         |
 */

注意:压栈的内容都位于Enclave线程栈中,物理上处于受到加密保护的EPC中,压栈目的在于OCALL返回时恢复Enclave内上下文。OCALL时,ocall_index和ms(marshalling parameter)地址分别通过rdi、rsi寄存器传参。ms地址指向位于Enclave外不可信栈上的ms内容,后者事先通过sgx_ocalloc函数分配空间并保存。因此Enclave线程栈上的密文内容,外界无法读取,而被寄存器传递的明文可以被外界读取。

__morestack(uRTS, AS)

然后又回到不可信世界【__morestack(uRTS)】,而且退出Enclave后的位置是一开始进入Enclave时候的ENCLU指令的下一条。

/* 
 * at this point, we may have returned due to a normal EEXIT,
 * or we may have returned due to an OCALL. We differentiate 
 * by popping the top of the stack. If it is not OCMD_ERET, we have
 * an untrusted bridge to call at that address.
 */

/* We have an ocall. Call our bridge function. */

通过OCMD_ERET判断是否正常退出Enclave,如果是,那么直接返回【.Leret】。不然,就需要执行OCALL代码了。

.Ldo_ocall:
/* call ocall
 * int ocall(const unsigned int proc, void *ocall_table, const void *ms, CEnclave *enclave);
 *
 * When EEXIT'ed from tRTS due to an OCALL,
 * - `rdi' holds OCALL index
 *- `rsi' holds the pointer to marshalling structure 
 */

完善参数信息,调用【stack_sticker(uRTS, AS)】

stack_sticker(uRTS, AS)

/*
 * function stack_sticker is the wrapper of ocall,
 * before call ocall, update the ret address and frame pointer (BP) on the stack
 *
 * Stack before:
 *                         |__morestack stack   |<--|
 *                          -------------           |
 *                         |return adress       |   |
 *                  xbp -> | caller xbp         | --|
 *                         |                    |
 *                  xsp -> |                    |
 *                          -------------
 *
 * Stack after:
 *                         |__morestack stack   |
 *                           ------------------
 *                         | __morestack(inside)|
 *                  xbp -> | xbp_t              | ---->the frame point of __morestack
 *                         |                    |
 *                  xsp -> |                    |
 *                         | <ecall>            |
 *                           ------------------
 * int stack_sticker(unsigned int proc, sgx_ocall_table_t *ocall_table, void *ms, CTrustThread *trust_thread, tcs_t *tcs)
 */
DECLARE_GLOBAL_FUNC stack_sticker

我们调整返回地址和栈帧 (BP),trust_thread压入OCALL帧信息(如果当前在调试模式下,还将Enclave外看不到的Enclave内调用栈更新到Enclave外调用栈上),然后调用【sgx_ocall(uRTS)】。

sgx_ocall(uRTS)

extern "C" int sgx_ocall(const unsigned int proc, const sgx_ocall_table_t *ocall_table, void *ms, CTrustThread *trust_thread)
{
    ...
    return enclave->ocall(proc, ocall_table, ms);
}

CEnclave::ocall(uRTS)

int CEnclave::ocall(const unsigned int proc, const sgx_ocall_table_t *ocall_table, void *ms)

此时我们是常规的OCALL,不是特殊的OCALL,进而【do_ocall(uRTS)】。

do_ocall(uRTS)

int do_ocall(const bridge_fn_t bridge, void *ms)
{
    ...
    error = bridge(ms);
    ...
}

执行OCALL

OCALL执行完毕

trust_thread弹出OCALL帧信息,逐级返回到【__morestack(uRTS, AS)】,并重新进入到Enclave,此时进入Enclave的目的编号是【ECMD_ORET】。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值