Skip to content

Commit 879dbe9

Browse files
committed
Merge tag 'x86_sgx_for_v5.16_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 SGX updates from Borislav Petkov: "Add a SGX_IOC_VEPC_REMOVE ioctl to the /dev/sgx_vepc virt interface with which EPC pages can be put back into their uninitialized state without having to reopen /dev/sgx_vepc, which could not be possible anymore after startup due to security policies" * tag 'x86_sgx_for_v5.16_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/sgx/virt: implement SGX_IOC_VEPC_REMOVE ioctl x86/sgx/virt: extract sgx_vepc_remove_page
2 parents 20273d2 + ae095b1 commit 879dbe9

File tree

3 files changed

+97
-5
lines changed

3 files changed

+97
-5
lines changed

Documentation/x86/sgx.rst

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,3 +250,38 @@ user wants to deploy SGX applications both on the host and in guests
250250
on the same machine, the user should reserve enough EPC (by taking out
251251
total virtual EPC size of all SGX VMs from the physical EPC size) for
252252
host SGX applications so they can run with acceptable performance.
253+
254+
Architectural behavior is to restore all EPC pages to an uninitialized
255+
state also after a guest reboot. Because this state can be reached only
256+
through the privileged ``ENCLS[EREMOVE]`` instruction, ``/dev/sgx_vepc``
257+
provides the ``SGX_IOC_VEPC_REMOVE_ALL`` ioctl to execute the instruction
258+
on all pages in the virtual EPC.
259+
260+
``EREMOVE`` can fail for three reasons. Userspace must pay attention
261+
to expected failures and handle them as follows:
262+
263+
1. Page removal will always fail when any thread is running in the
264+
enclave to which the page belongs. In this case the ioctl will
265+
return ``EBUSY`` independent of whether it has successfully removed
266+
some pages; userspace can avoid these failures by preventing execution
267+
of any vcpu which maps the virtual EPC.
268+
269+
2. Page removal will cause a general protection fault if two calls to
270+
``EREMOVE`` happen concurrently for pages that refer to the same
271+
"SECS" metadata pages. This can happen if there are concurrent
272+
invocations to ``SGX_IOC_VEPC_REMOVE_ALL``, or if a ``/dev/sgx_vepc``
273+
file descriptor in the guest is closed at the same time as
274+
``SGX_IOC_VEPC_REMOVE_ALL``; it will also be reported as ``EBUSY``.
275+
This can be avoided in userspace by serializing calls to the ioctl()
276+
and to close(), but in general it should not be a problem.
277+
278+
3. Finally, page removal will fail for SECS metadata pages which still
279+
have child pages. Child pages can be removed by executing
280+
``SGX_IOC_VEPC_REMOVE_ALL`` on all ``/dev/sgx_vepc`` file descriptors
281+
mapped into the guest. This means that the ioctl() must be called
282+
twice: an initial set of calls to remove child pages and a subsequent
283+
set of calls to remove SECS pages. The second set of calls is only
284+
required for those mappings that returned a nonzero value from the
285+
first call. It indicates a bug in the kernel or the userspace client
286+
if any of the second round of ``SGX_IOC_VEPC_REMOVE_ALL`` calls has
287+
a return code other than 0.

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ enum sgx_page_flags {
2727
_IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init)
2828
#define SGX_IOC_ENCLAVE_PROVISION \
2929
_IOW(SGX_MAGIC, 0x03, struct sgx_enclave_provision)
30+
#define SGX_IOC_VEPC_REMOVE_ALL \
31+
_IO(SGX_MAGIC, 0x04)
3032

3133
/**
3234
* struct sgx_enclave_create - parameter structure for the

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

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,8 @@ static int sgx_vepc_mmap(struct file *file, struct vm_area_struct *vma)
111111
return 0;
112112
}
113113

114-
static int sgx_vepc_free_page(struct sgx_epc_page *epc_page)
114+
static int sgx_vepc_remove_page(struct sgx_epc_page *epc_page)
115115
{
116-
int ret;
117-
118116
/*
119117
* Take a previously guest-owned EPC page and return it to the
120118
* general EPC page pool.
@@ -124,7 +122,12 @@ static int sgx_vepc_free_page(struct sgx_epc_page *epc_page)
124122
* case that a guest properly EREMOVE'd this page, a superfluous
125123
* EREMOVE is harmless.
126124
*/
127-
ret = __eremove(sgx_get_epc_virt_addr(epc_page));
125+
return __eremove(sgx_get_epc_virt_addr(epc_page));
126+
}
127+
128+
static int sgx_vepc_free_page(struct sgx_epc_page *epc_page)
129+
{
130+
int ret = sgx_vepc_remove_page(epc_page);
128131
if (ret) {
129132
/*
130133
* Only SGX_CHILD_PRESENT is expected, which is because of
@@ -144,10 +147,44 @@ static int sgx_vepc_free_page(struct sgx_epc_page *epc_page)
144147
}
145148

146149
sgx_free_epc_page(epc_page);
147-
148150
return 0;
149151
}
150152

153+
static long sgx_vepc_remove_all(struct sgx_vepc *vepc)
154+
{
155+
struct sgx_epc_page *entry;
156+
unsigned long index;
157+
long failures = 0;
158+
159+
xa_for_each(&vepc->page_array, index, entry) {
160+
int ret = sgx_vepc_remove_page(entry);
161+
if (ret) {
162+
if (ret == SGX_CHILD_PRESENT) {
163+
/* The page is a SECS, userspace will retry. */
164+
failures++;
165+
} else {
166+
/*
167+
* Report errors due to #GP or SGX_ENCLAVE_ACT; do not
168+
* WARN, as userspace can induce said failures by
169+
* calling the ioctl concurrently on multiple vEPCs or
170+
* while one or more CPUs is running the enclave. Only
171+
* a #PF on EREMOVE indicates a kernel/hardware issue.
172+
*/
173+
WARN_ON_ONCE(encls_faulted(ret) &&
174+
ENCLS_TRAPNR(ret) != X86_TRAP_GP);
175+
return -EBUSY;
176+
}
177+
}
178+
cond_resched();
179+
}
180+
181+
/*
182+
* Return the number of SECS pages that failed to be removed, so
183+
* userspace knows that it has to retry.
184+
*/
185+
return failures;
186+
}
187+
151188
static int sgx_vepc_release(struct inode *inode, struct file *file)
152189
{
153190
struct sgx_vepc *vepc = file->private_data;
@@ -233,9 +270,27 @@ static int sgx_vepc_open(struct inode *inode, struct file *file)
233270
return 0;
234271
}
235272

273+
static long sgx_vepc_ioctl(struct file *file,
274+
unsigned int cmd, unsigned long arg)
275+
{
276+
struct sgx_vepc *vepc = file->private_data;
277+
278+
switch (cmd) {
279+
case SGX_IOC_VEPC_REMOVE_ALL:
280+
if (arg)
281+
return -EINVAL;
282+
return sgx_vepc_remove_all(vepc);
283+
284+
default:
285+
return -ENOTTY;
286+
}
287+
}
288+
236289
static const struct file_operations sgx_vepc_fops = {
237290
.owner = THIS_MODULE,
238291
.open = sgx_vepc_open,
292+
.unlocked_ioctl = sgx_vepc_ioctl,
293+
.compat_ioctl = sgx_vepc_ioctl,
239294
.release = sgx_vepc_release,
240295
.mmap = sgx_vepc_mmap,
241296
};

0 commit comments

Comments
 (0)