Skip to content

Commit 9849bb2

Browse files
rchatrehansendc
authored andcommitted
x86/sgx: Support complete page removal
The SGX2 page removal flow was introduced in previous patch and is as follows: 1) Change the type of the pages to be removed to SGX_PAGE_TYPE_TRIM using the ioctl() SGX_IOC_ENCLAVE_MODIFY_TYPES introduced in previous patch. 2) Approve the page removal by running ENCLU[EACCEPT] from within the enclave. 3) Initiate actual page removal using the ioctl() SGX_IOC_ENCLAVE_REMOVE_PAGES introduced here. Support the final step of the SGX2 page removal flow with ioctl() SGX_IOC_ENCLAVE_REMOVE_PAGES. With this ioctl() the user specifies a page range that should be removed. All pages in the provided range should have the SGX_PAGE_TYPE_TRIM page type and the request will fail with EPERM (Operation not permitted) if a page that does not have the correct type is encountered. Page removal can fail on any page within the provided range. Support partial success by returning the number of pages that were successfully removed. Since actual page removal will succeed even if ENCLU[EACCEPT] was not run from within the enclave the ENCLU[EMODPR] instruction with RWX permissions is used as a no-op mechanism to ensure ENCLU[EACCEPT] was successfully run from within the enclave before the enclave page is removed. If the user omits running SGX_IOC_ENCLAVE_REMOVE_PAGES the pages will still be removed when the enclave is unloaded. Signed-off-by: Reinette Chatre <[email protected]> Signed-off-by: Dave Hansen <[email protected]> Reviewed-by: Jarkko Sakkinen <[email protected]> Tested-by: Haitao Huang <[email protected]> Tested-by: Vijay Dhanraj <[email protected]> Tested-by: Jarkko Sakkinen <[email protected]> Link: https://lkml.kernel.org/r/b75ee93e96774e38bb44a24b8e9bbfb67b08b51b.1652137848.git.reinette.chatre@intel.com
1 parent 45d546b commit 9849bb2

File tree

2 files changed

+166
-0
lines changed

2 files changed

