mirror of
https://github.com/intel/linux-sgx
synced 2026-06-08 14:49:32 +00:00
4d78a1a6cf
Signed-off-by: Zhang Lili <lili.z.zhang@intel.com>
1006 lines
35 KiB
C++
1006 lines
35 KiB
C++
/*
|
|
* Copyright (C) 2011-2020 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.
|
|
*
|
|
*/
|
|
|
|
#include "sgx_enclave_common.h"
|
|
#include "sgx_urts.h"
|
|
#include "arch.h"
|
|
#include "edmm_utility.h"
|
|
#include "isgx_user.h"
|
|
#include "se_error_internal.h"
|
|
#include "uae_service_internal.h"
|
|
#include "se_map.h"
|
|
#include "se_thread.h"
|
|
#include "se_trace.h"
|
|
#include "util.h"
|
|
#include <map>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/types.h>
|
|
#include <dlfcn.h>
|
|
#include "se_memcpy.h"
|
|
#define POINTER_TO_U64(A) ((__u64)((uintptr_t)(A)))
|
|
|
|
#define SGX_LAUNCH_SO "libsgx_launch.so.1"
|
|
#define SGX_GET_LAUNCH_TOKEN "get_launch_token"
|
|
|
|
func_get_launch_token_t get_launch_token_func = NULL;
|
|
|
|
static void* s_hdlopen = NULL;
|
|
static se_mutex_t s_dlopen_mutex;
|
|
|
|
static se_mutex_t s_device_mutex;
|
|
static se_mutex_t s_enclave_mutex;
|
|
|
|
static int s_driver_type = SGX_DRIVER_UNKNOWN; //driver which is opened
|
|
|
|
static se_file_handle_t s_hdevice = -1; //used for driver_type of SGX_DRIVER_OUT_OF_TREE or SGX_DRIVER_DCAP
|
|
static std::map<void*, int> s_hfile; //enclave file handles for driver_type of SGX_DRIVER_IN_KERNEL
|
|
|
|
static std::map<void*, size_t> s_enclave_size;
|
|
static std::map<void*, bool> s_enclave_init;
|
|
static std::map<void*, sgx_attributes_t> s_secs_attr;
|
|
|
|
|
|
typedef struct _mem_region_t {
|
|
void* addr;
|
|
size_t len;
|
|
int prot;
|
|
} mem_region_t;
|
|
|
|
static std::map<void*, mem_region_t> s_enclave_mem_region;
|
|
|
|
extern "C" bool open_file(int *hFile)
|
|
{
|
|
if (hFile == NULL)
|
|
return false;
|
|
|
|
se_mutex_lock(&s_device_mutex);
|
|
if (s_driver_type != SGX_DRIVER_IN_KERNEL) {
|
|
se_mutex_unlock(&s_device_mutex);
|
|
return false;
|
|
}
|
|
|
|
if (true == open_se_device(SGX_DRIVER_IN_KERNEL, hFile)) {
|
|
se_mutex_unlock(&s_device_mutex);
|
|
return true;
|
|
}
|
|
|
|
se_mutex_unlock(&s_device_mutex);
|
|
|
|
return false;
|
|
}
|
|
|
|
extern "C" void close_file(int *hFile)
|
|
{
|
|
se_mutex_lock(&s_device_mutex);
|
|
|
|
close_se_device(hFile);
|
|
|
|
se_mutex_unlock(&s_device_mutex);
|
|
}
|
|
|
|
extern "C" bool open_device(void)
|
|
{
|
|
se_mutex_lock(&s_device_mutex);
|
|
if (s_hdevice != -1) {
|
|
se_mutex_unlock(&s_device_mutex);
|
|
return true;
|
|
}
|
|
|
|
if (true == open_se_device(s_driver_type, &s_hdevice)) {
|
|
se_mutex_unlock(&s_device_mutex);
|
|
return true;
|
|
}
|
|
|
|
s_hdevice = -1;
|
|
se_mutex_unlock(&s_device_mutex);
|
|
|
|
return false;
|
|
}
|
|
|
|
extern "C" void close_device(void)
|
|
{
|
|
se_mutex_lock(&s_device_mutex);
|
|
|
|
close_se_device(&s_hdevice);
|
|
s_driver_type = SGX_DRIVER_UNKNOWN; //this may not be needed - can it change on the platform?
|
|
|
|
se_mutex_unlock(&s_device_mutex);
|
|
}
|
|
|
|
extern "C" int get_file_handle_from_address(void* target_address)
|
|
{
|
|
int hfile = -1;
|
|
|
|
//find the enclave file handle from the target address
|
|
se_mutex_lock(&s_enclave_mutex);
|
|
for (auto rec : s_enclave_size) {
|
|
if ((uint64_t)target_address >= (uint64_t)rec.first && (uint64_t)target_address < (uint64_t)rec.first + (uint64_t)rec.second)
|
|
{
|
|
if (s_hfile.count(rec.first) == 1)
|
|
{
|
|
hfile = s_hfile[rec.first];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
se_mutex_unlock(&s_enclave_mutex);
|
|
|
|
return hfile;
|
|
}
|
|
|
|
extern "C" void* get_enclave_base_address_from_address(void* target_address)
|
|
{
|
|
void* base_addr = NULL;
|
|
|
|
//find the enclave file handle from the target address
|
|
se_mutex_lock(&s_enclave_mutex);
|
|
for (auto rec : s_enclave_size) {
|
|
if ((uint64_t)target_address >= (uint64_t)rec.first && (uint64_t)target_address < (uint64_t)rec.first + (uint64_t)rec.second)
|
|
{
|
|
base_addr = rec.first;
|
|
break;
|
|
}
|
|
}
|
|
se_mutex_unlock(&s_enclave_mutex);
|
|
|
|
return base_addr;
|
|
|
|
}
|
|
|
|
static func_get_launch_token_t get_launch_token_function(void)
|
|
{
|
|
if (get_launch_token_func == NULL) {
|
|
se_mutex_lock(&s_dlopen_mutex);
|
|
if (get_launch_token_func != NULL)
|
|
{
|
|
se_mutex_unlock(&s_dlopen_mutex);
|
|
return get_launch_token_func;
|
|
}
|
|
|
|
if (s_hdlopen == NULL) {
|
|
s_hdlopen = dlopen(SGX_LAUNCH_SO, RTLD_LAZY);
|
|
if (s_hdlopen == NULL) {
|
|
se_mutex_unlock(&s_dlopen_mutex);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
get_launch_token_func = (func_get_launch_token_t)dlsym(s_hdlopen, SGX_GET_LAUNCH_TOKEN);
|
|
se_mutex_unlock(&s_dlopen_mutex);
|
|
}
|
|
|
|
return get_launch_token_func;
|
|
}
|
|
|
|
static void close_sofile(void)
|
|
{
|
|
se_mutex_lock(&s_dlopen_mutex);
|
|
if (s_hdlopen != NULL) {
|
|
dlclose(s_hdlopen);
|
|
s_hdlopen = NULL;
|
|
}
|
|
se_mutex_unlock(&s_dlopen_mutex);
|
|
}
|
|
|
|
static void __attribute__((constructor)) enclave_init(void)
|
|
{
|
|
se_mutex_init(&s_device_mutex);
|
|
se_mutex_init(&s_dlopen_mutex);
|
|
se_mutex_init(&s_enclave_mutex);
|
|
}
|
|
|
|
static void __attribute__((destructor)) enclave_fini(void)
|
|
{
|
|
close_device();
|
|
close_sofile();
|
|
se_mutex_destroy(&s_device_mutex);
|
|
se_mutex_destroy(&s_dlopen_mutex);
|
|
se_mutex_destroy(&s_enclave_mutex);
|
|
}
|
|
|
|
static uint32_t error_driver2api(int driver_error, int err_no)
|
|
{
|
|
uint32_t ret = ENCLAVE_UNEXPECTED;
|
|
|
|
if(driver_error == -1){
|
|
switch(err_no) {
|
|
case (int)ENOMEM:
|
|
ret = ENCLAVE_OUT_OF_MEMORY;
|
|
break;
|
|
case (int)EINVAL:
|
|
ret = ENCLAVE_INVALID_PARAMETER;
|
|
break;
|
|
case (int)EEXIST:
|
|
ret = ENCLAVE_INVALID_ADDRESS;
|
|
break;
|
|
case (int)EACCES:
|
|
ret = ENCLAVE_NOT_AUTHORIZED;
|
|
SE_PROD_LOG("Enclave not authorized to run, .e.g. provisioning enclave hosted in app "
|
|
"without access rights to /dev/sgx_provision. You need add the user id to group "
|
|
"sgx_prv or run the app as root.\n");
|
|
break;
|
|
default:
|
|
SE_TRACE(SE_TRACE_WARNING, "unexpected errno %#x from driver, might be a driver bug\n", err_no);
|
|
ret = ENCLAVE_UNEXPECTED;
|
|
break;
|
|
}
|
|
}
|
|
else{
|
|
switch (driver_error) {
|
|
case SGX_INVALID_SIG_STRUCT:
|
|
ret = ENCLAVE_INVALID_SIG_STRUCT;
|
|
break;
|
|
case SGX_INVALID_SIGNATURE:
|
|
ret = ENCLAVE_INVALID_SIGNATURE;
|
|
break;
|
|
case SGX_INVALID_ATTRIBUTE:
|
|
ret = ENCLAVE_INVALID_ATTRIBUTE;
|
|
break;
|
|
case SGX_INVALID_MEASUREMENT:
|
|
ret = ENCLAVE_INVALID_MEASUREMENT;
|
|
break;
|
|
case (int)SGX_POWER_LOST_ENCLAVE:
|
|
ret = ENCLAVE_LOST;
|
|
break;
|
|
case SGX_UNMASKED_EVENT:
|
|
ret = ENCLAVE_RETRY;
|
|
break;
|
|
case SGX_INVALID_PRIVILEGE:
|
|
ret = ENCLAVE_NOT_AUTHORIZED;
|
|
SE_PROD_LOG("Enclave not authorized to run, .e.g. provisioning enclave hosted in app "
|
|
"without access rights to /dev/sgx_provision. You need add the user id to group "
|
|
"sgx_prv or run the app as root.\n");
|
|
break;
|
|
default:
|
|
SE_TRACE(SE_TRACE_WARNING, "unexpected return value %#x from driver, might be a driver bug\n", driver_error);
|
|
ret = ENCLAVE_UNEXPECTED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t error_aesm2api(int aesm_error)
|
|
{
|
|
uint32_t ret = ENCLAVE_UNEXPECTED;
|
|
|
|
switch (aesm_error) {
|
|
case SGX_ERROR_INVALID_PARAMETER:
|
|
ret = ENCLAVE_INVALID_PARAMETER;
|
|
break;
|
|
case SGX_ERROR_SERVICE_UNAVAILABLE:
|
|
ret = ENCLAVE_SERVICE_NOT_AVAILABLE;
|
|
break;
|
|
case SGX_ERROR_NO_DEVICE:
|
|
ret = ENCLAVE_NOT_SUPPORTED;
|
|
break;
|
|
case SGX_ERROR_OUT_OF_EPC:
|
|
ret = ENCLAVE_DEVICE_NO_RESOURCES;
|
|
break;
|
|
case SGX_ERROR_SERVICE_INVALID_PRIVILEGE:
|
|
ret = ENCLAVE_NOT_AUTHORIZED;
|
|
break;
|
|
case SGX_ERROR_SERVICE_TIMEOUT:
|
|
ret = ENCLAVE_SERVICE_TIMEOUT;
|
|
break;
|
|
default:
|
|
ret = ENCLAVE_UNEXPECTED;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* enclave_create()
|
|
* Parameters:
|
|
* base_address [in, optional] - An optional preferred base address for the enclave.
|
|
* virtual_size [in] - The virtual address range of the enclave in bytes.
|
|
* initial_commit[in] - The amount of physical memory to reserve for the initial load of the enclave in bytes.
|
|
* type [in] - The architecture type of the enclave that you want to create.
|
|
* info [in] - A pointer to the architecture-specific information to use to create the enclave.
|
|
* info_size [in] - The length of the structure that the info parameter points to, in bytes.
|
|
* enclave_error [out, optional] - An optional pointer to a variable that receives an enclave error code.
|
|
* Return Value:
|
|
* If the function succeeds, the return value is the base address of the created enclave.
|
|
* If the function fails, the return value is NULL. The extended error information will be in the enclave_error parameter if used.
|
|
*/
|
|
extern "C" void* COMM_API enclave_create(
|
|
COMM_IN_OPT void* base_address,
|
|
COMM_IN size_t virtual_size,
|
|
COMM_IN size_t initial_commit,
|
|
COMM_IN uint32_t type,
|
|
COMM_IN const void* info,
|
|
COMM_IN size_t info_size,
|
|
COMM_OUT_OPT uint32_t* enclave_error)
|
|
{
|
|
UNUSED(initial_commit);
|
|
int hdevice_temp = -1;
|
|
size_t enclave_size = virtual_size;
|
|
void* enclave_base = NULL;
|
|
|
|
if ((type != ENCLAVE_TYPE_SGX1 && type != ENCLAVE_TYPE_SGX2) || info == NULL) {
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_INVALID_PARAMETER;
|
|
return NULL;
|
|
}
|
|
|
|
const enclave_create_sgx_t* enclave_create_sgx = (const enclave_create_sgx_t*)info;
|
|
if (info_size == 0 || sizeof(*enclave_create_sgx) != info_size) {
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_INVALID_PARAMETER;
|
|
return NULL;
|
|
}
|
|
|
|
secs_t* secs = (secs_t*)enclave_create_sgx->secs;
|
|
SE_TRACE(SE_TRACE_DEBUG, "\n secs->attibutes.flags = %llx, secs->attributes.xfrm = %llx \n", secs->attributes.flags, secs->attributes.xfrm);
|
|
|
|
if (s_driver_type == SGX_DRIVER_UNKNOWN)
|
|
{
|
|
//the driver type is not know and the device is not open
|
|
//determine the driver type and open the device
|
|
if (false == get_driver_type(&s_driver_type))
|
|
{
|
|
SE_TRACE(SE_TRACE_WARNING, "\ncreate enclave: failed to find a driver\n");
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_NOT_SUPPORTED;
|
|
return NULL;
|
|
}
|
|
//if out-of-tree or dcap driver then open the device - we just do this once
|
|
if (( s_driver_type == SGX_DRIVER_OUT_OF_TREE) || (s_driver_type == SGX_DRIVER_DCAP))
|
|
{
|
|
open_device();
|
|
}
|
|
}
|
|
//if in-kernel driver then open the file for each enclave load
|
|
if (s_driver_type == SGX_DRIVER_IN_KERNEL)
|
|
{
|
|
if (false == open_file( &hdevice_temp)) {
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_NOT_SUPPORTED;
|
|
return NULL;
|
|
}
|
|
//The in-kernel driver does not do the base and size alignment. This is up to user mode to do it.
|
|
//Therefore enclave_size will be virtual_size*2. The unused region will be released by calling munmap later.
|
|
enclave_size = virtual_size*2;
|
|
}
|
|
else
|
|
{
|
|
hdevice_temp = s_hdevice;
|
|
}
|
|
|
|
if(s_driver_type == SGX_DRIVER_IN_KERNEL)
|
|
{
|
|
enclave_base = mmap(base_address, enclave_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
}
|
|
else
|
|
{
|
|
enclave_base = mmap(base_address, enclave_size, PROT_NONE, MAP_SHARED, hdevice_temp, 0);
|
|
}
|
|
|
|
if (enclave_base == MAP_FAILED) {
|
|
SE_TRACE(SE_TRACE_WARNING, "\ncreate enclave: mmap failed, errno = %d\n", errno);
|
|
if (enclave_error != NULL)
|
|
*enclave_error = error_driver2api(-1, errno);
|
|
if(s_driver_type == SGX_DRIVER_IN_KERNEL)
|
|
{
|
|
close_file(&hdevice_temp);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
if(s_driver_type == SGX_DRIVER_IN_KERNEL)
|
|
{
|
|
uint64_t aligned_addr = ((uint64_t)enclave_base + virtual_size - 1) & ~(virtual_size - 1);
|
|
if(aligned_addr != (uint64_t)enclave_base)
|
|
{
|
|
int ret = munmap(enclave_base, aligned_addr - (uint64_t)enclave_base);
|
|
if(ret == -1)
|
|
{
|
|
SE_TRACE(SE_TRACE_WARNING, "\ncreate enclave: munmap failed, errno = %d\n", errno);
|
|
if (enclave_error != NULL)
|
|
{
|
|
*enclave_error = ENCLAVE_UNEXPECTED;
|
|
}
|
|
close_file(&hdevice_temp);
|
|
munmap(enclave_base, enclave_size);
|
|
return NULL;
|
|
}
|
|
}
|
|
if((uint64_t)enclave_base + virtual_size != aligned_addr)
|
|
{
|
|
int ret = munmap((void *)(aligned_addr + virtual_size),(uint64_t)enclave_base + virtual_size - aligned_addr);
|
|
if(ret == -1)
|
|
{
|
|
SE_TRACE(SE_TRACE_WARNING, "\ncreate enclave: munmap failed, errno = %d\n", errno);
|
|
if (enclave_error != NULL)
|
|
{
|
|
*enclave_error = ENCLAVE_UNEXPECTED;
|
|
}
|
|
close_file(&hdevice_temp);
|
|
munmap((void *)aligned_addr, enclave_size - aligned_addr + (uint64_t)enclave_base);
|
|
return NULL;
|
|
}
|
|
}
|
|
enclave_base = (void*)aligned_addr;
|
|
}
|
|
|
|
secs->base = enclave_base;
|
|
|
|
struct sgx_enclave_create param = { 0 };
|
|
param.src = POINTER_TO_U64(secs);
|
|
|
|
int ret = ioctl(hdevice_temp, SGX_IOC_ENCLAVE_CREATE, ¶m);
|
|
if (ret) {
|
|
SE_TRACE(SE_TRACE_WARNING, "\nSGX_IOC_ENCLAVE_CREATE failed: ret = %d\n", ret);
|
|
if (enclave_error != NULL)
|
|
*enclave_error = error_driver2api(ret, errno);
|
|
|
|
//if in-kernel driver then close the file handle
|
|
if (s_driver_type == SGX_DRIVER_IN_KERNEL)
|
|
{
|
|
close_file(&hdevice_temp);
|
|
}
|
|
munmap(enclave_base, virtual_size);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//in-kernel and DCAP drivers support special provision key access mode (DCAP also supports whitelisting for provision key access)
|
|
if (((s_driver_type == SGX_DRIVER_IN_KERNEL) || (s_driver_type == SGX_DRIVER_DCAP)) && (secs->attributes.flags & SGX_FLAGS_PROVISION_KEY))
|
|
{
|
|
int hdev_prov = -1;
|
|
if (s_driver_type == SGX_DRIVER_IN_KERNEL)
|
|
{
|
|
hdev_prov = open("/dev/sgx/provision", O_RDWR);
|
|
if (-1 == hdev_prov)
|
|
{
|
|
//in-kernel driver can still succeed if the MRSIGNER is whitelisted for provision key
|
|
SE_TRACE(SE_TRACE_WARNING, "\nOpen in-kernel driver node, failed: errno = %d\n", errno);
|
|
}
|
|
else
|
|
{
|
|
struct sgx_enclave_set_attribute_in_kernel attrp = { 0 };
|
|
attrp.attribute_fd = hdev_prov;
|
|
int ret2 = ioctl(hdevice_temp, SGX_IOC_ENCLAVE_SET_ATTRIBUTE_IN_KERNEL, &attrp);
|
|
if ( ret2 )
|
|
{
|
|
SE_TRACE(SE_TRACE_WARNING, "\nSGX_IOC_ENCLAVE_SET_ATTRIBUTE, failed: errno = %d\n", errno);
|
|
}
|
|
}
|
|
close(hdev_prov);
|
|
}
|
|
else
|
|
{
|
|
hdev_prov = open("/dev/sgx_prv", O_RDWR);
|
|
if (-1 == hdev_prov)
|
|
{
|
|
//DCAP driver can still succeed if the MRSIGNER is whitelisted for provision key
|
|
SE_TRACE(SE_TRACE_WARNING, "\nOpen DCAP driver node, failed: errno = %d\n", errno);
|
|
}
|
|
else
|
|
{
|
|
struct sgx_enclave_set_attribute attrp = { 0, 0 };
|
|
attrp.addr = POINTER_TO_U64(enclave_base);
|
|
attrp.attribute_fd = hdev_prov;
|
|
int ret2 = ioctl(hdevice_temp, SGX_IOC_ENCLAVE_SET_ATTRIBUTE, &attrp);
|
|
if ( ret2 )
|
|
{
|
|
SE_TRACE(SE_TRACE_WARNING, "\nSGX_IOC_ENCLAVE_SET_ATTRIBUTE, failed: errno = %d\n", errno);
|
|
//It may fail here if DCAP driver doesn't support this ioctl
|
|
//Therefore we will continue here instead of returning error code
|
|
//The initialization could fail if the driver requires the provision file access
|
|
}
|
|
close(hdev_prov);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
se_mutex_lock(&s_enclave_mutex);
|
|
|
|
//if in-kernel driver then save the file handle
|
|
if (s_driver_type == SGX_DRIVER_IN_KERNEL)
|
|
{
|
|
s_hfile[enclave_base] = hdevice_temp;
|
|
}
|
|
s_enclave_size[enclave_base] = virtual_size;
|
|
|
|
sgx_attributes_t secs_attr;
|
|
memset(&secs_attr, 0, sizeof(sgx_attributes_t));
|
|
memcpy_s(&secs_attr, sizeof(sgx_attributes_t), &secs->attributes, sizeof(sgx_attributes_t));
|
|
s_secs_attr[enclave_base] = secs_attr;
|
|
|
|
s_enclave_mem_region[enclave_base].addr = 0;
|
|
s_enclave_mem_region[enclave_base].len = 0;
|
|
s_enclave_mem_region[enclave_base].prot = 0;
|
|
|
|
se_mutex_unlock(&s_enclave_mutex);
|
|
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_ERROR_SUCCESS;
|
|
return enclave_base;
|
|
}
|
|
|
|
/* enclave_load_data()
|
|
* Parameters:
|
|
* target_address [in] - The address in the enclave where you want to load the data.
|
|
* target_size [in] - The size of the range that you want to load in the enclave, in bytes.
|
|
* source_buffer [in, optional] - An optional pointer to the data you want to load into the enclave.
|
|
* data_properties [in] - The properties of the pages you want to add to the enclave.
|
|
* enclave_error [out, optional] - An optional pointer to a variable that receives an enclave error code.
|
|
* Return Value:
|
|
* The return value is the number of bytes that was loaded into the enclave.
|
|
* If the number is different than target_size parameter an error occurred. The extended error information will be in the enclave_error parameter if used.
|
|
*/
|
|
extern "C" size_t COMM_API enclave_load_data(
|
|
COMM_IN void* target_address,
|
|
COMM_IN size_t target_size,
|
|
COMM_IN_OPT const void* source_buffer,
|
|
COMM_IN uint32_t data_properties,
|
|
COMM_OUT_OPT uint32_t* enclave_error)
|
|
{
|
|
if (target_address == NULL || ((uint64_t)(target_address) & ((1 << SE_PAGE_SHIFT) - 1)) != 0 || target_size < SE_PAGE_SIZE || target_size % SE_PAGE_SIZE != 0) {
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_INVALID_PARAMETER;
|
|
return 0;
|
|
}
|
|
|
|
sec_info_t sec_info;
|
|
memset(&sec_info, 0, sizeof(sec_info_t));
|
|
|
|
sec_info.flags = data_properties;
|
|
if (!(sec_info.flags & ENCLAVE_PAGE_THREAD_CONTROL))
|
|
sec_info.flags |= SI_FLAG_REG;
|
|
else
|
|
sec_info.flags &= ~SI_MASK_MEM_ATTRIBUTE;
|
|
|
|
if (sec_info.flags & ENCLAVE_PAGE_UNVALIDATED)
|
|
sec_info.flags ^= ENCLAVE_PAGE_UNVALIDATED;
|
|
|
|
int hfile = -1;
|
|
size_t pages = target_size / SE_PAGE_SIZE;
|
|
if (s_driver_type == SGX_DRIVER_IN_KERNEL)
|
|
{
|
|
hfile = get_file_handle_from_address(target_address);
|
|
|
|
//todo - may need to check EADD parameters for better error reporting since the driver
|
|
// may not do this (the driver will just take a fault on EADD)
|
|
|
|
if (hfile == -1)
|
|
{
|
|
SE_TRACE(SE_TRACE_WARNING, "\nAdd Page FAILED - %p is not in a valid enclave \n", target_address);
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_INVALID_ADDRESS;
|
|
return 0;
|
|
}
|
|
|
|
void* enclave_base_addr = get_enclave_base_address_from_address(target_address);
|
|
if (enclave_base_addr == NULL)
|
|
{
|
|
SE_TRACE(SE_TRACE_WARNING, "\nAdd Page FAILED - %p is not in a valid enclave \n", target_address);
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_INVALID_ADDRESS;
|
|
return 0;
|
|
}
|
|
|
|
uint8_t* source = (uint8_t*)source_buffer;
|
|
if (source == NULL) {
|
|
source = (uint8_t*)aligned_alloc(SE_PAGE_SIZE, target_size);
|
|
if(source == NULL)
|
|
{
|
|
if (enclave_error != NULL)
|
|
{
|
|
*enclave_error = ENCLAVE_OUT_OF_MEMORY;
|
|
}
|
|
return 0;
|
|
}
|
|
memset(source, 0 , target_size);
|
|
}
|
|
|
|
struct sgx_enclave_add_pages_in_kernel addp;
|
|
memset(&addp, 0, sizeof(sgx_enclave_add_pages_in_kernel));
|
|
if(source_buffer != NULL)
|
|
{
|
|
addp.src = POINTER_TO_U64(source_buffer);
|
|
}
|
|
else
|
|
{
|
|
addp.src = POINTER_TO_U64(source);
|
|
}
|
|
|
|
addp.offset = POINTER_TO_U64((uint64_t)target_address - (uint64_t)enclave_base_addr);
|
|
addp.length = target_size;
|
|
addp.secinfo = POINTER_TO_U64(&sec_info);
|
|
if (!(data_properties & ENCLAVE_PAGE_UNVALIDATED))
|
|
addp.flags = SGX_PAGE_MEASURE;
|
|
addp.count = 0;
|
|
int ret = ioctl(hfile, SGX_IOC_ENCLAVE_ADD_PAGES_IN_KERNEL, &addp);
|
|
if (ret) {
|
|
SE_TRACE(SE_TRACE_WARNING, "\nAdd Page - %p to %p... FAIL\n", source, target_address);
|
|
if (enclave_error != NULL)
|
|
*enclave_error = error_driver2api(ret, errno);
|
|
if(source_buffer == NULL)
|
|
{
|
|
free(source);
|
|
source = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
if(source_buffer == NULL)
|
|
{
|
|
free(source);
|
|
source = NULL;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
uint8_t page_data[SE_PAGE_SIZE] __attribute__ ((aligned(4096)));
|
|
|
|
uint8_t* source = (uint8_t*)source_buffer;
|
|
if (source == NULL) {
|
|
source = page_data;
|
|
memset(source, 0, SE_PAGE_SIZE);
|
|
}
|
|
//DCAP and out-of-tree driver use same parameter for the SGX_IOC_ENCLAVE_ADD_PAGE
|
|
for (size_t i = 0; i < pages; i++) {
|
|
struct sgx_enclave_add_page addp = { 0, 0, 0, 0 };
|
|
addp.addr = POINTER_TO_U64((uint8_t*)target_address + SE_PAGE_SIZE * i);
|
|
if(source_buffer != NULL)
|
|
{
|
|
addp.src = POINTER_TO_U64(source + SE_PAGE_SIZE * i);
|
|
}
|
|
else
|
|
{
|
|
addp.src = POINTER_TO_U64(source);
|
|
}
|
|
addp.secinfo = POINTER_TO_U64(&sec_info);
|
|
if (!(data_properties & ENCLAVE_PAGE_UNVALIDATED))
|
|
addp.mrmask |= 0xFFFF;
|
|
|
|
int ret = ioctl(s_hdevice, SGX_IOC_ENCLAVE_ADD_PAGE, &addp);
|
|
if (ret) {
|
|
SE_TRACE(SE_TRACE_WARNING, "\nAdd Page - %p to %p... FAIL\n", source, target_address);
|
|
|
|
if (enclave_error != NULL)
|
|
*enclave_error = error_driver2api(ret, errno);
|
|
return SE_PAGE_SIZE * i;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int prot = (int)(sec_info.flags & SI_MASK_MEM_ATTRIBUTE);
|
|
if (sec_info.flags & ENCLAVE_PAGE_THREAD_CONTROL)
|
|
{
|
|
prot = (int)(SI_FLAGS_RW & SI_MASK_MEM_ATTRIBUTE);
|
|
}
|
|
|
|
// find the enclave base
|
|
void* enclave_base = get_enclave_base_address_from_address(target_address);
|
|
if (enclave_base == NULL) {
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_INVALID_ENCLAVE;
|
|
return 0;
|
|
}
|
|
|
|
se_mutex_lock(&s_enclave_mutex);
|
|
auto enclave_mem_region = &s_enclave_mem_region[enclave_base];
|
|
se_mutex_unlock(&s_enclave_mutex);
|
|
|
|
void* next_page = (void*)((uint64_t)enclave_mem_region->addr + (uint64_t)enclave_mem_region->len);
|
|
if ((enclave_mem_region->prot != prot) || (target_address != next_page)) {
|
|
if (enclave_mem_region->addr != 0) {
|
|
//the new load of enclave data either has a different protection or is not contiguous with the last one, mprotect/mmap the range stored in memory region structure
|
|
int ret = 0;
|
|
if (hfile != -1) {
|
|
if (MAP_FAILED == mmap(enclave_mem_region->addr, enclave_mem_region->len,
|
|
enclave_mem_region->prot, MAP_SHARED | MAP_FIXED, hfile, 0))
|
|
ret=-1;
|
|
}
|
|
else
|
|
ret = mprotect(enclave_mem_region->addr, enclave_mem_region->len, enclave_mem_region->prot);
|
|
if (0 != ret) {
|
|
if (enclave_error != NULL)
|
|
*enclave_error = error_driver2api(-1, errno);
|
|
return 0;
|
|
}
|
|
}
|
|
//record the current load of enclave data in the memory region structure
|
|
enclave_mem_region->addr = target_address;
|
|
enclave_mem_region->len = target_size;
|
|
enclave_mem_region->prot = prot;
|
|
} else {
|
|
//this load of enclave data is extending the memory region
|
|
enclave_mem_region->len += target_size;
|
|
}
|
|
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_ERROR_SUCCESS;
|
|
return target_size;
|
|
}
|
|
|
|
/* enclave_initialize()
|
|
* Parameters:
|
|
* base_address [in] - The enclave base address as returned from the enclave_create API.
|
|
* info [in] - A pointer to the architecture-specific information to use to initialize the enclave.
|
|
* info_size [in] - The length of the structure that the info parameter points to, in bytes.
|
|
* enclave_error [out, optional] - An optional pointer to a variable that receives an enclave error code.
|
|
* Return Value:
|
|
* non-zero - The function succeeds.
|
|
* zero - The function fails and the extended error information will be in the enclave_error parameter if used.
|
|
*/
|
|
extern "C" bool COMM_API enclave_initialize(
|
|
COMM_IN void* base_address,
|
|
COMM_IN const void* info,
|
|
COMM_IN size_t info_size,
|
|
COMM_OUT_OPT uint32_t* enclave_error)
|
|
{
|
|
if (base_address == NULL || info == NULL) {
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_INVALID_PARAMETER;
|
|
return false;
|
|
}
|
|
|
|
int hfile = -1;
|
|
if (s_driver_type == SGX_DRIVER_IN_KERNEL)
|
|
{
|
|
hfile = get_file_handle_from_address(base_address);
|
|
if (hfile == -1)
|
|
{
|
|
SE_TRACE(SE_TRACE_WARNING, "\nSGX_IOC_ENCLAVE_INIT failed - %p is not a valid enclave \n", base_address);
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_INVALID_ADDRESS;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const enclave_init_sgx_t* enclave_init_sgx = (const enclave_init_sgx_t*)info;
|
|
if (info_size == 0 || sizeof(*enclave_init_sgx) != info_size) {
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_INVALID_PARAMETER;
|
|
return false;
|
|
}
|
|
|
|
//mprotect the last region
|
|
se_mutex_lock(&s_enclave_mutex);
|
|
auto enclave_mem_region = &s_enclave_mem_region[base_address];
|
|
se_mutex_unlock(&s_enclave_mutex);
|
|
if (enclave_mem_region->addr != 0) {
|
|
//the new load of enclave data either has a different protection or is not contiguous with the last one, mprotect/mmap the range stored in memory region structure
|
|
int ret = 0;
|
|
if (hfile != -1) {
|
|
if (MAP_FAILED == mmap(enclave_mem_region->addr, enclave_mem_region->len, enclave_mem_region->prot, MAP_SHARED | MAP_FIXED, hfile, 0))
|
|
ret=-1;
|
|
}
|
|
else
|
|
ret= mprotect(enclave_mem_region->addr, enclave_mem_region->len, enclave_mem_region->prot);
|
|
if (0 != ret) {
|
|
if (enclave_error != NULL)
|
|
*enclave_error = error_driver2api(-1, errno);
|
|
return 0;
|
|
}
|
|
//record the current load of enclave data in the memory region structure
|
|
enclave_mem_region->addr = 0; //just in case we need to call enclave_initialize twice
|
|
}
|
|
|
|
int ret = 0;
|
|
if ( s_driver_type == SGX_DRIVER_OUT_OF_TREE )
|
|
{
|
|
//out-of-tree driver requires a launch token to be provided
|
|
se_mutex_lock(&s_enclave_mutex);
|
|
std::map<void*, sgx_attributes_t>::iterator it = s_secs_attr.find(base_address);
|
|
if (it == s_secs_attr.end()) {
|
|
se_mutex_unlock(&s_enclave_mutex);
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_INVALID_PARAMETER;
|
|
return false;
|
|
}
|
|
se_mutex_unlock(&s_enclave_mutex);
|
|
|
|
sgx_launch_token_t launch_token;
|
|
memset(launch_token, 0, sizeof(sgx_launch_token_t));
|
|
|
|
enclave_css_t* enclave_css = (enclave_css_t*)enclave_init_sgx->sigstruct;
|
|
if (0 == enclave_css->header.hw_version) {
|
|
func_get_launch_token_t func = get_launch_token_function();
|
|
if (func == NULL) {
|
|
SE_TRACE(SE_TRACE_WARNING, "Failed to get sysmbol %s from %s.\n", SGX_GET_LAUNCH_TOKEN, SGX_LAUNCH_SO);
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_UNEXPECTED;
|
|
return false;
|
|
}
|
|
|
|
sgx_status_t status = func(enclave_css, &it->second, &launch_token);
|
|
if (status != SGX_SUCCESS) {
|
|
if (enclave_error != NULL)
|
|
*enclave_error = error_aesm2api(status);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
struct sgx_enclave_init initp = { 0, 0, 0 };
|
|
initp.addr = POINTER_TO_U64(base_address);
|
|
initp.sigstruct = POINTER_TO_U64(enclave_css);
|
|
initp.einittoken = POINTER_TO_U64(&launch_token);
|
|
|
|
ret = ioctl(s_hdevice, SGX_IOC_ENCLAVE_INIT, &initp);
|
|
}
|
|
else if (s_driver_type == SGX_DRIVER_DCAP )
|
|
{
|
|
//dcap driver does not need a launch token
|
|
struct sgx_enclave_init_dcap initp = { 0, 0 };
|
|
initp.addr = POINTER_TO_U64(base_address);
|
|
initp.sigstruct = POINTER_TO_U64(enclave_init_sgx->sigstruct);
|
|
|
|
ret = ioctl(s_hdevice, SGX_IOC_ENCLAVE_INIT_DCAP, &initp);
|
|
}
|
|
else
|
|
{
|
|
//in-kernel driver does not need a launch token or the enclave address
|
|
struct sgx_enclave_init_in_kernel initp = { 0 };
|
|
initp.sigstruct = POINTER_TO_U64(enclave_init_sgx->sigstruct);
|
|
|
|
ret = ioctl(hfile, SGX_IOC_ENCLAVE_INIT_IN_KERNEL, &initp);
|
|
}
|
|
|
|
if (ret) {
|
|
SE_TRACE(SE_TRACE_WARNING, "\nSGX_IOC_ENCLAVE_INIT failed error = %d\n", ret);
|
|
if (enclave_error != NULL)
|
|
*enclave_error = error_driver2api(ret, errno);
|
|
return false;
|
|
}
|
|
|
|
se_mutex_lock(&s_enclave_mutex);
|
|
std::map<void*, bool>::iterator it = s_enclave_init.find(base_address);
|
|
if (it != s_enclave_init.end() && it->second) {
|
|
se_mutex_unlock(&s_enclave_mutex);
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_ALREADY_INITIALIZED;
|
|
return false;
|
|
}
|
|
|
|
s_enclave_init[base_address] = true;
|
|
se_mutex_unlock(&s_enclave_mutex);
|
|
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_ERROR_SUCCESS;
|
|
return true;
|
|
}
|
|
|
|
/* enclave_delete()
|
|
* Parameters:
|
|
* base_address [in] - The enclave base address as returned from the enclave_create API.
|
|
* enclave_error [out, optional] - An optional pointer to a variable that receives an enclave error code.
|
|
* Return Value:
|
|
* non-zero - The function succeeds.
|
|
* zero - The function fails and the extended error information will be in the enclave_error parameter if used.
|
|
*/
|
|
extern "C" bool COMM_API enclave_delete(
|
|
COMM_IN void* base_address,
|
|
COMM_OUT_OPT uint32_t* enclave_error)
|
|
{
|
|
if (base_address == NULL) {
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_INVALID_PARAMETER;
|
|
return false;
|
|
}
|
|
|
|
se_mutex_lock(&s_enclave_mutex);
|
|
std::map<void*, size_t>::iterator it = s_enclave_size.find(base_address);
|
|
if (it == s_enclave_size.end()) {
|
|
se_mutex_unlock(&s_enclave_mutex);
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_INVALID_PARAMETER;
|
|
return false;
|
|
}
|
|
|
|
s_enclave_size.erase(base_address);
|
|
s_enclave_init.erase(base_address);
|
|
s_enclave_mem_region.erase(base_address);
|
|
if (s_driver_type == SGX_DRIVER_IN_KERNEL)
|
|
{
|
|
int hfile_temp = s_hfile[base_address];
|
|
close_file(&hfile_temp);
|
|
s_hfile.erase(base_address);
|
|
}
|
|
se_mutex_unlock(&s_enclave_mutex);
|
|
|
|
if (0 != munmap(base_address, it->second)) {
|
|
SE_TRACE(SE_TRACE_WARNING, "delete SGX enclave failed, error = %d\n", errno);
|
|
if (enclave_error != NULL) {
|
|
if (errno == EINVAL)
|
|
*enclave_error = ENCLAVE_INVALID_PARAMETER;
|
|
else
|
|
*enclave_error = ENCLAVE_UNEXPECTED;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (enclave_error != NULL)
|
|
*enclave_error = ENCLAVE_ERROR_SUCCESS;
|
|
return true;
|
|
}
|
|
|
|
/* enclave_get_information()
|
|
* Parameters:
|
|
* base_address [in] - The enclave base address as returned from the enclave_create API.
|
|
* info_type[in] - Identifies the type of information requested. initialized.
|
|
* output_info[out] - Pointer to information returned by the API
|
|
* output_info_size[in, out] - Size of the output_info buffer, in bytes. If the API succeeds, then this will return the number of bytes returned in output_info. If the API fails with, ENCLAVE_INVALID_SIZE, then this will return the required size
|
|
* enclave_error [out, optional] - An optional pointer to a variable that receives an enclave error code.
|
|
*/
|
|
bool COMM_API enclave_get_information(
|
|
COMM_IN void* base_address,
|
|
COMM_IN uint32_t info_type,
|
|
COMM_OUT void* output_info,
|
|
COMM_IN_OUT size_t* output_info_size,
|
|
COMM_OUT_OPT uint32_t* enclave_error)
|
|
{
|
|
UNUSED(base_address);
|
|
UNUSED(info_type);
|
|
UNUSED(output_info);
|
|
UNUSED(output_info_size);
|
|
|
|
if (enclave_error)
|
|
*enclave_error = ENCLAVE_NOT_SUPPORTED;
|
|
|
|
return false;
|
|
}
|
|
|
|
/* enclave_set_information
|
|
* Parameters
|
|
* base_address [in] - The enclave base address as returned from the enclave_create API.
|
|
* info_type[in] - Identifies the type of information requested. not been initialized.
|
|
* input_info[in] - Pointer to information provided to the API
|
|
* input_info_size[in] - Size of the information, in bytes, provided in input_info from the API.
|
|
* enclave_error [out, optional] - An optional pointer to a variable that receives an enclave error code.
|
|
*/
|
|
bool COMM_API enclave_set_information(
|
|
COMM_IN void* base_address,
|
|
COMM_IN uint32_t info_type,
|
|
COMM_IN void* input_info,
|
|
COMM_IN size_t input_info_size,
|
|
COMM_OUT_OPT uint32_t* enclave_error)
|
|
{
|
|
UNUSED(base_address);
|
|
UNUSED(info_type);
|
|
UNUSED(input_info);
|
|
UNUSED(input_info_size);
|
|
|
|
if (enclave_error)
|
|
*enclave_error = ENCLAVE_NOT_SUPPORTED;
|
|
|
|
return false;
|
|
}
|