Skip to content

Commit 45d546b

Browse files
rchatrehansendc
authored andcommitted
x86/sgx: Support modifying SGX page type
Every enclave contains one or more Thread Control Structures (TCS). The TCS contains meta-data used by the hardware to save and restore thread specific information when entering/exiting the enclave. With SGX1 an enclave needs to be created with enough TCSs to support the largest number of threads expecting to use the enclave and enough enclave pages to meet all its anticipated memory demands. In SGX1 all pages remain in the enclave until the enclave is unloaded. SGX2 introduces a new function, ENCLS[EMODT], that is used to change the type of an enclave page from a regular (SGX_PAGE_TYPE_REG) enclave page to a TCS (SGX_PAGE_TYPE_TCS) page or change the type from a regular (SGX_PAGE_TYPE_REG) or TCS (SGX_PAGE_TYPE_TCS) page to a trimmed (SGX_PAGE_TYPE_TRIM) page (setting it up for later removal). With the existing support of dynamically adding regular enclave pages to an initialized enclave and changing the page type to TCS it is possible to dynamically increase the number of threads supported by an enclave. Changing the enclave page type to SGX_PAGE_TYPE_TRIM is the first step of dynamically removing pages from an initialized enclave. The complete page removal flow is: 1) Change the type of the pages to be removed to SGX_PAGE_TYPE_TRIM using the SGX_IOC_ENCLAVE_MODIFY_TYPES ioctl() introduced here. 2) Approve the page removal by running ENCLU[EACCEPT] from within the enclave. 3) Initiate actual page removal using the ioctl() introduced in the following patch. Add ioctl() SGX_IOC_ENCLAVE_MODIFY_TYPES to support changing SGX enclave page types within an initialized enclave. With SGX_IOC_ENCLAVE_MODIFY_TYPES the user specifies a page range and the enclave page type to be applied to all pages in the provided range. The ioctl() itself can return an error code based on failures encountered by the kernel. It is also possible for SGX specific failures to be encountered. Add a result output parameter to communicate the SGX return code. It is possible for the enclave page type change request to fail on any page within the provided range. Support partial success by returning the number of pages that were successfully changed. After the page type is changed the page continues to be accessible from the kernel perspective with page table entries and internal state. The page may be moved to swap. Any access until ENCLU[EACCEPT] will encounter a page fault with SGX flag set in error code. Signed-off-by: Reinette Chatre <[email protected]> Signed-off-by: Dave Hansen <[email protected]> Reviewed-by: Jarkko Sakkinen <[email protected]> Tested-by: Jarkko Sakkinen <[email protected]> Tested-by: Haitao Huang <[email protected]> Tested-by: Vijay Dhanraj <[email protected]> Link: https://lkml.kernel.org/r/babe39318c5bf16fc65fbfb38896cdee72161575.1652137848.git.reinette.chatre@intel.com
1 parent 7b013e7 commit 45d546b

File tree

2 files changed

+222
-0
lines changed

2 files changed

+222
-0
lines changed

