Implement memory access bound check with hardware trap for 64-bit platforms (#293)

Also implement native stack overflow check with hardware trap for 64-bit platforms
Refine classic interpreter and fast interpreter to improve performance
Update document
This commit is contained in:
wenyongh
2020-06-28 15:41:25 +08:00
committed by GitHub
parent 548926ab1a
commit ee315e4049
33 changed files with 1143 additions and 438 deletions

View File

@ -398,7 +398,8 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
aot_func = func_ctxes[func_idx - import_func_count]->aot_func;
callee_cell_num = aot_func->param_cell_num + aot_func->local_cell_num + 1;
if (!check_stack_boundary(comp_ctx, func_ctx, callee_cell_num))
if (comp_ctx->enable_bound_check
&& !check_stack_boundary(comp_ctx, func_ctx, callee_cell_num))
goto fail;
/* Call the function */

View File

@ -75,18 +75,13 @@ check_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
uint32 offset, uint32 bytes)
{
LLVMValueRef offset_const = I32_CONST(offset);
LLVMValueRef bytes_const = I32_CONST(bytes);
LLVMValueRef bytes64_const = I64_CONST(bytes);
LLVMValueRef heap_base_offset = func_ctx->heap_base_offset;
LLVMValueRef addr, maddr, offset1, offset2, cmp;
LLVMValueRef mem_base_addr, mem_check_bound, total_mem_size;
LLVMValueRef addr, maddr, offset1, cmp, cmp1, cmp2;
LLVMValueRef mem_base_addr, mem_check_bound;
LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder);
LLVMBasicBlockRef check_succ, check_mem_space;
LLVMBasicBlockRef check_succ;
AOTValue *aot_value;
CHECK_LLVM_CONST(offset_const);
CHECK_LLVM_CONST(bytes_const);
CHECK_LLVM_CONST(bytes64_const);
/* Get memory base address and memory data size */
if (func_ctx->mem_space_unchanged) {
@ -104,21 +99,20 @@ check_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
aot_value = func_ctx->block_stack.block_list_end->value_stack.value_list_end;
POP_I32(addr);
/* offset1 = offset + addr; */
BUILD_OP(Add, offset_const, addr, offset1, "offset1");
/* return addres directly if constant offset and inside memory space */
if (LLVMIsConstant(offset1)) {
uint32 mem_offset = (uint32)LLVMConstIntGetZExtValue(offset1);
if (LLVMIsConstant(addr)) {
int64 mem_offset = (int64)LLVMConstIntGetSExtValue(addr) + (int64)offset;
uint32 num_bytes_per_page = comp_ctx->comp_data->num_bytes_per_page;
uint32 init_page_count = comp_ctx->comp_data->mem_init_page_count;
uint32 mem_data_size = num_bytes_per_page * init_page_count;
int64 mem_data_size = num_bytes_per_page * init_page_count;
if (mem_data_size > 0
&& mem_offset >= 0
&& mem_offset <= mem_data_size - bytes) {
/* inside memory space */
/* maddr = mem_base_addr + moffset */
if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder,
mem_base_addr,
offset1 = I32_CONST((uint32)mem_offset);
CHECK_LLVM_CONST(offset_const);
if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder, mem_base_addr,
&offset1, 1, "maddr"))) {
aot_set_last_error("llvm build add failed.");
goto fail;
@ -127,51 +121,35 @@ check_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
}
}
if (comp_ctx->comp_data->mem_init_page_count == 0) {
/* Get total memory size */
if (func_ctx->mem_space_unchanged) {
total_mem_size = func_ctx->total_mem_size;
}
else {
if (!(total_mem_size = LLVMBuildLoad(comp_ctx->builder,
func_ctx->total_mem_size,
"total_mem_size"))) {
aot_set_last_error("llvm build load failed.");
if (!(offset_const = LLVMBuildZExt(comp_ctx->builder, offset_const,
I64_TYPE, "offset_i64"))
|| !(addr = LLVMBuildSExt(comp_ctx->builder, addr,
I64_TYPE, "addr_i64"))) {
aot_set_last_error("llvm build extend i32 to i64 failed.");
goto fail;
}
}
ADD_BASIC_BLOCK(check_mem_space, "check_mem_space");
LLVMMoveBasicBlockAfter(check_mem_space, block_curr);
/* if total_mem_size is zero, boundary check fail */
BUILD_ICMP(LLVMIntEQ, total_mem_size, I32_ZERO, cmp,
"cmp_total_mem_size");
if (!aot_emit_exception(comp_ctx, func_ctx,
EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS,
true, cmp, check_mem_space)) {
goto fail;
}
SET_BUILD_POS(check_mem_space);
}
if (!(aot_value->is_local
&& aot_checked_addr_list_find(func_ctx, aot_value->local_idx,
offset, bytes))) {
/* offset2 = offset1 - heap_base_offset; */
BUILD_OP(Sub, offset1, heap_base_offset, offset2, "offset2");
/* offset1 = offset + addr; */
BUILD_OP(Add, offset_const, addr, offset1, "offset1");
if (comp_ctx->enable_bound_check
&& !(aot_value->is_local
&& aot_checked_addr_list_find(func_ctx, aot_value->local_idx,
offset, bytes))) {
if (!(mem_check_bound =
get_memory_check_bound(comp_ctx, func_ctx, bytes))) {
goto fail;
}
BUILD_ICMP(LLVMIntSGT, func_ctx->mem_bound_check_heap_base, offset1,
cmp1, "cmp1");
BUILD_ICMP(LLVMIntSGT, offset1, mem_check_bound, cmp2, "cmp2");
BUILD_OP(Or, cmp1, cmp2, cmp, "cmp");
/* Add basic blocks */
ADD_BASIC_BLOCK(check_succ, "check_succ");
LLVMMoveBasicBlockAfter(check_succ, block_curr);
/* offset2 > bound ? */
BUILD_ICMP(LLVMIntUGT, offset2, mem_check_bound, cmp, "cmp");
if (!aot_emit_exception(comp_ctx, func_ctx,
EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS,
true, cmp, check_succ)) {

View File

@ -182,29 +182,6 @@ create_memory_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
}
}
/* Load total memory size */
offset = I32_CONST(offsetof(AOTModuleInstance, total_mem_size));
if (!(func_ctx->total_mem_size =
LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->aot_inst,
&offset, 1, "bound_check_1byte_offset"))) {
aot_set_last_error("llvm build in bounds gep failed");
return false;
}
if (!(func_ctx->total_mem_size =
LLVMBuildBitCast(comp_ctx->builder, func_ctx->total_mem_size,
INT32_PTR_TYPE, "bound_check_1byte_ptr"))) {
aot_set_last_error("llvm build bit cast failed");
return false;
}
if (mem_space_unchanged) {
if (!(func_ctx->total_mem_size =
LLVMBuildLoad(comp_ctx->builder, func_ctx->total_mem_size,
"bound_check_1byte"))) {
aot_set_last_error("llvm build load failed");
return false;
}
}
/* Load memory bound check constants */
offset = I32_CONST(offsetof(AOTModuleInstance, mem_bound_check_1byte));
if (!(func_ctx->mem_bound_check_1byte =
@ -215,7 +192,7 @@ create_memory_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
}
if (!(func_ctx->mem_bound_check_1byte =
LLVMBuildBitCast(comp_ctx->builder, func_ctx->mem_bound_check_1byte,
INT32_PTR_TYPE, "bound_check_1byte_ptr"))) {
INT64_PTR_TYPE, "bound_check_1byte_ptr"))) {
aot_set_last_error("llvm build bit cast failed");
return false;
}
@ -237,7 +214,7 @@ create_memory_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
}
if (!(func_ctx->mem_bound_check_2bytes =
LLVMBuildBitCast(comp_ctx->builder, func_ctx->mem_bound_check_2bytes,
INT32_PTR_TYPE, "bound_check_2bytes_ptr"))) {
INT64_PTR_TYPE, "bound_check_2bytes_ptr"))) {
aot_set_last_error("llvm build bit cast failed");
return false;
}
@ -259,7 +236,7 @@ create_memory_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
}
if (!(func_ctx->mem_bound_check_4bytes =
LLVMBuildBitCast(comp_ctx->builder, func_ctx->mem_bound_check_4bytes,
INT32_PTR_TYPE, "bound_check_4bytes_ptr"))) {
INT64_PTR_TYPE, "bound_check_4bytes_ptr"))) {
aot_set_last_error("llvm build bit cast failed");
return false;
}
@ -281,7 +258,7 @@ create_memory_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
}
if (!(func_ctx->mem_bound_check_8bytes =
LLVMBuildBitCast(comp_ctx->builder, func_ctx->mem_bound_check_8bytes,
INT32_PTR_TYPE, "bound_check_8bytes_ptr"))) {
INT64_PTR_TYPE, "bound_check_8bytes_ptr"))) {
aot_set_last_error("llvm build bit cast failed");
return false;
}
@ -294,23 +271,23 @@ create_memory_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
}
}
/* Load heap base offset */
offset = I32_CONST(offsetof(AOTModuleInstance, heap_base_offset));
if (!(func_ctx->heap_base_offset =
/* Load bound_check_heap_base */
offset = I32_CONST(offsetof(AOTModuleInstance, mem_bound_check_heap_base));
if (!(func_ctx->mem_bound_check_heap_base =
LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->aot_inst,
&offset, 1, "heap_base_offset_offset"))) {
&offset, 1, "bound_check_heap_base_offset"))) {
aot_set_last_error("llvm build in bounds gep failed");
return false;
}
if (!(func_ctx->heap_base_offset =
LLVMBuildBitCast(comp_ctx->builder, func_ctx->heap_base_offset,
INT32_PTR_TYPE, "heap_base_offset_tmp"))) {
if (!(func_ctx->mem_bound_check_heap_base =
LLVMBuildBitCast(comp_ctx->builder, func_ctx->mem_bound_check_heap_base,
INT64_PTR_TYPE, "bound_check_heap_base_tmp"))) {
aot_set_last_error("llvm build bit cast failed");
return false;
}
if (!(func_ctx->heap_base_offset =
LLVMBuildLoad(comp_ctx->builder, func_ctx->heap_base_offset,
"heap_base_offset"))) {
if (!(func_ctx->mem_bound_check_heap_base =
LLVMBuildLoad(comp_ctx->builder, func_ctx->mem_bound_check_heap_base,
"bound_check_heap_base"))) {
aot_set_last_error("llvm build load failed");
return false;
}
@ -936,6 +913,11 @@ aot_create_comp_context(AOTCompData *comp_data,
comp_ctx->is_jit_mode = true;
comp_ctx->target_machine =
LLVMGetExecutionEngineTargetMachine(comp_ctx->exec_engine);
#ifndef OS_ENABLE_HW_BOUND_CHECK
comp_ctx->enable_bound_check = true;
#else
comp_ctx->enable_bound_check = false;
#endif
}
else {
/* Create LLVM target machine */
@ -1049,6 +1031,21 @@ aot_create_comp_context(AOTCompData *comp_data,
get_target_arch_from_triple(triple_norm, comp_ctx->target_arch,
sizeof(comp_ctx->target_arch));
if (option->bounds_checks == 1 || option->bounds_checks == 0) {
/* Set by user */
comp_ctx->enable_bound_check =
(option->bounds_checks == 1) ? true : false;
}
else {
/* Unset by user, use default value */
if (strstr(comp_ctx->target_arch, "64") && !option->is_sgx_platform) {
comp_ctx->enable_bound_check = false;
}
else {
comp_ctx->enable_bound_check = true;
}
}
os_printf("Create AoT compiler with:\n");
os_printf(" target: %s\n", comp_ctx->target_arch);
os_printf(" target cpu: %s\n", cpu);
@ -1114,14 +1111,11 @@ aot_create_comp_context(AOTCompData *comp_data,
goto fail;
}
LLVMAddBasicAliasAnalysisPass(comp_ctx->pass_mgr);
LLVMAddPromoteMemoryToRegisterPass(comp_ctx->pass_mgr);
LLVMAddInstructionCombiningPass(comp_ctx->pass_mgr);
LLVMAddCFGSimplificationPass(comp_ctx->pass_mgr);
LLVMAddJumpThreadingPass(comp_ctx->pass_mgr);
LLVMAddConstantPropagationPass(comp_ctx->pass_mgr);
LLVMAddReassociatePass(comp_ctx->pass_mgr);
LLVMAddGVNPass(comp_ctx->pass_mgr);
LLVMAddCFGSimplificationPass(comp_ctx->pass_mgr);
/* Create metadata for llvm float experimental constrained intrinsics */
if (!(comp_ctx->fp_rounding_mode =

View File

@ -105,9 +105,8 @@ typedef struct AOTFuncContext {
LLVMValueRef native_stack_bound;
LLVMValueRef last_alloca;
LLVMValueRef heap_base_offset;
LLVMValueRef mem_base_addr;
LLVMValueRef total_mem_size;
LLVMValueRef mem_bound_check_heap_base;
LLVMValueRef mem_bound_check_1byte;
LLVMValueRef mem_bound_check_2bytes;
LLVMValueRef mem_bound_check_4bytes;
@ -188,6 +187,9 @@ typedef struct AOTCompContext {
/* Bulk memory feature */
bool enable_bulk_memory;
/* Bounday Check */
bool enable_bound_check;
/* Whether optimize the JITed code */
bool optimize;
@ -227,9 +229,11 @@ typedef struct AOTCompOption{
char *target_cpu;
char *cpu_features;
bool enable_bulk_memory;
bool is_sgx_platform;
uint32 opt_level;
uint32 size_level;
uint32 output_format;
uint32 bounds_checks;
} AOTCompOption, *aot_comp_option_t;
AOTCompContext *