目录

构建CEnclave对象

往单例模式的CEnclavePool加入上面构造的CEnclave对象

CEnclave对象保存属于自己的TCS

根据选择开启调试

CEnclave类对象阶段性更新结果

Intel VTune Profiler设置

其他调试相关操作

裁剪敏感页面、动态页面

让tRTS完成一些初始化操作

进入【do_init_enclave(tRTS)】,执行【ECMD_INIT_ENCLAVE】任务

完成Enclave内部的符号地址重定位、CPU信息备案、库优选过程

Enclave符号、地址重定位

梳理系统配置特性情况

初始化并优选字符串库和密码学库

“保留内存”的初始化

构建__stack_chk_guard

回到do_init_enclave,开始初始化当前线程相关内容

回到do_init_enclave,将堆区和保留内存区清零及裁剪接受

Enclave基本的初始化完成

后续


继《再回顾SGX初始化(二)——创建Enclave》将Enclave成功加载之后,这一篇主要讲CEnclave对象及其TCS列表的构建维护、调试模式的配置、第一次进入Enclave并在tRTS端完成一些必要的初始化工作【诸如Enclave内部符号和地址的重定位过程、字符串库、加密库优选等操作】。

从这里开始就是继《再回顾SGX初始化(二)——创建Enclave》之后,__create_enclave的后续步骤

构建CEnclave对象

// initialize the enclave object
ret = enclave->initialize(file,loader,metadata->enclave_size,metadata->tcs_policy,enclave_version,metadata->tcs_min_pool);

首先根据uRTS版本号和元数据版本号(元数据版本号在加载加载Enclave的时候就已经验证过了)确定Enclave(SDK、tRTS)的版本号。然后就开始CEnclave对象的初始化:

CEnclave类对象成员变量 设置值

m_enclave_info

成员变量

设置值
.lpFileName 赋值为一个Enclave文件名长度加4的缓冲区,用于存放Enclave文件名字符串,加四是为了至少能保证存放下Unicode的'\0'。
.unicode 根据是否是Unicode编码来初始化
.file_name_size 初始化为Enclave文件名长度

.g_peak_heap_used_addr(用于dbg)

获取该符号的绝对虚拟地址

CLoader::get_symbol_address("g_peak_heap_used")

.g_peak_rsrv_mem_committed_addr(用于dbg)

获取该符号的绝对虚拟地址

CLoader::get_symbol_address("g_peak_rsrv_mem_committed")

.start_addr(用于dbg)

Enclave虚拟地址空间的基址

CLoader::m_secs.base

.misc_select(用于dbg)

CLoader::m_secs.misc_select

m_enclave_id

CLoader::m_enclave_id

m_start_addr

CLoader::m_start_addr

m_size

metadata->enclave_size

m_version

前面确定的enclave_version

m_new_thread_event

一个int大小的缓冲区,用作FUTEX

m_thread_pool

TCS策略

设置值
TCS_POLICY_BIND

CThreadPoolBindMode(继承自CTrustThreadPool)类对象。

其中CThreadPoolBindMode::m_tcs_min_pool赋值为tcs_min_pool

TCS_POLICY_UNBIND

【两者区别在于Bind模式下,TCS和线程ID一一绑定,当线程退出后,那么对应Bind的TCS就应该标记为空闲,而UnBind的TCS只有在该TCS引用数为0时才收回。因此会导致两者回收空闲CTrustThread(对应一个TCS)的机制不同(CThreadPool{Un}BindMode::garbage_collect)】

CThreadPoolUnBindMode(继承自CTrustThreadPool)类对象。

其中CThreadPoolUnBindMode::m_tcs_min_pool赋值为tcs_min_pool

m_dynamic_tcs_list_size

之前建立布局(build_context)的时候我们已经将TCS布局加入到m_tcs_list。那个时候我们用一个Bool变量保存了m_tcs_list中每一项是不是动态的,这里我们就根据这个Bool变量来计数共有几个动态TCS,赋值给m_dynamic_tcs_list_size

m_target_info

成员变量 设置值

.mr_enclave

CLoader::m_secs.mr_enclave

.attributes CLoader::m_secs.attributes
.config_svn CLoader::m_secs.config_svn
.misc_select CLoader::m_secs.misc_select
.config_id,是个uint8_t数组 CLoader::m_secs.config_id

m_sealed_key

从扩展特性数组(ex_features_p)中提取出密封密钥

m_dbg_flag(用于dbg)

从CLoader::m_secs.attributes.flags中判断是否设置了Debug标识。这里直接从SECS中查看设置了Debug标识更加准确。EPC中的SECS也是按照CLoader的这个SECS建立的。

往单例模式的CEnclavePool加入上面构造的CEnclave对象

//add enclave to enclave pool before init_enclave because in simualtion
//mode init_enclave will rely on CEnclavePool to get Enclave instance.
if (FALSE == CEnclavePool::instance()->add_enclave(enclave))