arch/x86/include/uapi/asm/sgx.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ enum sgx_page_flags {
3131
_IO(SGX_MAGIC, 0x04)
3232
#define SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS \
3333
_IOWR(SGX_MAGIC, 0x05, struct sgx_enclave_restrict_permissions)
34+
#define SGX_IOC_ENCLAVE_MODIFY_TYPES \
35+
_IOWR(SGX_MAGIC, 0x06, struct sgx_enclave_modify_types)
3436

3537
/**
3638
* struct sgx_enclave_create - parameter structure for the
@@ -97,6 +99,24 @@ struct sgx_enclave_restrict_permissions {
9799
__u64 count;
98100
};
99101

102+
/**
103+
* struct sgx_enclave_modify_types - parameters for ioctl
104+
* %SGX_IOC_ENCLAVE_MODIFY_TYPES
105+
* @offset: starting page offset (page aligned relative to enclave base
106+
* address defined in SECS)
107+
* @length: length of memory (multiple of the page size)
108+
* @page_type: new type for pages in range described by @offset and @length
109+
* @result: (output) SGX result code of ENCLS[EMODT] function
110+
* @count: (output) bytes successfully changed (multiple of page size)
111+
*/
112+
struct sgx_enclave_modify_types {
113+
__u64 offset;
114+
__u64 length;
115+
__u64 page_type;
116+
__u64 result;
117+
__u64 count;
118+
};
119+
100120
struct sgx_enclave_run;
101121

102122
/**

arch/x86/kernel/cpu/sgx/ioctl.c

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,205 @@ static long sgx_ioc_enclave_restrict_permissions(struct sgx_encl *encl,
872872
return ret;
873873
}
874874

875+
/**
876+
* sgx_enclave_modify_types() - Modify type of SGX enclave pages
877+
* @encl: Enclave to which the pages belong.
878+
* @modt: Checked parameters from user about which pages need modifying
879+
* and their new page type.
880+
*
881+
* Return:
882+
* - 0: Success
883+
* - -errno: Otherwise
884+
*/
885+
static long sgx_enclave_modify_types(struct sgx_encl *encl,
886+
struct sgx_enclave_modify_types *modt)
887+
{
888+
unsigned long max_prot_restore;
889+
enum sgx_page_type page_type;
890+
struct sgx_encl_page *entry;
891+
struct sgx_secinfo secinfo;
892+
unsigned long prot;
893+
unsigned long addr;
894+
unsigned long c;
895+
void *epc_virt;
896+
int ret;
897+
898+
page_type = modt->page_type & SGX_PAGE_TYPE_MASK;
899+
900+
/*
901+
* The only new page types allowed by hardware are PT_TCS and PT_TRIM.
902+
*/
903+
if (page_type != SGX_PAGE_TYPE_TCS && page_type != SGX_PAGE_TYPE_TRIM)
904+
return -EINVAL;
905+
906+
memset(&secinfo, 0, sizeof(secinfo));
907+
908+
secinfo.flags = page_type << 8;
909+
910+
for (c = 0 ; c < modt->length; c += PAGE_SIZE) {
911+
addr = encl->base + modt->offset + c;
912+
913+
mutex_lock(&encl->lock);
914+
915+
entry = sgx_encl_load_page(encl, addr);
916+
if (IS_ERR(entry)) {
917+
ret = PTR_ERR(entry) == -EBUSY ? -EAGAIN : -EFAULT;
918+
goto out_unlock;
919+
}
920+
921+
/*
922+
* Borrow the logic from the Intel SDM. Regular pages
923+
* (SGX_PAGE_TYPE_REG) can change type to SGX_PAGE_TYPE_TCS
924+
* or SGX_PAGE_TYPE_TRIM but TCS pages can only be trimmed.
925+
* CET pages not supported yet.
926+
*/
927+
if (!(entry->type == SGX_PAGE_TYPE_REG ||
928+
(entry->type == SGX_PAGE_TYPE_TCS &&
929+
page_type == SGX_PAGE_TYPE_TRIM))) {
930+
ret = -EINVAL;
931+
goto out_unlock;
932+
}
933+
934+
max_prot_restore = entry->vm_max_prot_bits;
935+
936+
/*
937+
* Once a regular page becomes a TCS page it cannot be
938+
* changed back. So the maximum allowed protection reflects
939+
* the TCS page that is always RW from kernel perspective but
940+
* will be inaccessible from within enclave. Before doing
941+
* so, do make sure that the new page type continues to
942+
* respect the originally vetted page permissions.
943+
*/
944+
if (entry->type == SGX_PAGE_TYPE_REG &&
945+
page_type == SGX_PAGE_TYPE_TCS) {
946+
if (~entry->vm_max_prot_bits & (VM_READ | VM_WRITE)) {
947+
ret = -EPERM;
948+
goto out_unlock;
949+
}
950+
prot = PROT_READ | PROT_WRITE;
951+
entry->vm_max_prot_bits = calc_vm_prot_bits(prot, 0);
952+
953+
/*
954+
* Prevent page from being reclaimed while mutex
955+
* is released.
956+
*/
957+
if (sgx_unmark_page_reclaimable(entry->epc_page)) {
958+
ret = -EAGAIN;
959+
goto out_entry_changed;
960+
}
961+
962+
/*
963+
* Do not keep encl->lock because of dependency on
964+
* mmap_lock acquired in sgx_zap_enclave_ptes().
965+
*/
966+
mutex_unlock(&encl->lock);
967+
968+
sgx_zap_enclave_ptes(encl, addr);
969+
970+
mutex_lock(&encl->lock);
971+
972+
sgx_mark_page_reclaimable(entry->epc_page);
973+
}
974+
975+
/* Change EPC type */
976+
epc_virt = sgx_get_epc_virt_addr(entry->epc_page);
977+
ret = __emodt(&secinfo, epc_virt);
978+
if (encls_faulted(ret)) {
979+
/*
980+
* All possible faults should be avoidable:
981+
* parameters have been checked, will only change
982+
* valid page types, and no concurrent
983+
* SGX1/SGX2 ENCLS instructions since these are
984+
* protected with mutex.
985+
*/
986+
pr_err_once("EMODT encountered exception %d\n",
987+
ENCLS_TRAPNR(ret));
988+
ret = -EFAULT;
989+
goto out_entry_changed;
990+
}
991+
if (encls_failed(ret)) {
992+
modt->result = ret;
993+
ret = -EFAULT;
994+
goto out_entry_changed;
995+
}
996+
997+
ret = sgx_enclave_etrack(encl);
998+
if (ret) {
999+
ret = -EFAULT;
1000+
goto out_unlock;
1001+
}
1002+
1003+
entry->type = page_type;
1004+
1005+
mutex_unlock(&encl->lock);
1006+
}
1007+
1008+
ret = 0;
1009+
goto out;
1010+
1011+
out_entry_changed:
1012+
entry->vm_max_prot_bits = max_prot_restore;
1013+
out_unlock:
1014+
mutex_unlock(&encl->lock);
1015+
out:
1016+
modt->count = c;
1017+
1018+
return ret;
1019+
}
1020+
1021+
/**
1022+
* sgx_ioc_enclave_modify_types() - handler for %SGX_IOC_ENCLAVE_MODIFY_TYPES
1023+
* @encl: an enclave pointer
1024+
* @arg: userspace pointer to a &struct sgx_enclave_modify_types instance
1025+
*
1026+
* Ability to change the enclave page type supports the following use cases:
1027+
*
1028+
* * It is possible to add TCS pages to an enclave by changing the type of
1029+
* regular pages (%SGX_PAGE_TYPE_REG) to TCS (%SGX_PAGE_TYPE_TCS) pages.
1030+
* With this support the number of threads supported by an initialized
1031+
* enclave can be increased dynamically.
1032+
*
1033+
* * Regular or TCS pages can dynamically be removed from an initialized
1034+
* enclave by changing the page type to %SGX_PAGE_TYPE_TRIM. Changing the
1035+
* page type to %SGX_PAGE_TYPE_TRIM marks the page for removal with actual
1036+
* removal done by handler of %SGX_IOC_ENCLAVE_REMOVE_PAGES ioctl() called
1037+
* after ENCLU[EACCEPT] is run on %SGX_PAGE_TYPE_TRIM page from within the
1038+
* enclave.
1039+
*
1040+
* Return:
1041+
* - 0: Success
1042+
* - -errno: Otherwise
1043+
*/
1044+
static long sgx_ioc_enclave_modify_types(struct sgx_encl *encl,
1045+
void __user *arg)
1046+
{
1047+
struct sgx_enclave_modify_types params;
1048+
long ret;
1049+
1050+
ret = sgx_ioc_sgx2_ready(encl);
1051+
if (ret)
1052+
return ret;
1053+
1054+
if (copy_from_user(&params, arg, sizeof(params)))
1055+
return -EFAULT;
1056+
1057+
if (sgx_validate_offset_length(encl, params.offset, params.length))
1058+
return -EINVAL;
1059+
1060+
if (params.page_type & ~SGX_PAGE_TYPE_MASK)
1061+
return -EINVAL;
1062+
1063+
if (params.result || params.count)
1064+
return -EINVAL;
1065+
1066+
ret = sgx_enclave_modify_types(encl, &params);
1067+
1068+
if (copy_to_user(arg, &params, sizeof(params)))
1069+
return -EFAULT;
1070+
1071+
return ret;
1072+
}
1073+
8751074
long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
8761075
{
8771076
struct sgx_encl *encl = filep->private_data;
@@ -897,6 +1096,9 @@ long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
8971096
ret = sgx_ioc_enclave_restrict_permissions(encl,
8981097
(void __user *)arg);
8991098
break;
1099+
case SGX_IOC_ENCLAVE_MODIFY_TYPES:
1100+
ret = sgx_ioc_enclave_modify_types(encl, (void __user *)arg);
1101+
break;
9001102
default:
9011103
ret = -ENOIOCTLCMD;
9021104
break;

0 commit comments

Comments
 (0)