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. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEzv7L6UO9uDPlPSfHEsHwGGHeVUoFAmF/x7AACgkQEsHwGGHe VUqHXA//YWrukmJ5PQZwWqkXGo6h42JWhIdNfSC2c1SVdz1cioGUCCswALTX4g8l MYYf3eN12GJ296jPh7m9bz8JvlYjdavSm3Y1yzHIjuQ3q6qywHIuYTbsrMD7waUD PkcY1TTYgNJ2+f0AgsC4GZhlcpf9g5DqiftW6wvExx5tLUNsVu3Y3gZy/+fajP4f s/TMjcdr2QmPsjun00KfoIY4/z0u8LkyRMSwyoxSV6wYdL6rRtfYFWsbEUS+W6Nw /VJ0IKl+aBQ1ztsDc4M5h1uy9II2M/Row5k6JjyrdG4X8D6ACSG7cho6qcMjXgcP Gac7Im5IyjPEorxqXAgJiMoAl9lU9a2JMVZqPtihYsQW/ygMTdpzP9sBpcZPMevc gxQD4gyixwzUa3cyVDzTPBdk/DEuGc2nwn2k9nPvmNxKMonX1oLEiP7hu265mvet 56DtwKJF9ddtpepO2zFCg1qX+eZnTuhuZNCPsm/pmdGgzI8cyLznho33OgUSZEQY c1UisT7HXNRVC/1Q8VBDTU/D9LtIk+2+Q5lQkcNeftI5PYKTXIVddkOkqJ4GhGWJ 9EasA4UtnhvsLzJ76gxxuUf677ns+1TCo65e7Hu1+X0eTmBJK3boe3aMHvJeHEWH Asd+SMkYWfxAlW/arAYhR2JgT9wgEH3pSx4eXnpGwpeValxBPRs= =1UYy -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
879dbe9ffe
@ -250,3 +250,38 @@ user wants to deploy SGX applications both on the host and in guests
|
||||
on the same machine, the user should reserve enough EPC (by taking out
|
||||
total virtual EPC size of all SGX VMs from the physical EPC size) for
|
||||
host SGX applications so they can run with acceptable performance.
|
||||
|
||||
Architectural behavior is to restore all EPC pages to an uninitialized
|
||||
state also after a guest reboot. Because this state can be reached only
|
||||
through the privileged ``ENCLS[EREMOVE]`` instruction, ``/dev/sgx_vepc``
|
||||
provides the ``SGX_IOC_VEPC_REMOVE_ALL`` ioctl to execute the instruction
|
||||
on all pages in the virtual EPC.
|
||||
|
||||
``EREMOVE`` can fail for three reasons. Userspace must pay attention
|
||||
to expected failures and handle them as follows:
|
||||
|
||||
1. Page removal will always fail when any thread is running in the
|
||||
enclave to which the page belongs. In this case the ioctl will
|
||||
return ``EBUSY`` independent of whether it has successfully removed
|
||||
some pages; userspace can avoid these failures by preventing execution
|
||||
of any vcpu which maps the virtual EPC.
|
||||
|
||||
2. Page removal will cause a general protection fault if two calls to
|
||||
``EREMOVE`` happen concurrently for pages that refer to the same
|
||||
"SECS" metadata pages. This can happen if there are concurrent
|
||||
invocations to ``SGX_IOC_VEPC_REMOVE_ALL``, or if a ``/dev/sgx_vepc``
|
||||
file descriptor in the guest is closed at the same time as
|
||||
``SGX_IOC_VEPC_REMOVE_ALL``; it will also be reported as ``EBUSY``.
|
||||
This can be avoided in userspace by serializing calls to the ioctl()
|
||||
and to close(), but in general it should not be a problem.
|
||||
|
||||
3. Finally, page removal will fail for SECS metadata pages which still
|
||||
have child pages. Child pages can be removed by executing
|
||||
``SGX_IOC_VEPC_REMOVE_ALL`` on all ``/dev/sgx_vepc`` file descriptors
|
||||
mapped into the guest. This means that the ioctl() must be called
|
||||
twice: an initial set of calls to remove child pages and a subsequent
|
||||
set of calls to remove SECS pages. The second set of calls is only
|
||||
required for those mappings that returned a nonzero value from the
|
||||
first call. It indicates a bug in the kernel or the userspace client
|
||||
if any of the second round of ``SGX_IOC_VEPC_REMOVE_ALL`` calls has
|
||||
a return code other than 0.
|
||||
|
@ -27,6 +27,8 @@ enum sgx_page_flags {
|
||||
_IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init)
|
||||
#define SGX_IOC_ENCLAVE_PROVISION \
|
||||
_IOW(SGX_MAGIC, 0x03, struct sgx_enclave_provision)
|
||||
#define SGX_IOC_VEPC_REMOVE_ALL \
|
||||
_IO(SGX_MAGIC, 0x04)
|
||||
|
||||
/**
|
||||
* struct sgx_enclave_create - parameter structure for the
|
||||
|
@ -111,10 +111,8 @@ static int sgx_vepc_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sgx_vepc_free_page(struct sgx_epc_page *epc_page)
|
||||
static int sgx_vepc_remove_page(struct sgx_epc_page *epc_page)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Take a previously guest-owned EPC page and return it to the
|
||||
* general EPC page pool.
|
||||
@ -124,7 +122,12 @@ static int sgx_vepc_free_page(struct sgx_epc_page *epc_page)
|
||||
* case that a guest properly EREMOVE'd this page, a superfluous
|
||||
* EREMOVE is harmless.
|
||||
*/
|
||||
ret = __eremove(sgx_get_epc_virt_addr(epc_page));
|
||||
return __eremove(sgx_get_epc_virt_addr(epc_page));
|
||||
}
|
||||
|
||||
static int sgx_vepc_free_page(struct sgx_epc_page *epc_page)
|
||||
{
|
||||
int ret = sgx_vepc_remove_page(epc_page);
|
||||
if (ret) {
|
||||
/*
|
||||
* 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)
|
||||
}
|
||||
|
||||
sgx_free_epc_page(epc_page);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long sgx_vepc_remove_all(struct sgx_vepc *vepc)
|
||||
{
|
||||
struct sgx_epc_page *entry;
|
||||
unsigned long index;
|
||||
long failures = 0;
|
||||
|
||||
xa_for_each(&vepc->page_array, index, entry) {
|
||||
int ret = sgx_vepc_remove_page(entry);
|
||||
if (ret) {
|
||||
if (ret == SGX_CHILD_PRESENT) {
|
||||
/* The page is a SECS, userspace will retry. */
|
||||
failures++;
|
||||
} else {
|
||||
/*
|
||||
* Report errors due to #GP or SGX_ENCLAVE_ACT; do not
|
||||
* WARN, as userspace can induce said failures by
|
||||
* calling the ioctl concurrently on multiple vEPCs or
|
||||
* while one or more CPUs is running the enclave. Only
|
||||
* a #PF on EREMOVE indicates a kernel/hardware issue.
|
||||
*/
|
||||
WARN_ON_ONCE(encls_faulted(ret) &&
|
||||
ENCLS_TRAPNR(ret) != X86_TRAP_GP);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of SECS pages that failed to be removed, so
|
||||
* userspace knows that it has to retry.
|
||||
*/
|
||||
return failures;
|
||||
}
|
||||
|
||||
static int sgx_vepc_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct sgx_vepc *vepc = file->private_data;
|
||||
@ -233,9 +270,27 @@ static int sgx_vepc_open(struct inode *inode, struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long sgx_vepc_ioctl(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct sgx_vepc *vepc = file->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SGX_IOC_VEPC_REMOVE_ALL:
|
||||
if (arg)
|
||||
return -EINVAL;
|
||||
return sgx_vepc_remove_all(vepc);
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct file_operations sgx_vepc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = sgx_vepc_open,
|
||||
.unlocked_ioctl = sgx_vepc_ioctl,
|
||||
.compat_ioctl = sgx_vepc_ioctl,
|
||||
.release = sgx_vepc_release,
|
||||
.mmap = sgx_vepc_mmap,
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user