构建一个临界区,将之前构建的CEnclave对象的CEnclave::m_enclave_id和CEnclave类对象本身封装成一个Node类对象,加入到CEnclavePool::m_enclave_list。

CEnclave对象保存属于自己的TCS

enclave->add_thread(tcs_list[idx].first, tcs_list[idx].second);

CLoader::m_tcs_list中记录了之前构建布局时保存的所有TCS,其中有一部分TCS被标记为动态,代表着这个TCS是后续按需动态生成的(之前不分配EPC空间),由一个Bool变量来记录其动态与否。将TCS加入到CEnclave::m_thread_pool。

CEnclave类对象成员变量 设置值

m_enclave_info

成员变量

设置值
.lpFileName 赋值为一个Enclave文件名长度加4的缓冲区,用于存放Enclave文件名字符串,加四是为了至少能保证存放下Unicode的'\0'。
.unicode 根据是否是Unicode编码来初始化
.file_name_size 初始化为Enclave文件名长度

.g_peak_heap_used_addr(用于dbg)

获取该符号的绝对虚拟地址

CLoader::get_symbol_address("g_peak_heap_used")

.g_peak_rsrv_mem_committed_addr(用于dbg)

获取该符号的绝对虚拟地址

CLoader::get_symbol_address("g_peak_rsrv_mem_committed")

.start_addr(用于dbg)

Enclave虚拟地址空间的基址

CLoader::m_secs.base

.misc_select(用于dbg)

CLoader::m_secs.misc_select

m_enclave_id

CLoader::m_enclave_id

m_start_addr

CLoader::m_start_addr

m_size

metadata->enclave_size

m_version

前面确定的enclave_version

m_new_thread_event

一个int大小的缓冲区,用作FUTEX

m_thread_pool

TCS策略

设置值
TCS_POLICY_BIND

CThreadPoolBindMode(继承自CTrustThreadPool)类对象。

其中CThreadPoolBindMode::m_tcs_min_pool赋值为tcs_min_pool

TCS_POLICY_UNBIND

【两者区别在于Bind模式下,TCS和线程ID一一绑定,当线程退出后,那么对应Bind的TCS就应该标记为空闲,而UnBind的TCS只有在该TCS引用数为0时才收回。因此会导致两者回收空闲CTrustThread(对应一个TCS)的机制不同(CThreadPool{Un}BindMode::garbage_collect)】

CThreadPoolUnBindMode(继承自CTrustThreadPool)类对象。

其中CThreadPoolUnBindMode::m_tcs_min_pool赋值为tcs_min_pool

m_dynamic_tcs_list_size

之前建立布局(build_context)的时候我们已经将TCS布局加入到m_tcs_list。那个时候我们用一个Bool变量保存了m_tcs_list中每一项是不是动态的,这里我们就根据这个Bool变量来计数共有几个动态TCS,赋值给m_dynamic_tcs_list_size

m_target_info

成员变量 设置值

.mr_enclave

CLoader::m_secs.mr_enclave

.attributes CLoader::m_secs.attributes
.config_svn CLoader::m_secs.config_svn
.misc_select CLoader::m_secs.misc_select
.config_id,是个uint8_t数组 CLoader::m_secs.config_id

m_sealed_key

从扩展特性数组(ex_features_p)中提取出密封密钥

m_dbg_flag(用于dbg)

从CLoader::m_secs.attributes.flags中判断是否设置了Debug标识。这里直接从SECS中查看设置了Debug标识更加准确。EPC中的SECS也是按照CLoader的这个SECS建立的。

m_thread_pool(CTrustThreadPool类对象)

【更新】

CLoader::m_tcs_list中的所有tcs都一一加入

具体做法是先将CLoader::m_tcs_list中记录的所有TCS和当前这个CEnclave封装成一个CTrustThread类对象“trust_thread”(其中会构建CTrustThread::m_tcs_info),然后将这个CTrustThread类对象“trust_thread”加入CTrustThreadPool中对应的成员数据结构

TCS类型(逐项的加入池中) 操作
普通TCS 支持EDMM(调用EnclaveCreatorHW::is_EDMM_supported)、并且m_utility_thread尚未构建、并且动态TCS的数量(CEnclave::m_dynamic_tcs_list_size,前面初始化过)不为0。

把当前这个“trust_thread”作为唯一的CTrustThreadPool::m_utility_thread。这是一个通用的TCS,只在支持EDMM的情况下会存在,用来做一些它适合做的特殊工作,后续会用到它。

其他情况(比如不支持EDMM或已有通用TCS了或动态TCS为空)

“trust_thread”加入到CTrustThreadPool::m_free_thread_vector,说明这个TCS被作为一个空闲的已经分配EPC的TCS。

动态TCS

“trust_thread”加入到CTrustThreadPool::m_unallocated_threads,说明这个TCS是一个动态的、事先未分配EPC空间地TCS。

将当前TCS封装成的CTrustThread::m_tcs_info插入到当前CEnclave的m_enclave_info.tcs_list中。方便dbg信息中TCS信息的记录和使用。

