初始化enclave

1
init_enclave() -> SgxResult<SgxEnclave>

底层调用rsgx_create_enclave【通过file_name和launch_token来初始化一个enclave实例】,参数如下:

  • file_name:经过编译后的enclave签名文件(enclave.signed.so

  • debug:

    • 0 - 不开启debug
    • 1 - 开启debug,enclave中的数据代码可以被enclave外的程序访问,用于调试
  • launch_token:初始化enclave时可以传入全0 buffer,函数会创建一个有效token与enclave对应,并更新该参数。之后如果要重新创建这个enclave,可以再使用这个token

  • launch_token_updated:

    • 0 - token没有被自动更新
    • 1 - token被自动更新
  • misc_attr:misc选择和enclave属性 {暂时不清楚作用}

init_enclave()返回一个SgxEnclave结构

1
2
3
4
5
pub struct SgxEnclave {
id: sgx_enclave_id_t,
debug: i32,
path: PathBuf,
}

初始化sgxwasm的SpecDriver

1
fn sgx_enclave_wasm_init(enclave: &SgxEnclave) -> Result<(),String>

调用ECall函数sgxwasm_init(),传入两个隐参数:

  • enclave_id:通过enclave.geteid()获取
  • retval:ECall函数的返回值,由Intel定义,不用在ECall函数中手动修改

在Enclave中会通过lazy_static的方式建立一个全局变量SPECDRIVER(带有互斥量)

1
2
3
4
lazy_static!{
static ref SPECDRIVER: SgxMutex<SpecDriver>
= SgxMutex::new(SpecDriver::new());
}

sgxwasm_init()在SPECDRIVER中新建一个SpecDriver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
pub struct SpecDriver {
spec_module: SpecModule,
instances: HashMap<String, ModuleRef>,
last_module: Option<ModuleRef>,
}

// 标准模块,是从wasm外部导入函数所使用的外部环境
pub struct SpecModule {
table: TableRef,
memory: MemoryRef,
global_i32: GlobalRef,
global_f32: GlobalRef,
global_f64: GlobalRef,
}

// ModuleRef是ModuleInstance的引用
// ModuleInstance是一个wasm模块实例
pub struct ModuleInstance {
signatures: RefCell<Vec<Arc<Signature>>>,
tables: RefCell<Vec<TableRef>>,
funcs: RefCell<Vec<FuncRef>>,
memories: RefCell<Vec<MemoryRef>>,
globals: RefCell<Vec<GlobalRef>>,
exports: RefCell<BTreeMap<String, ExternVal>>,
}

在Enclave外加载wast文件

1
wasm_main_loop(wast_file: &str, enclave: &SgxEnclave) -> Result<(), String>

首先对读入的wast_file文本进行分析(使用ScriptParser),读取每一行的指令到结构Command中:

1
2
3
4
5
6
pub struct Command<F32 = f32, F64 = f64> {
/// 行号
pub line: u64,
/// 指令类型
pub kind: CommandKind<F32, F64>,
}

对每一行指令匹配CommandKind,并执行对应的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
pub enum CommandKind<F32 = f32, F64 = f64> {
// 定义、验证、实例化一个module
Module {
// wasm二进制模块
module: ModuleBinary,
// 模块名称,可以将wasm模块注册到name
name: Option<String>,
},

// 确保特定的指令会产生特定的结果
AssertReturn {
// 执行的指令
action: Action<F32, F64>,
/// 预期的指令执行结果
expected: Vec<Value<F32, F64>>,
},

// 确保指定的指令产生NaN
AssertReturnCanonicalNan {
// 执行的指令
action: Action<F32, F64>,
},

// 确保指定的指令产生 NaN with 1 in MSB of fraction field
AssertReturnArithmeticNan {
// 执行的指令
action: Action<F32, F64>,
},

// 确保指定的指令产生trap
AssertTrap {
// 执行的指令
action: Action<F32, F64>,
// 错误信息
message: String,
},

// 确保特定的模块是无效的
AssertInvalid {
// 无效的模块
module: ModuleBinary,
// 错误信息
message: String,
},

// 确保特定的模块不能被decode
AssertMalformed {
module: ModuleBinary,
// 错误信息
message: String,
},

// 确保特定的模块不能被实例化
AssertUninstantiable {
// 不能被实例化的模块
module: ModuleBinary,
// 错误信息
message: String,
},

// 确保特定的指令在资源耗尽的时候执行
AssertExhaustion {
action: Action<F32, F64>,
// 错误信息
message: String,
},

// 确保特定的模块不能被链接
AssertUnlinkable {
module: ModuleBinary,
// 错误信息
message: String,
},

// 将已经注册过的模块改名
Register {
// 需要改名的模块名,若爲None则将使用最后定义的模块
name: Option<String>,
// 新名称
as_name: String,
},

// 执行指定指令
PerformAction(Action<F32, F64>),
}

指令Action

wasm模块中的指令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pub enum Action<F32 = f32, F64 = f64> {
// 调用函数
Invoke {
// 模块名称,若爲None,则取最后定义的模块
module: Option<String>,
// 函数名
field: String,
// 需要传递给函数的参数
args: Vec<Value<F32, F64>>,
},
// 读取全局变量
Get {
// 模块名称,若爲None,则取最后定义的模块
module: Option<String>,
// 指令作用在字段field上
field: String,
},
}

sgxwasm中定义的指令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
pub enum SgxWasmAction {
// 调用函数
Invoke {
// 模块名称,若爲None,则取最后定义的模块
module: Option<String>,
// 函数名
field: String,
// 需要传递给函数的参数
args: Vec<BoundaryValue>
},

// 读取全局变量
Get {
// 模块名称,若爲None,则取最后定义的模块
module: Option<String>,
// 指令作用在字段field上
field: String,
},

// 加载模块
LoadModule {
// 模块名称,若爲None,则取最后定义的模块
name: Option<String>,
// 二进制模块
module: Vec<u8>,
},

// 尝试加载一个模块
TryLoad {
// 二进制模块
module: Vec<u8>,
},

// 将已经注册过的模块改名
Register {
// 模块名称,若爲None,则取最后定义的模块
name: Option<String>,
// 新名称
as_name: String,
},
}

CommandKind::Module

1
2
3
4
5
// 加载一个模块
fn sgx_enclave_wasm_load_module(module : Vec<u8>,
name : &Option<String>,
enclave : &SgxEnclave)
-> Result<(), String>

创建一个SgxWasmAction::LoadModule结构,再调用sgx_enclave_wasm_invoke()

CommandKind::AssertReturn

1
2
// 根据wasm的Action执行指令
fn sgx_enclave_wasm_run_action(action: &Action, enclave: &SgxEnclave) -> Result<Option<RuntimeValue>, InterpreterError>

先匹配Action:

  • 如果是Action::Invoke,那么创建一个SgxWasmAction::Invoke结构并交给sgx_enclave_wasm_invoke()
  • 如果是Action::Get,那么创建一个SgxWasmAction::Get结构并交给sgx_enclave_wasm_invoke()

然后根据sgx_enclave_wasm_invoke()的返回结果来判断输出是否是我们想要的

**备注:**对于另外一些CommandKind(包括CommandKind::AssertReturnCanonicalNan、CommandKind::AssertReturnArithmeticNan、CommandKind::AssertExhaustion、CommandKind::AssertTrap),过程同CommandKind::AssertReturn,只不过是对返回值的判断有所不同

**备注2:**对于CommandKind::PerformAction,只需匹配和执行Action,不需要后面的判断

CommandKind::AssertInvalid

1
2
// 尝试加载一个模块
fn sgx_enclave_wasm_try_load(module : &[u8], enclave : &SgxEnclave) -> Result<(), InterpreterError>

先创建一个SgxWasmAction::TryLoad结构,再交给sgx_enclave_wasm_invoke()

**备注:**对于另外一些CommandKind(包括CommandKind::AssertMalformed、CommandKind::AssertUnlinkable、CommandKind::AssertUninstantiable),由于这些类型的SgxWasmAction都不能加载可用的模块,所以只能尝试加载模块

CommandKind::Register

1
2
3
4
5
// 注册一个模块
fn sgx_enclave_wasm_register(name : Option<String>,
as_name : String,
enclave : &SgxEnclave)
-> Result<(), InterpreterError>

创建一个SgxWasmAction::Register结构,再交给sgx_enclave_wasm_invoke()

调用函數sgx_enclave_wasm_invoke()

1
2
3
4
// 根据SgxWasmAction通过ECall执行指定的指令
fn sgx_enclave_wasm_invoke(req_str : String,
result_max_len : usize,
enclave : &SgxEnclave) -> (Result<Option<BoundaryValue>, InterpreterError>, sgx_status_t)
  • req_str:将SgxWasmAction序列化得到
  • result_max_len:返回结果的最大长度

sgx_enclave_wasm_invoke()调用ECall函数sgxwasm_run_action()

安全函数sgxwasm_run_action()

1
2
fn sgxwasm_run_action(req_bin : *const u8, req_length: usize,
result_bin : *mut u8, result_max_len: usize) -> sgx_status_t
  • req_bin:需要执行的SgxWasmAction请求
  • req_length:请求长度
  • result_bin:返回的内容
  • result_max_len:返回值的最大长度

首先通过req_bin和req_length来在Enclave中创建一个安全的SgxWasmAction结构action_req,再对其进行匹配:

SgxWasmAction::Invoke

1
2
3
// 调用函数
fn wasm_invoke(module: Option<String>, field: String, args: Vec<RuntimeValue>)
-> Result<Option<RuntimeValue>, InterpreterError>

首先获取全局的SPECDRIVER存入变量program,根据module的值在SpecDriver.instances中查找对应名字的模块,如果module的值爲None,则通过SpecDriver.last_module返回最后注册的模块,得到一个ModuleRef结构module通过module的invoke_export()方法调用导出函数:

1
module.invoke_export(&field, &args, program.spec_module())
  • &field:函数名
  • &args:传入函数的参数
  • program.spec_module():externals,导入wasm函数的外部环境,实现Externals trait

invoke_export()方法通过函数名查找导出函数,得到FuncRef类型的func_instance,并使用 FuncInstance::invoke(&func_instance, args, externals) 调用函数,FuncInstance结构是一个函数实例,在wasm中,函数的定义方式有两种:在wasm模块中定义或者从宿主环境导入,分别对应FuncInstanceInternal::Internal和FuncInstanceInternal::Host

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pub struct FuncInstance(FuncInstanceInternal);

#[derive(Clone)]
pub(crate) enum FuncInstanceInternal {
Internal {
signature: Arc<Signature>,
module: Weak<ModuleInstance>,
body: Arc<FuncBody>,
},
Host {
signature: Signature,
host_func_index: usize,
},
}

在FuncInstance::invoke()调用函数时,首先判断传入参数是否符合函数籤名,然后匹配函数的定义方式FuncInstanceInternal

  • Internal:初始化一个Interpreter,然后调用 interpreter.start_execution(externals) 执行函数(更底层的内容可能会写在下一章)

    1
    2
    3
    4
    5
    6
    pub struct Interpreter {
    value_stack: ValueStack,
    call_stack: CallStack,
    return_type: Option<ValueType>,
    state: InterpreterState,
    }
  • Host:调用 externals.invoke_index(*host_func_index, args.into()) ,externals即爲invoke_export()方法传入的第三参数:导入函数的外部环境。这里的externals就是SpecModule,它实现的Externals trait如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    impl Externals for SpecModule {
    fn invoke_index(
    &mut self,
    index: usize,
    args: RuntimeArgs,
    ) -> Result<Option<RuntimeValue>, Trap> {
    match index {
    PRINT_FUNC_INDEX => {
    println!("print: {:?}", args);
    Ok(None)
    }
    _ => panic!("SpecModule doesn't provide function at index {}", index),
    }
    }
    }

SgxWasmAction::Get

1
2
3
// 获取全局变量
fn wasm_get(module : Option<String>, field : String)
-> Result<Option<RuntimeValue>, InterpreterError>

首先获取全局的SPECDRIVER存入变量program,根据module的值在SpecDriver.instances中查找对应名字的模块,如果module的值爲None,则通过SpecDriver.last_module返回最后注册的模块,得到一个ModuleRef结构module

然后通过 module.export_by_name(&field) 获得一个wasm的运行时的实体引用ExternVal,可能的值包括:函数、Table、Memory、全局变量

1
2
3
4
5
6
pub enum ExternVal {
Func(FuncRef),
Table(TableRef),
Memory(MemoryRef),
Global(GlobalRef),
}

接着匹配module的该导出实体是否爲Global,若不是,返回None

SgxWasmAction::LoadModule

1
2
3
// 加载一个模块到SpecDriver中
fn wasm_load_module(name: Option<String>, module: Vec<u8>)
-> Result<(), InterpreterError>

首先获取全局的SPECDRIVER存入变量spec_driver,然后通过 try_load_module(&module[..]) 获得一个parity_wasm库的Module类型的module,接着通过 ModuleInstance::new(&module, &**spec_driver) 的方式新建一个NotStartedModuleRef实例,并使用 run_start(spec_driver.spec_module()) 方法将其转变爲ModuleRef类型

最后将该实例通过 spec_driver.add_module(name, instance.clone()) 存放入spec_driver中,将SpecDriver.last_module改爲该模块,并在SpecDriver.instances中插入该模块与其名字的键值对

SgxWasmAction::TryLoad

1
2
// 尝试加载一个模块,不加载到SpecDriver中
fn wasm_try_load(wasm: Vec<u8>) -> Result<(), InterpreterError>

首先获取全局的SPECDRIVER存入变量spec_driver,然后通过 try_load_module(&module[..]) 获得一个parity_wasm库的Module类型的module,接着通过 ModuleInstance::new(&module, &ImportsBuilder::default()) 的方式新建一个NotStartedModuleRef实例instance,尝试对其使用 run_start(spec_driver.spec_module())方法,若成功,返回Ok(()),若失败,返回错误

SgxWasmAction::Register

1
2
3
// 使用as_name注册一个新模块
fn wasm_register(name: &Option<String>, as_name: String)
-> Result<(), InterpreterError>

首先获取全局的SPECDRIVER存入变量spec_driver,根据module的值在SpecDriver.instances中查找对应名字name的模块,如果name的值爲None,则通过SpecDriver.last_module返回最后注册的模块

最后将该模块通过 spec_driver.add_module(name, instance.clone()) 存放入spec_driver中,将SpecDriver.last_module改爲该模块,并在SpecDriver.instances中插入该模块与其名字的键值对

函数执行Interpreter底层