SGX v2.8开始Enclave内部开始新增pthread库。在这之前,Enclave内部只能维护互斥量同步机制、读写锁同步机制、条件变量同步机制。如下图所示
SGX内部一开始不支持创建pthread线程的原因是因为线程的管理涉及到系统调用,这并不是用户态的Enclave能够完成的。
而现在SGXv2.8之后,为什么能支持SGX内部创建pthread线程呢?
简单来说tRTS pthread最终需要通过OCALL切换到uRTS进行线程管理,只是说tRTS内提供了一些API让SGX内部能够方便的使用pthread线程。
以【pthread_create(tRTS)】为主线来揭露tRTS pthread库的样貌
/*
*: function: create a new thread
* limitation: Currently the "attr" is unused inside SGX pthread_create(). And the thread is created with "PTHREAD_CREATE_JOINABLE" attribute.
* So user need to call pthread_join() to free pthread_create() created thread's resource.
*/
int pthread_create(pthread_t *threadp, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
可以看到【pthread_create】和我们平时直接用的API接口完全一致。《pthread_create(3) — Linux manual page》
不过目前【attr】这个属性参数暂不使用。
做法如下:
首先判断【start_routine】、【arg】均处于Enclave内部。
然后创建一个【pthread_t】变量。
对【pthread_t thread】初始化如下
pthread_t成员变量 | 赋值 | 说明 |
lock | SGX_SPINLOCK_INITIALIZER | |
tid | NULL | |
retval | NULL | 返回值 |
joiner_td | WAITER_TD_NULL | JOIN者的线程数据 |
creater_td | (sgx_thread_t)get_thread_data() | 当前线程也就是创建者线程的线程数据 |
state | _STATE_PREPARING | |
start_routine | start_routine | Hook上入口函数 |
arg | arg | 传参 |
将【pthread_t->common_queue_elm】的地址挂到【g_work_queue】链表的尾部,期间用【g_work_queue_lock】来加锁。
创建者线程调用【pthread_create_ocall】切换上下文进入到uRST,创建新的pthread线程
extern "C" sgx_status_t pthread_create_ocall(unsigned long long self)
首先获取从传参的TCS(该线程还在tRTS时,从它的线程数据中找到)找到当前的Enclave。
然后获取一个空闲的TCS。
初始化一下属性,并将新线程设置为【PTHREAD_CREATE_DETACHED】,使用uRTS下标准的【pthread_create】函数创建线程。值得注意的是【pthread_create(uRTS)】创建的新线程的入口函数是【pthread_create_routine】,由【pthread_create_routine】到时候会去调用之前【pthread_create(tRTS)】中Hook好的真正的入口函数。
创建者线程回到tRTS,循环等待新线程,直到其状态从【_STATE_PREPARING】变成【_STATE_EXECUTION】。
新线程先开始执行【pthread_create_routine】,切换上下文进入tRTS
static void* pthread_create_routine(void* arg)
将当前Enclave的引用数加一。
将新线程的线程ID和创建者申请来给新线程的TCS进行绑定。
然后使用【CEnclave::ecall】一路切换上下文进入tRTS。可以参考《ECALL Switch模式》
进入tRTS后,在查ECALL符号表时,常规ECALL回去查符号表,而此时我们这种创建tRTS pthread的情况下【ECMD_ECALL_PTHREAD】,我们会直接执行【_pthread_thread_run】
新线程执行真正的入口函数前,进行一些信息的维护,并通知创建者线程
/*
* Note: This function is the entry point of sgx_pthread
*/
sgx_status_t _pthread_thread_run(void* ms)
先从【g_work_queue】链表头部获取当前会用到的【pthread_t】变量,里面包含了很多新线程的相关信息(之前设置过)。
更新当前线程的TLS信息【pthread_info_tls】
【__thread pthread_info pthread_info_tls】成员变量 | 赋值 | 说明 |
m_pthread | 【pthread_t thread】变量 | TLS所对应的线程句柄 |
m_local_storage | NULL | 线程本地存储 |
更新线程句柄【pthread_t thread】
【pthread_t thread】成员变量 | 赋值 |
tid | 【__thread pthread_info pthread_info_tls】 |
state | 状态赋值为【_STATE_EXECUTION】 |
然后唤醒创建者线程继续创建者线程自己的流程。
新线程执行当时Hook的入口函数,新线程创建完毕
//Execute the thread function
thread->start_routine(thread->arg);
新版《Intel_SGX_Developer_Reference_Linux_Open_Source》手册从2.8版本开始也有了SGX内pthread库的描述
截图来自《Intel_SGX_Developer_Reference_Linux_2.11_Open_Source》