//EDMM is supported if and only if all of the following requirements are met:
//1. We operate in HW mode
//2. CPU has EDMM support
//3. Driver has EDMM support
//4. Both the uRTS version and enclave (metadata) version are higher than 1.5
bool EnclaveCreatorHW::is_EDMM_supported(sgx_enclave_id_t enclave_id)
//检测过程中,先用Enclave ID从CEnclavePool找到对应的CEnclave(也就是我们现在在讨论的这个)。

根据选择开启调试

然后根据调试模式是否开启、是否使用SGX硬件设置CEnclave::m_enclave_info.enclave_type的标识位。

CEnclave类对象阶段性更新结果

CEnclave类对象成员变量 设置值

m_enclave_info(debug_enclave_info_t类型,后续也被当做debug_info来记录调试信息)

【更新】

成员变量

设置值
.lpFileName 赋值为一个Enclave文件名长度加4的缓冲区,用于存放Enclave文件名字符串,加四是为了至少能保证存放下Unicode的'\0'。
.unicode 根据是否是Unicode编码来初始化
.file_name_size 初始化为Enclave文件名长度

.g_peak_heap_used_addr(用于dbg)

获取该符号的绝对虚拟地址

CLoader::get_symbol_address("g_peak_heap_used")

.g_peak_rsrv_mem_committed_addr(用于dbg)

获取该符号的绝对虚拟地址

CLoader::get_symbol_address("g_peak_rsrv_mem_committed")

.start_addr(用于dbg)

Enclave虚拟地址空间的基址

CLoader::m_secs.base

.misc_select(用于dbg)

CLoader::m_secs.misc_select

.tcs_list(debug_tcs_info_t类型,是个链表,用于dbg)【更新】 将每个TCS调试信息(CTrustThread::m_tcs_info)插入这个tcs_list

.enclave_type【更新】

根据是否开了调试,对enclave_type中ET_DEBUG进行设置。

根据是否使用SGX模拟模式,对enclave_type中ET_SIM进行设置。

m_enclave_id

CLoader::m_enclave_id

m_start_addr

CLoader::m_start_addr

m_size

metadata->enclave_size

m_version

前面确定的enclave_version

m_new_thread_event

一个int大小的缓冲区,用作FUTEX

m_thread_pool(CTrustThreadPool类型)

【更新】

所选的TCS策略

设置值 左侧为根据TCS策略初始化m_thread_pool。右侧是m_thread_pool进一步包含的成员变量 CLoader::m_tcs_list中的所有tcs都逐一添加到m_thread_pool m_thread_pool的成员变量 赋值

TCS_POLICY_BIND

CThreadPoolBindMode(继承自CTrustThreadPool)类对象。

其中CThreadPoolBindMode::m_tcs_min_pool赋值为tcs_min_pool

m_utility_thread【更新】 支持EDMM等情况下,使用普通CTrustThread对象
(由TCS等信息封装)来作为唯一这个通用线程TCS
m_free_thread_vector【更新】 普通CTrustThread对象,空闲的已经分配了EPC的TCS数组。

TCS_POLICY_UNBIND

【两者区别在于Bind模式下,TCS和线程ID一一绑定,当线程退出后,那么对应Bind的TCS就应该标记为空闲,而UnBind的TCS只有在该TCS引用数为0时才收回。因此会导致两者回收空闲CTrustThread(对应一个TCS)的机制不同(CThreadPool{Un}BindMode::garbage_collect)】

CThreadPoolUnBindMode(继承自CTrustThreadPool)类对象。

其中CThreadPoolUnBindMode::m_tcs_min_pool赋值为tcs_min_pool

m_unallocated_threads【更新】 动态CTrustThread对象,动态的、事先未分配EPC空间的TCS数组。

m_dynamic_tcs_list_size

之前建立布局(build_context)的时候我们已经将TCS布局加入到m_tcs_list。那个时候我们用一个Bool变量保存了m_tcs_list中每一项是不是动态的,这里我们就根据这个Bool变量来计数共有几个动态TCS,赋值给m_dynamic_tcs_list_size

m_target_info

成员变量 设置值

.mr_enclave

CLoader::m_secs.mr_enclave

.attributes CLoader::m_secs.attributes
.config_svn CLoader::m_secs.config_svn
.misc_select CLoader::m_secs.misc_select
.config_id,是个uint8_t数组 CLoader::m_secs.config_id

m_sealed_key

从扩展特性数组(ex_features_p)中提取出密封密钥

m_dbg_flag(用于dbg)

从CLoader::m_secs.attributes.flags中判断是否设置了Debug标识。这里直接从SECS中查看设置了Debug标识更加准确。EPC中的SECS也是按照CLoader的这个SECS建立的。

Intel VTune Profiler设置

如果开启了调试或者SGX模拟模式,我们还需要初始化Intel VTune中的ITT(instrumentation and tracing technology)并检测是否可用。然后用getenv检测"SGX_DBG_OPTIN"调试选项是否开启。

