以SampleCode/Switchless为例。
从Enclave内部以Swtichless模式调用OCALL
// SampleCode/Switchless/Enclave/Enclave.cpp
void ecall_repeat_ocalls(unsigned long nrepeats, int use_switchless) {
sgx_status_t(*ocall_fn)(void) = use_switchless ? ocall_empty_switchless : ocall_empty;
while (nrepeats--) {
ocall_fn();
}
}
进入tRTS端的stub
stub是由sgx_edger8r根据App/Enclave源码动态生成的,先于App/Enclave源码编译之前生成_{t,u}.{c,h}这些stub文件,这些stub文件会在App/Enclave编译时用到。
// SampleCode/Switchless/Enclave/Enclave_t.c
sgx_status_t SGX_CDECL ocall_empty_switchless(void)
{
sgx_status_t status = SGX_SUCCESS;
status = sgx_ocall_switchless(1, NULL);
return status;
}
sgx_ocall_switchless是tRTS端的函数
/*=========================================================================
* The implementation of switchless OCall
*========================================================================*/
sgx_status_t sgx_ocall_switchless(const unsigned int index, void* ms)
在第一次Switchless OCALL时(通过sl_call_once使得仅执行一次),会对tRTS端的OCALL管理器进行初始化。如果Enclave初始化时候没有开启Switchless模式,那么Switchless模式的OCALL会退化成一个普通的Switch模式的OCALL。OCALL管理器初始化放到下面另起章节讲。
/* If Switchless SGX is not enabled at enclave creation, then switchless OCalls
* fallback to the traditional OCalls */
if (sl_call_once(&g_init_ocall_mngr_done, init_tswitchless_ocall_mngr, NULL))
return sgx_ocall(index, ms);
紧接着,如果没有被激活的不可信工人线程,那么OCALL从Switchless模式退化成Switch模式。
如果有休眠的工人线程,让专门用来唤醒工人线程的“叫醒者线程”去唤醒所有线程。
构建OCALL任务,并以Switchless模式处理OCALL。
// perform switchless OCALL
error = sl_call_mngr_call(&g_ocall_mngr, &call_task, g_uswitchless_handle->us_config.retries_before_fallback);
OCALL管理器初始化
// sdk/switchless/sgx_tswitchless/sgx_ocall_switchless.c
static int_type init_tswitchless_ocall_mngr(void* param){
...
// check if switchless has been initialized
...
// g_uswitchless_handle is checked in sl_init_switchless()
...
// clone data from the untrusted side, ensuring all the relevant data is outside enclave
...
// wrong manager type, attack ?
...
}
先检查SGX初始化时候Switchless模式是否开启了。
然后将存放在uRTS端uSwitchless句柄中的OCALL管理器,克隆一份放到tRTS端的【g_ocall_mngr】。通过下面这个函数实现克隆。
// switchless call managers are allocated on untrusted side, then pointers to data structures are passed to the enclave.
// the following function clones the data, performing all neccessary checks
//
// returns 0 on success
uint32_t sl_call_mngr_clone(struct sl_call_mngr* mngr, const struct sl_call_mngr* untrusted) {
...
// copies fields of untrusted structures ensuring that all the data resided on untrusted side
...
// garbage data ? probably an attack
...
// clone internal pointers to siglines structure
...
// check that the call manager is properly initialized
// if not, can indicate an attack
...
// check that the array of task structures is outside enclave
...
}
这里的OCALL管理器克隆类似于ECALL管理器克隆,克隆的内容包括:类型(这里是OCALL)、信号线管理器(“浅”拷贝基础上,因为当前是tRTS,是OCALL的Sender方,构建一个空闲信号线情况数据结构,来统计空闲的信号线)、调用任务(存在uRTS端,这里克隆的是指针)、OCALL表置NULL(tRTS端的OCALL表没用,uRTS端的OCALL工人线程会使用uRTS端的OCALL管理器中记录的OCALL表)、调用处理句柄置NULL(tRTS是OCALL的发送方,而不是处理方)。
检查克隆出来的OCALL管理器类型Type的确是OCALL。
Switchless模式处理OCALL,类似于Switchless模式处理ECALL。总的来说就是把OCALL任务丢给OCALL工人线程,并通过信号线让OCALL工人线程去执行OCALL任务。
OCALL工人线程接受信号线,并处理器OCALL任务
类似于ECALL工人线程的逻辑
【run_worker】->【uworker_process_calls】->【sl_call_mngr_process】->【sl_siglines_process_signals】->查看所有信号线每个Bit确定是否有信号来了->【process_switchless_call】