corda/sdk/trts/trts_veh.cpp
zhaohuidu 85947caa12 Upgrade to Linux 1.6 gold release
switch code to linux 1.6 opensource gold release
2016-09-19 14:55:22 +08:00

409 lines
12 KiB
C++

/*
* Copyright (C) 2011-2016 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* File: trts_veh.cpp
* Description:
* This file implements the support of custom exception handling.
*/
#include "sgx_trts_exception.h"
#include <stdlib.h>
#include "sgx_trts.h"
#include "xsave.h"
#include "arch.h"
#include "sgx_spinlock.h"
#include "thread_data.h"
#include "global_data.h"
#include "trts_internal.h"
typedef struct _handler_node_t
{
uintptr_t callback;
struct _handler_node_t *next;
} handler_node_t;
static handler_node_t *g_first_node = NULL;
static sgx_spinlock_t g_handler_lock = SGX_SPINLOCK_INITIALIZER;
static uintptr_t g_veh_cookie = 0;
#define ENC_VEH_POINTER(x) (uintptr_t)(x) ^ g_veh_cookie
#define DEC_VEH_POINTER(x) (sgx_exception_handler_t)((x) ^ g_veh_cookie)
static bool is_stack_addr(void *address, size_t size)
{
thread_data_t *thread_data = get_thread_data();
size_t stack_base = thread_data->stack_base_addr;
size_t stack_limit = thread_data->stack_limit_addr;
size_t addr = (size_t) address;
return (addr <= (addr + size)) && (stack_base >= (addr + size)) && (stack_limit <= addr);
}
static bool is_valid_sp(uintptr_t sp)
{
return ( !(sp & (sizeof(uintptr_t) - 1)) // sp is expected to be 4/8 bytes aligned
&& is_stack_addr((void*)sp, 0) ); // sp points to the top/bottom of stack are accepted
}
// sgx_register_exception_handler()
// register a custom exception handler
// Parameter
// is_first_handler - the order in which the handler should be called.
// if the parameter is nonzero, the handler is the first handler to be called.
// if the parameter is zero, the handler is the last handler to be called.
// exception_handler - a pointer to the handler to be called.
// Return Value
// handler - success
// NULL - fail
void *sgx_register_exception_handler(int is_first_handler, sgx_exception_handler_t exception_handler)
{
// initialize g_veh_cookie for the first time sgx_register_exception_handler is called.
if(unlikely(g_veh_cookie == 0))
{
uintptr_t rand = 0;
do
{
if(SGX_SUCCESS != sgx_read_rand((unsigned char *)&rand, sizeof(rand)))
{
return NULL;
}
} while(rand == 0);
sgx_spin_lock(&g_handler_lock);
if(g_veh_cookie == 0)
{
g_veh_cookie = rand;
}
sgx_spin_unlock(&g_handler_lock);
}
if(!sgx_is_within_enclave((const void*)exception_handler, 0))
{
return NULL;
}
handler_node_t *node = (handler_node_t *)malloc(sizeof(handler_node_t));
if(!node)
{
return NULL;
}
node->callback = ENC_VEH_POINTER(exception_handler);
// write lock
sgx_spin_lock(&g_handler_lock);
if((g_first_node == NULL) || is_first_handler)
{
node->next = g_first_node;
g_first_node = node;
}
else
{
handler_node_t *tmp = g_first_node;
while(tmp->next != NULL)
{
tmp = tmp->next;
}
node->next = NULL;
tmp->next = node;
}
// write unlock
sgx_spin_unlock(&g_handler_lock);
return node;
}
// sgx_unregister_exception_handler()
// unregister a custom exception handler.
// Parameter
// handler - a handler to the custom exception handler previously
// registered using the sgx_register_exception_handler function.
// Return Value
// none zero - success
// 0 - fail
int sgx_unregister_exception_handler(void *handler)
{
if(!handler)
{
return 0;
}
int status = 0;
// write lock
sgx_spin_lock(&g_handler_lock);
if(g_first_node)
{
handler_node_t *node = g_first_node;
if(node == handler)
{
g_first_node = node->next;
status = 1;
}
else
{
while(node->next != NULL)
{
if(node->next == handler)
{
node->next = node->next->next;
status = 1;
break;
}
node = node->next;
}
}
}
// write unlock
sgx_spin_unlock(&g_handler_lock);
if(status) free(handler);
return status;
}
// continue_execution(sgx_exception_info_t *info):
// try to restore the thread context saved in info to current execution context.
extern "C" __attribute__((regparm(1))) void continue_execution(sgx_exception_info_t *info);
// internal_handle_exception(sgx_exception_info_t *info):
// the 2nd phrase exception handing, which traverse registered exception handlers.
// if the exception can be handled, then continue execution
// otherwise, throw abortion, go back to 1st phrase, and call the default handler.
extern "C" __attribute__((regparm(1))) void internal_handle_exception(sgx_exception_info_t *info)
{
int status = EXCEPTION_CONTINUE_SEARCH;
handler_node_t *node = NULL;
thread_data_t *thread_data = get_thread_data();
size_t size = 0;
uintptr_t *nhead = NULL;
uintptr_t *ntmp = NULL;
uintptr_t xsp = 0;
if (thread_data->exception_flag < 0)
goto failed_end;
thread_data->exception_flag++;
// read lock
sgx_spin_lock(&g_handler_lock);
node = g_first_node;
while(node != NULL)
{
size += sizeof(uintptr_t);
node = node->next;
}
// There's no exception handler registered
if (size == 0)
{
sgx_spin_unlock(&g_handler_lock);
//exception cannot be handled
thread_data->exception_flag = -1;
//instruction triggering the exception will be executed again.
continue_execution(info);
}
if ((nhead = (uintptr_t *)malloc(size)) == NULL)
{
sgx_spin_unlock(&g_handler_lock);
goto failed_end;
}
ntmp = nhead;
node = g_first_node;
while(node != NULL)
{
*ntmp = node->callback;
ntmp++;
node = node->next;
}
// read unlock
sgx_spin_unlock(&g_handler_lock);
// call exception handler until EXCEPTION_CONTINUE_EXECUTION is returned
ntmp = nhead;
while(size > 0)
{
sgx_exception_handler_t handler = DEC_VEH_POINTER(*ntmp);
status = handler(info);
if(EXCEPTION_CONTINUE_EXECUTION == status)
{
break;
}
ntmp++;
size -= sizeof(sgx_exception_handler_t);
}
free(nhead);
// call default handler
// ignore invalid return value, treat to EXCEPTION_CONTINUE_SEARCH
// check SP to be written on SSA is pointing to the trusted stack
xsp = info->cpu_context.REG(sp);
if (!is_valid_sp(xsp))
{
goto failed_end;
}
if(EXCEPTION_CONTINUE_EXECUTION == status)
{
//exception is handled, decrease the nested exception count
thread_data->exception_flag--;
}
else
{
//exception cannot be handled
thread_data->exception_flag = -1;
}
//instruction triggering the exception will be executed again.
continue_execution(info);
failed_end:
thread_data->exception_flag = -1; // mark the current exception cannot be handled
abort(); // throw abortion
}
// trts_handle_exception(void *tcs)
// the entry point for the exceptoin handling
// Parameter
// the pointer of TCS
// Return Value
// none zero - success
// 0 - fail
#include "trts_internal.h"
extern "C" sgx_status_t trts_handle_exception(void *tcs)
{
thread_data_t *thread_data = get_thread_data();
ssa_gpr_t *ssa_gpr = NULL;
sgx_exception_info_t *info = NULL;
uintptr_t sp, *new_sp = NULL;
size_t size = 0;
if (tcs == NULL) goto default_handler;
if(get_enclave_state() != ENCLAVE_INIT_DONE)
{
goto default_handler;
}
// check if the exception is raised from 2nd phrase
if(thread_data->exception_flag == -1) {
goto default_handler;
}
if ((TD2TCS(thread_data) != tcs)
|| (((thread_data->first_ssa_gpr)&(~0xfff)) - SE_PAGE_SIZE) != (uintptr_t)tcs) {
goto default_handler;
}
// no need to check the result of ssa_gpr because thread_data is always trusted
ssa_gpr = reinterpret_cast<ssa_gpr_t *>(thread_data->first_ssa_gpr);
sp = ssa_gpr->REG(sp);
if(!is_stack_addr((void*)sp, 0)) // check stack overrun only, alignment will be checked after exception handled
{
g_enclave_state = ENCLAVE_CRASHED;
return SGX_ERROR_STACK_OVERRUN;
}
size = 0;
#ifdef SE_GNU64
size += 128; // x86_64 requires a 128-bytes red zone, which begins directly
// after the return addr and includes func's arguments
#endif
// decrease the stack to give space for info
size += sizeof(sgx_exception_info_t);
sp -= size;
sp = sp & ~0xF;
// check the decreased sp to make sure it is in the trusted stack range
if(!is_stack_addr((void *)sp, size))
{
g_enclave_state = ENCLAVE_CRASHED;
return SGX_ERROR_STACK_OVERRUN;
}
if(ssa_gpr->exit_info.valid != 1)
{ // exception handlers are not allowed to call in a non-exception state
goto default_handler;
}
info = (sgx_exception_info_t *)sp;
// No need to check the stack as it have already been checked by assembly code
// initialize the info with SSA[0]
info->exception_vector = (sgx_exception_vector_t)ssa_gpr->exit_info.vector;
info->exception_type = (sgx_exception_type_t)ssa_gpr->exit_info.exit_type;
info->cpu_context.REG(ax) = ssa_gpr->REG(ax);
info->cpu_context.REG(cx) = ssa_gpr->REG(cx);
info->cpu_context.REG(dx) = ssa_gpr->REG(dx);
info->cpu_context.REG(bx) = ssa_gpr->REG(bx);
info->cpu_context.REG(sp) = ssa_gpr->REG(sp);
info->cpu_context.REG(bp) = ssa_gpr->REG(bp);
info->cpu_context.REG(si) = ssa_gpr->REG(si);
info->cpu_context.REG(di) = ssa_gpr->REG(di);
info->cpu_context.REG(flags) = ssa_gpr->REG(flags);
info->cpu_context.REG(ip) = ssa_gpr->REG(ip);
#ifdef SE_64
info->cpu_context.r8 = ssa_gpr->r8;
info->cpu_context.r9 = ssa_gpr->r9;
info->cpu_context.r10 = ssa_gpr->r10;
info->cpu_context.r11 = ssa_gpr->r11;
info->cpu_context.r12 = ssa_gpr->r12;
info->cpu_context.r13 = ssa_gpr->r13;
info->cpu_context.r14 = ssa_gpr->r14;
info->cpu_context.r15 = ssa_gpr->r15;
#endif
// decrease the stack to save the SSA[0]->ip
size = sizeof(uintptr_t);
new_sp = (uintptr_t *)(sp - size);
if(!is_stack_addr(new_sp, size))
{
g_enclave_state = ENCLAVE_CRASHED;
return SGX_ERROR_STACK_OVERRUN;
}
ssa_gpr->REG(ip) = (size_t)internal_handle_exception; // prepare the ip for 2nd phrase handling
ssa_gpr->REG(sp) = (size_t)new_sp; // new stack for internal_handle_exception
ssa_gpr->REG(ax) = (size_t)info; // 1st parameter (info) for LINUX32
ssa_gpr->REG(di) = (size_t)info; // 1st parameter (info) for LINUX64, LINUX32 also uses it while restoring the context
*new_sp = info->cpu_context.REG(ip); // for debugger to get call trace
return SGX_SUCCESS;
default_handler:
g_enclave_state = ENCLAVE_CRASHED;
return SGX_ERROR_ENCLAVE_CRASHED;
}