如果VTune或者“SGX_DBG_OPTIN”开启,那么首先将CEnclave::m_enclave_info.tcs_list中的每个TCS调试信息的调试标识【位置为debug_tcs_info_t::TCS_address + sizeof(uint64_t),可以参考《SGX结构体》中tcs_t结构体】更新为1(通过对/proc/${pid}/mem对应的文件句柄直接进行内存修改,因为这些信息都是存于SGX外部,所以可以修改)。

然后,如果VTune确认开启,那么就对VTune的ITT模块加载,参数包括了Enclave的起始虚拟地址、结束虚拟地址、Enclave文件的绝对路径,用于后续调试。

其他调试相关操作

设置当前Enclave调试信息的结构体版本(CEnclave::m_enclave_info.struct_version),然后把它插入到一个全局的记录所有Enclave调试信息的链表(g_debug_enclave_info_list)。目的就是为了将调试事件作为一个异常(我记得调试都是作为一个异常来触发的)挂到调试器中。

裁剪敏感页面、动态页面

如果SGX支持EDMM(使用的硬件模式、CPU支持EDMM、驱动支持EDMM、uRTS和Enclave元数据版本号大于1.5,其实我们前面提到过EnclaveCreatorHW::is_EDMM_supported)。那么我们在EINIT之后需要额外地将敏感的(如TCS)、动态的(动态栈、动态堆、动态线程布局)布局页面逐一地从ELRANGE中裁剪掉(向SGX驱动发送信令SGX_IOC_ENCLAVE_TRIM,由SGX驱动代为完成裁剪任务)。值得注意的是裁剪动作需要Enclave内部调用EACCEPT接受,裁剪动作才算完成,因此这是其中一个需要进入tRTS进行初始化的原因,不过在接受裁剪之前,tRTS还需要用到这些可能被裁剪的布局进行一些调整操作。

让tRTS完成一些初始化操作

//call trts to do some intialization
if(SGX_SUCCESS != (ret = get_enclave_creator()->initialize(loader.get_enclave_id())))

主要是为了完成Enclave布局的调整使得Enclave能够真正执行,并且接受页面裁剪动作。

事先准备一个记录系统特征的结构体(system_features_t),tRTS做一些初始化操作时要用到,但是tRTS内部无法使用CPUID硬件指令等。

system_features_t成员变量 操作或赋值

.cpu_features

根据CPUID的检测情况对这个成员变量的每个标识Bit进行逐Bit的赋值。

CPUID.0x0.EAX

判断所支持的叶功能数量。

CPUID.0x0.[EBX-EDX]

判断当前CPU是不是Intel的(“GenuineIntel”)。

CPUID.0x1.EAX

判断该CPU是否是凌动(ATOM)系列。

CPUID.0x1.EDX

(参考《Intel 64 and IA-32手册 2》Table 3-11. More on Feature Information Returned in the EDX Register)

判断该CPU是否FPU、CMOV、Intel MMX(Multi-media Extension Technology)、FXSR(FXSAVE和FXRSTOR指令, 保存/恢复x87 FPU、MMX技术和SSE状态)、SSE(当支持FXSR时检测该项,后缩略为“FXSR Valid”)、SSE2【FXSR Valid】

CPUID.0x1.ECX

(参考《Intel 64 and IA-32手册 2》Table 3-10. Feature Information Returned in the ECX Register)

判断该CPU是否支持SSE3【FXSR Valid】、SSSE3【FXSR Valid】、MOVBE【FXSR Valid】、SSE4_1【FXSR Valid,Penryn架构】、SSE4_2【FXSR Valid,Penryn架构】、POPCNT【FXSR Valid】、PCLMULQDQ【FXSR Valid】、AESNI【FXSR Valid】、RDRAND(IvyBridge架构)、XSR(XSAVE和XRSTOR指令,保存/恢复处理器扩展状态)、AVX(Advanced Vector Extensions,XSR Valid)、F16C(16-bit floating-point conversion instructions,IvyBridge架构,XSR Valid)、FMA(Fused-Multiply-Add,Haswell架构,XSR Valid)

CPUID.0x7.0.EBX

判断该CPU是否支持BMI(Bit-Manipulation Instructions,v1和v2,Haswell架构)、HLE(Hardware Lock Elision,Haswell架构)、RTM(Restricted Transactional Memory,Haswell架构)、RDSEED(Read Random SEED,Haswell架构)、ADX (Multi-Precision Add-Carry Instruction Extensions,Haswell架构)、AVX(Advanced Vector Extensions,Haswell架构,XSR Valid)

CPUID.0x80000001.0.ECX

判断该CPU是否支持LZCNT(Count the Number of Leading Zero Bits)、PREFETCHW(Prefetch Data into Caches in Anticipation of a Write)

.cpu_features_ext CPU扩展特性的支持情况检测及标注(get_cpu_features_ext函数,位于psw/urts/cpu_features_ext.cpp)

.cpuinfo_table

将CPUID.0x0|0x1|0x4|0x7的CPUID信息全都保存到这个CPU信息表中,后续“支持FIPS的OPENSSL”会用到