+166
-0
lines changed

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ enum sgx_page_flags {
3333
_IOWR(SGX_MAGIC, 0x05, struct sgx_enclave_restrict_permissions)
3434
#define SGX_IOC_ENCLAVE_MODIFY_TYPES \
3535
_IOWR(SGX_MAGIC, 0x06, struct sgx_enclave_modify_types)
36+
#define SGX_IOC_ENCLAVE_REMOVE_PAGES \
37+
_IOWR(SGX_MAGIC, 0x07, struct sgx_enclave_remove_pages)
3638

3739
/**
3840
* struct sgx_enclave_create - parameter structure for the
@@ -117,6 +119,25 @@ struct sgx_enclave_modify_types {
117119
__u64 count;
118120
};
119121

122+
/**
123+
* struct sgx_enclave_remove_pages - %SGX_IOC_ENCLAVE_REMOVE_PAGES parameters
124+
* @offset: starting page offset (page aligned relative to enclave base
125+
* address defined in SECS)
126+
* @length: length of memory (multiple of the page size)
127+
* @count: (output) bytes successfully changed (multiple of page size)
128+
*
129+
* Regular (PT_REG) or TCS (PT_TCS) can be removed from an initialized
130+
* enclave if the system supports SGX2. First, the %SGX_IOC_ENCLAVE_MODIFY_TYPES
131+
* ioctl() should be used to change the page type to PT_TRIM. After that
132+
* succeeds ENCLU[EACCEPT] should be run from within the enclave and then
133+
* %SGX_IOC_ENCLAVE_REMOVE_PAGES can be used to complete the page removal.
134+
*/
135+
struct sgx_enclave_remove_pages {
136+
__u64 offset;
137+
__u64 length;
138+
__u64 count;
139+
};
140+
120141
struct sgx_enclave_run;
121142

122143
/**

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

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,148 @@ static long sgx_ioc_enclave_modify_types(struct sgx_encl *encl,
10711071
return ret;
10721072
}
10731073

1074+
/**
1075+
* sgx_encl_remove_pages() - Remove trimmed pages from SGX enclave
1076+
* @encl: Enclave to which the pages belong
1077+
* @params: Checked parameters from user on which pages need to be removed
1078+
*
1079+
* Return:
1080+
* - 0: Success.
1081+
* - -errno: Otherwise.
1082+
*/
1083+
static long sgx_encl_remove_pages(struct sgx_encl *encl,
1084+
struct sgx_enclave_remove_pages *params)
1085+
{
1086+
struct sgx_encl_page *entry;
1087+
struct sgx_secinfo secinfo;
1088+
unsigned long addr;
1089+
unsigned long c;
1090+
void *epc_virt;
1091+
int ret;
1092+
1093+
memset(&secinfo, 0, sizeof(secinfo));
1094+
secinfo.flags = SGX_SECINFO_R | SGX_SECINFO_W | SGX_SECINFO_X;
1095+
1096+
for (c = 0 ; c < params->length; c += PAGE_SIZE) {
1097+
addr = encl->base + params->offset + c;
1098+
1099+
mutex_lock(&encl->lock);
1100+
1101+
entry = sgx_encl_load_page(encl, addr);
1102+
if (IS_ERR(entry)) {
1103+
ret = PTR_ERR(entry) == -EBUSY ? -EAGAIN : -EFAULT;
1104+
goto out_unlock;
1105+
}
1106+
1107+
if (entry->type != SGX_PAGE_TYPE_TRIM) {
1108+
ret = -EPERM;
1109+
goto out_unlock;
1110+
}
1111+
1112+
/*
1113+
* ENCLS[EMODPR] is a no-op instruction used to inform if
1114+
* ENCLU[EACCEPT] was run from within the enclave. If
1115+
* ENCLS[EMODPR] is run with RWX on a trimmed page that is
1116+
* not yet accepted then it will return
1117+
* %SGX_PAGE_NOT_MODIFIABLE, after the trimmed page is
1118+
* accepted the instruction will encounter a page fault.
1119+
*/
1120+
epc_virt = sgx_get_epc_virt_addr(entry->epc_page);
1121+
ret = __emodpr(&secinfo, epc_virt);
1122+
if (!encls_faulted(ret) || ENCLS_TRAPNR(ret) != X86_TRAP_PF) {
1123+
ret = -EPERM;
1124+
goto out_unlock;
1125+
}
1126+
1127+
if (sgx_unmark_page_reclaimable(entry->epc_page)) {
1128+
ret = -EBUSY;
1129+
goto out_unlock;
1130+
}
1131+
1132+
/*
1133+
* Do not keep encl->lock because of dependency on
1134+
* mmap_lock acquired in sgx_zap_enclave_ptes().
1135+
*/
1136+
mutex_unlock(&encl->lock);
1137+
1138+
sgx_zap_enclave_ptes(encl, addr);
1139+
1140+
mutex_lock(&encl->lock);
1141+
1142+
sgx_encl_free_epc_page(entry->epc_page);
1143+
encl->secs_child_cnt--;
1144+
entry->epc_page = NULL;
1145+
xa_erase(&encl->page_array, PFN_DOWN(entry->desc));
1146+
sgx_encl_shrink(encl, NULL);
1147+
kfree(entry);
1148+
1149+
mutex_unlock(&encl->lock);
1150+
}
1151+
1152+
ret = 0;
1153+
goto out;
1154+
1155+
out_unlock:
1156+
mutex_unlock(&encl->lock);
1157+
out:
1158+
params->count = c;
1159+
1160+
return ret;
1161+
}
1162+
1163+
/**
1164+
* sgx_ioc_enclave_remove_pages() - handler for %SGX_IOC_ENCLAVE_REMOVE_PAGES
1165+
* @encl: an enclave pointer
1166+
* @arg: userspace pointer to &struct sgx_enclave_remove_pages instance
1167+
*
1168+
* Final step of the flow removing pages from an initialized enclave. The
1169+
* complete flow is:
1170+
*
1171+
* 1) User changes the type of the pages to be removed to %SGX_PAGE_TYPE_TRIM
1172+
* using the %SGX_IOC_ENCLAVE_MODIFY_TYPES ioctl().
1173+
* 2) User approves the page removal by running ENCLU[EACCEPT] from within
1174+
* the enclave.
1175+
* 3) User initiates actual page removal using the
1176+
* %SGX_IOC_ENCLAVE_REMOVE_PAGES ioctl() that is handled here.
1177+
*
1178+
* First remove any page table entries pointing to the page and then proceed
1179+
* with the actual removal of the enclave page and data in support of it.
1180+
*
1181+
* VA pages are not affected by this removal. It is thus possible that the
1182+
* enclave may end up with more VA pages than needed to support all its
1183+
* pages.
1184+
*
1185+
* Return:
1186+
* - 0: Success
1187+
* - -errno: Otherwise
1188+
*/
1189+
static long sgx_ioc_enclave_remove_pages(struct sgx_encl *encl,
1190+
void __user *arg)
1191+
{
1192+
struct sgx_enclave_remove_pages params;
1193+
long ret;
1194+
1195+
ret = sgx_ioc_sgx2_ready(encl);
1196+
if (ret)
1197+
return ret;
1198+
1199+
if (copy_from_user(&params, arg, sizeof(params)))
1200+
return -EFAULT;
1201+
1202+
if (sgx_validate_offset_length(encl, params.offset, params.length))
1203+
return -EINVAL;
1204+
1205+
if (params.count)
1206+
return -EINVAL;
1207+
1208+
ret = sgx_encl_remove_pages(encl, &params);
1209+
1210+
if (copy_to_user(arg, &params, sizeof(params)))
1211+
return -EFAULT;
1212+
1213+
return ret;
1214+
}
1215+
10741216
long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
10751217
{
10761218
struct sgx_encl *encl = filep->private_data;
@@ -1099,6 +1241,9 @@ long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
10991241
case SGX_IOC_ENCLAVE_MODIFY_TYPES:
11001242
ret = sgx_ioc_enclave_modify_types(encl, (void __user *)arg);
11011243
break;
1244+
case SGX_IOC_ENCLAVE_REMOVE_PAGES:
1245+
ret = sgx_ioc_enclave_remove_pages(encl, (void __user *)arg);
1246+
break;
11021247
default:
11031248
ret = -ENOIOCTLCMD;
11041249
break;

0 commit comments

Comments
 (0)