.system_feature_set[0](实际上是一个长度只有1的数组)

对它标记SYS_FEATURE_EXTEND位。

如果支持EDMM,额外标记EDMM_ENABLE_BIT位(0x1ULL)

.size

system_features_t结构体的大小

.version

min(v2.2,Enclave版本号【CEnclave::m_version】)

.sealed_key

CEnclave::m_sealed_key,如果支持PCL,那么之前会从扩展特性数组中提取并保存到CEnclave::m_sealed_key

.cpu_core_num

当前在线(可用)的处理器数量【sysconf(_SC_NPROCESSORS_ONLN)】

system_features_t结构体准备完。

切换上下文进入Enclave,进入【do_init_enclave】函数执行【ECMD_INIT_ENCLAVE】任务。

切换上下文进入Enclave的方式称为ECALL Switch模式。请参考《ECALL Switch模式》

进入【do_init_enclave(tRTS)】,执行【ECMD_INIT_ENCLAVE】任务

sgx_status_t do_init_enclave(void *ms, void *tcs) __attribute__((section(".nipx")));

首先我们要获得Enclave基址(VA)。之前编译enclave.so时加入【-Wl,--defsym,__ImageBase=0】编译选项(-Wl说明这是个链接选项),使得__ImageBase位于ELRANGE的起始地址(0代表__ImageBase处于Enclave内的的0偏移地址),获取__ImageBase就可以知道ELRANGE的基址(通过调用get_enclave_base汇编函数)。

【重要:__ImageBase指向的是Enclave文件映射基址还是ELRANGE基址,我目前有点迷糊了,个人目前倾向于它是ELRANGE的基址】

紧接着将Enclave状态从ENCLAVE_INIT_NOT_STARTED转换到ENCLAVE_INIT_IN_PROGRESS(调用lock_enclave)。

然后调用

if(0 != init_enclave(enclave_base, ms))

完成Enclave内部的符号地址重定位、CPU信息备案、库优选过程

调用init_enclave.

// init_enclave()
//      Initialize enclave.
// Parameters:
//      [IN] enclave_base - the enclave base address
//      [IN] ms - the marshalling structure passed by uRTS
// Return Value:
//       0 - success
//      -1 - fail
//
extern "C" int init_enclave(void *enclave_base, void *ms)

这里的参数ms就是我们很早之前初始化的system_features_t对象.

如果我们用了PCL特性的,Enclave代码是密封起来的,那么就用system_features_t.sealed_key对密封的代码进行解密,这个密封密钥是程序员开发的不可信APP部分中调用sgx_create_encrypted_enclave时传入的sealed_blob,还记得最开始说的扩展特性数组ex_features_p吗?

不过我们这里没有用PCL扩展特性,这也是一个比较新的特性。

Enclave符号、地址重定位

然后对Enclave文件映射中的所有重定位的符号、PLT项进行重定位设置(调用relocate_enclave函数)。具体参考《SGX初始化末期对ELF文件映射的重定位项进行配置》

梳理系统配置特性情况

接着对ms(marshalling structure)参数,也就是system_features_t对象(前面有system_features_t成员变量表格),检查确保其存储在Enclave外部(调用sgx_is_outside_enclave,根据起止地址来检查)。并通过sgx_lfence保证检查中的load指令已经完成。【这一步需要在重定位过程完成后进行,原因尚不清楚。】

// sgx_is_outside_enclave()
// Parameters:
//      addr - the start address of the buffer
//      size - the size of the buffer
// Return Value:
//      1 - the buffer is strictly outside the enclave
//      0 - the whole buffer or part of the buffer is not outside the enclave,
//          or the buffer is wrap around
//
int sgx_is_outside_enclave(const void *addr, size_t size)

【扩展一下】由于sgx_is_outside_enclave其中的一个参数是指针的size,当指针是一个字符串指针时,我们就会需要strlen获取字符串的长度来作为参数size。《A Tale of Two Worlds: Assessing the Vulnerability of Enclave Shielding Runtimes》(YouTu be上有会议录屏)就利用sgx-step对strlen进行侧信道,获取字符串长度,并得知字符串中最后一个'\0'(ASCII码0)所在的index,在AES中,下一轮的密文产自上一轮的密文(第一轮是明文),也就是:第n轮密文X位(Bit)=SBOX(第n-1轮第Y位)“异或”n-1轮的轮密钥。由于我们知道n-1轮中某个Bit是0(通过strlen侧信道),我们就可以推导出这一轮的轮密钥。这是一件非常危险的事情。

回归主线。取得system_features_t对象的大小或者说偏移,将这个对象中tRTS无法识别的域清零。并且tRTS内部使用全局变量对CPU特性进行保存记录,之前system_features_t对象是传参进来的,也就是ms参数,全局变量记录情况如下。

tRTS全局变量 赋值

g_cpu_core_num

system_features_t.cpu_core_num

.data.rel.ro节

g_sdk_version

system_features_t.version .data.rel.ro节

EDMM_supported

SDK v1.5不支持EDMM,SDK v2.0+支持EDMM .data.rel.ro节

初始化堆(heap_init)

获取X特性状态(调用get_xfeature_state)。EREPORT(ENCLU.0x0)获取当前Enclave自身的报告,报告中包含了XFRM值。

typedef struct _report_t                    /* 432 bytes */
{sgx_report_body_t       body;sgx_key_id_t            key_id;         /* (384) KeyID used for diversifying the key tree */sgx_mac_t               mac;            /* (416) The Message Authentication Code over this structure. */
} sgx_report_t;

XFRM这就反映了X特性状态。如果是Legacy的X特性(Legacy默认包含一些基本特性),tRTS内的全局变量g_xsave_enabled就标记为0,不然标记为1。之前讲到过uRTS的g_xsave_enabled,这个同样是反映CPU额外支持的扩展特性情况,不过这个是保存在uRTS的全局变量。这其实体现了一个点:很多东西都是SGX内外各一套(最常见的就是寄存器值内外各一套)。

/* XSAVE Feature Request Mask */
#define SGX_XFRM_LEGACY          0x0000000000000003ULL     /* Legacy XFRM which includes the basic feature bits required by SGX, x87 state(0x01) and SSE state(0x02) */
#define SGX_XFRM_AVX             0x0000000000000006ULL     /* AVX XFRM which includes AVX state(0x04) and SSE state(0x02) required by AVX */
#define SGX_XFRM_AVX512          0x00000000000000E6ULL     /* AVX-512 XFRM - not supported */
#define SGX_XFRM_MPX             0x0000000000000018ULL     /* MPX XFRM - not supported */

初始化并优选字符串库和密码学库

if (0 != init_optimized_libs(cpu_features, (uint32_t*)sys_features.cpuinfo_table, xfrm))

根据CPU配置情况,初始化被优选的字符串库【常规String、Intel Fast String二选一】、密码学库【默认使用Intel® Integrated Performance Primitives (Intel® IPP) Cryptography 2018 Update 2.1,可选Intel® Software Guard Extensions SSL cryptographic library (Intel® SGX SSL)】。做法如下:

首先获取CPU特性标记位,如下

CPU特性标记位 赋值
升级版uRTS采集的CPU特性标记位,SYS_FEATURE_EXTEND system_features_t.cpu_features,并清除它的冲突位【INCOMPAT_FEATURE_BIT】
旧版的uRTS采集的CPU特性标记位 system_features_t.cpu_features_ext

然后在这个CPU特性标记位的基础上,将保留位清0,检查掩码一致性,如果OS&ENCLAVE不支持对AVX进行SAVE / RESTORE,那么清除与AVX及其更高版本的标记位,最终保存到全局的g_cpu_feature_indicator。

tRTS全局变量 赋值

g_cpu_core_num

system_features_t.cpu_core_num

.data.rel.ro节

g_sdk_version

system_features_t.version .data.rel.ro节

EDMM_supported

SDK v1.5不支持EDMM,SDK v2.0+支持EDMM .data.rel.ro节

g_cpu_feature_indicator

【更新】

从uRTS传到tRTS的CPU特性标记位的基础上检查调整,用于此全局变量赋值 .data.rel.ro节

按照g_cpu_feature_indicator标记的情况,初始化并选择字符串库、密码学库。

关于初始化优选的字符串库(调用sgx_init_string_lib)

_TLIBC_USE_INTEL_FAST_STRING_宏开关开启与否

操作
开启

会更新__intel_cpu_feature_indicator、__intel_cpu_feature_indicator_x和__intel_cpu_indicator这三个变量的配置。【后续才会具体初始化Intel Fast String库】

不开启

普通String库,也就是啥也不变更。

关于初始化密码学库(调用sgx_init_crypto_lib)

密码库选择 操作
Intel IPP 初始化IPP库。
Intel SGX SSL 用system_features_t.cpuinfo_table
初始化异常处理句柄。为了以防EPID-SDK使用IPP,这里也对IPP进行初始化。
/* IPP library Initialization
* Parameters:
*   Return: sgx_status_t  - SGX_SUCCESS or failure as defined sgx_error.h
*   Inputs: uint64_t cpu_feature_indicator - Bit array of host CPU feature bits */
extern "C" sgx_status_t init_ipp_cpuid(uint64_t cpu_feature_indicator)

init_ipp_cpuid中,我们首先根据前面的CPU特性指示器,需要保证CPU已经支持SSE4.1,这是一个baseline,同时如果支持SSE4.1,也就意味着除了支持SSE4.1,早些的MMX、SSE、SSE2、SSE3、SSSE3均以支持了。额外的,根据CPU特性指示器,查看CPU是否支持如下,

CPU扩展特性
MOVBE
SSE4.2
AVX
AES(AES_NI)
PCLMULQDQ
RDRND
F16C
AVX2
ADX
RDSEED
SHA
AVX512F
AVX512PF
AVX512ER
AVX512CD
AVX512DQ
AVX512BW
AVX512VL
AVX512VBMI
AVX512_4VNNIW
AVX512_4FMAPS
AVX512IFMA52

并用于指导后续IPP初始化。目前似乎只实施了针对AVX2、SSE4.1的优化算法。

带着制定好的CPU特性情况,调用ippcpSetCpuFeatures,这是一个IPP的API。

“保留内存”的初始化

if(rsrv_mem_init(get_rsrv_base(), get_rsrv_size(), get_rsrv_min_size()) != SGX_SUCCESS)

对rsrv保留内存的关键信息做了下记录。

通过g_global_data和__ImageBase确定如下rsrv相关信息,并将这些信息保存到全局变量rsrv_mem_base、rsrv_mem_size、rsrv_mem_min_size(参考sdk/tmm_rsrv/sgx_rsrv_mem_init.cpp文件)。

rsrv相关信息 说明 所在Section

rsrv_mem_base

rsrv(reserve)的基址,__ImageBase + g_global_data.rsrv_offset

.data.rel.ro

rsrv_mem_size

rsrv的大小(如果支持EDMM,根据g_global_data记录的布局信息LAYOUT_ID_RSRV_MAX,会对rsrv在原本的大小上额外进行扩大) .data.rel.ro

rsrv_mem_min_size

rsrv的最小大小,根据g_global_data记录的布局信息LAYOUT_ID_RSRV_MIN设置。 .data.rel.ro

构建__stack_chk_guard

调用RDRAND硬件指令【0x0F, 0xC7, 0xF0】读取随机数到__stack_chk_guard。它用处就是作为一个canary,防止栈溢出。后面会用来保护TCS对应的线程栈。

extern "C" {
uintptr_t __stack_chk_guard __attribute__((section(RELRO_SECTION_NAME)))= 0;
#define __weak_alias(alias,sym)                 \__asm__(".weak " __STRING(alias) " ; "      \__STRING(alias) " = " __STRING(sym))
__weak_alias(__intel_security_cookie, __stack_chk_guard);
}

回到do_init_enclave,开始初始化当前线程相关内容

if (SGX_SUCCESS != do_init_thread(tcs, true))
sgx_status_t do_init_thread(void *tcs, bool enclave_init)

初始化当前tcs_t对象对应的线程栈、TLS等的地址空间。

初始化之前这些地址空间都是相对tcs_t对象的偏移,初始化中需要加上tcs_t对象地址这个基址。将__stack_chk_guard存到tcs_t对象对应的canary【调用TCS2CANARY(tcs)获取canary地址】,用于保护线程栈。

如果支持EDMM并且调用do_init_thread的目的,最终是为了初始化Enclave,那么当前tcs_t对象代表线程是一个通用线程【SGX_UTILITY_THREAD】。

是否是第一次初始化当前线程的TCS 其他子情况 线程数据中的栈提交地址赋值【thread_data_t.stack_commit_addr】

支持EDMM,并且此次是Enclave初始化任务或者该tcs是一个动态的tcs

调整线程栈提交地址,加上动态栈最大空间(对于栈来说也就是回退这么大空间预留出来。)
不满足上述条件 thread_data_t.stack_limit_addr,不做更改
之前的保存的栈提交地址saved_stack_commit_addr

从程序头查到TLS段的起址和文件大小(以文件形式存储时的静态大小),把TLS段内容拷贝到当前线程的TLS中。

回到do_init_enclave,将堆区和保留内存区清零及裁剪接受

如果不支持EDMM,将堆区和保留内存区清零。如果支持EDMM,那么将之前申请裁剪的敏感的、动态的页面进行裁剪“接受”【调用EACCEPT硬件指令】,并对已分配的(也是初始为最小的)堆区、保留内存区清零。

Enclave基本的初始化完成

将Enclave状态(g_enclave_state)更新为ENCLAVE_INIT_DONE,代表Enclave基本的初始化。

从最开始uRTS完成了Enclave文件映射加载到ELRANGE,到现在也就是这一篇文章主要讲tRTS内部对Enclave进行布局调整、布局裁剪“接受”等操作。

并且到此tRTS内部对Enclave的基本的初始化已经完成了。紧接着就退出到uRTS,回到【__create_enclave】函数。在uRTS这边,【CTrustThreadPool::reset】会把刚才用来在tRTS内做初始化工作的TCS_CTT给放回到空闲列表【CTrustThreadPool::m_free_thread_vector】。

后续

由于进入tRTS完成Enclave初始化部分工作就占据了很大篇幅,因此Switchless初始化等操作请见《再回顾SGX初始化(四)》

再回顾SGX初始化(三)——uRTS维护Enclave、tRTS完成Enclave构建收尾确认工作相关推荐

  1. SGX初始化中ELF文件解析

    先记 ElfParser::run_parser()函数是SGX初始化<再回顾sgx_create_enclave>慢慢长征路的中间一环.比较独立又有些复杂,单独抽出来讲. ELF文件布局 ...

  2. c语言常数-ox6a是什么意思,那年声明理解不了定义与初始化(三)

    那年声明理解不了定义与初始化 穷则独善其身,达则兼善天下 -- <孟子> 编程之外 追逐简单美 编程之内 回顾微机原理-浮点数 神秘角色-机器码 神秘角色-机器码基础 神秘角色-反汇编 当 ...

  3. 30天敏捷结果(6):周五回顾,找到三件做的好以及三件需要改善的事情

    "I've found that small wins, small projects, small differences often make huge differences.&quo ...

  4. 三插头内部结构图_三方面维护硬度计才能使寿命更长久

    硬度计是光机电一体化的高新技术产品.与其他精密仪器一样,定期的保养维护少不了.轻则导致操作不顺畅,或试验结果有偏差;重则机器损坏,返厂维修耽误时间又费钱.如何才能使用寿命才能更长?现在为大家介绍一下在 ...

  5. #十二、编写三角形类Triangle,初始化三个属性,分别是三条边的长度,定义一个计算并打印周长的函数 #十三、编写等腰三角形类EWtriangle,继承于三角形类,初始化只用传一个腰长和一个底长,定

    #十二.编写三角形类Triangle,初始化三个属性,分别是三条边的长度,定义一个计算并打印周长的函数 #十三.编写等腰三角形类EWtriangle,继承于三角形类,初始化只用传一个腰长和一个底长,定 ...

  6. 最近准备把安卓和java的知识再回顾一遍,顺便会写博客上!千变万化还都是源于基础,打扎实基础...

    最近准备把安卓和java的知识再回顾一遍,顺便会写博客上!千变万化还都是源于基础,打扎实基础,加油吧 距离去北京还有23天 转载于:https://www.cnblogs.com/AceIsSunsh ...

  7. 先为成功的人工作,再与成功的人合作,最后是让成功的人为你工作

    曾经有人采访比尔盖次成功的秘决.比尔盖次说:因为又有更多的成功人士在为我工作. 陈安之的超级成功学也有提到:先为成功的人工作,再与成功的人合作,最后是让成功的人为你工作. 成功的人很多,但在我生活中我 ...

  8. R语言使用table1包绘制(生成)三线表、使用单变量分列构建三线表、编写自定义函数在三线表中添加p值

    R语言使用table1包绘制(生成)三线表.使用单变量分列构建三线表.编写自定义函数在三线表中添加p值 目录

  9. R语言使用table1包绘制(生成)三线表、使用单变量分列构建三线表、设置transpose参数转置三线表、变量作为列,子组(strata)作为行

    R语言使用table1包绘制(生成)三线表.使用单变量分列构建三线表.设置transpose参数转置三线表.变量作为列,子组(strata)作为行 目录

  10. R语言使用table1包绘制(生成)三线表、使用单变量分列构建三线表、自定义overall的标签名称

    R语言使用table1包绘制(生成)三线表.使用单变量分列构建三线表.自定义overall的标签名称 目录

最新文章

  1. 火狐浏览器设置cookie失败_IE、谷歌Cookie记录失败,火狐成功(IE和Firefox下的Cookie兼容问题)...
  2. SparkStreaming和Storm的区别
  3. NoSQL Databases - MongoDB
  4. java的虚拟机不支持在鲲鹏上_屌炸天,Oracle 发布了一个全栈虚拟机 GraalVM,支持 Python!...
  5. java操作文件爱女_Java的IO操作---File类
  6. jwt, json web token
  7. 这月跳槽的多吗?月薪多少才正常
  8. python画旋转圆_Python使用PyQt界面库绘制不停旋转的圆控件
  9. linux httpd 内存,apache占用内存过高耗完内存?
  10. python爬虫爬取页面源码在本页面展示
  11. linux下blast设计引物,Primer-BLAST:NCBI的引物设计和特异性检验工具
  12. Android 开机logo支持的格式
  13. 服装收银系统2022年排行榜新鲜出炉!
  14. python绘图苹果_如何使用python代码画一个苹果?
  15. HTML5网页设计的基本知识-几个概念
  16. 周志华揭开机器学习本质的57张PPT
  17. 懒人的法宝——办公自动化!
  18. java中五子棋_Java简单五子棋的实现
  19. 课程设计 齿轮油泵泵体的机械加工工艺规程及工艺夹具装备设计
  20. POJ 3107 Godfather (树的重心)

热门文章

  1. oeasy教您玩转vim - 4 - # 深入帮助
  2. 怎么做超链接html,HTML怎么做超链接
  3. sd卡与FAT32文件系统
  4. 计算机管理丢失computer文件,Win7弹框提示找不到Computer Management.lnk文件怎么办?...
  5. Python实现二维码扫码登录
  6. 进化树相关概念和类型介绍
  7. 两种有趣的排序方法:睡眠排序、猴子排序(golang版本)
  8. 基于Multisim的简易数字钟
  9. IT规划的两大困惑及未来之路
  10. 十进制进制法_二进制/八进制/十进制/十六进制 怎么学会?是怎么算的方式?...