Skip to content

Commit f1ce398

Browse files
minipli-ossgregkh
authored andcommitted
nitro_enclaves: Fix stale file descriptors on failed usercopy
A failing usercopy of the slot uid will lead to a stale entry in the file descriptor table as put_unused_fd() won't release it. This enables userland to refer to a dangling 'file' object through that still valid file descriptor, leading to all kinds of use-after-free exploitation scenarios. Exchanging put_unused_fd() for close_fd(), ksys_close() or alike won't solve the underlying issue, as the file descriptor might have been replaced in the meantime, e.g. via userland calling close() on it (leading to a NULL pointer dereference in the error handling code as 'fget(enclave_fd)' will return a NULL pointer) or by dup2()'ing a completely different file object to that very file descriptor, leading to the same situation: a dangling file descriptor pointing to a freed object -- just in this case to a file object of user's choosing. Generally speaking, after the call to fd_install() the file descriptor is live and userland is free to do whatever with it. We cannot rely on it to still refer to our enclave object afterwards. In fact, by abusing userfaultfd() userland can hit the condition without any racing and abuse the error handling in the nitro code as it pleases. To fix the above issues, defer the call to fd_install() until all possible errors are handled. In this case it's just the usercopy, so do it directly in ne_create_vm_ioctl() itself. Signed-off-by: Mathias Krause <minipli@grsecurity.net> Signed-off-by: Andra Paraschiv <andraprs@amazon.com> Cc: stable <stable@vger.kernel.org> Link: https://lore.kernel.org/r/20210429165941.27020-2-andraprs@amazon.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 8e3a324 commit f1ce398

1 file changed

Lines changed: 17 additions & 26 deletions

File tree

drivers/virt/nitro_enclaves/ne_misc_dev.c

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1524,15 +1524,16 @@ static const struct file_operations ne_enclave_fops = {
15241524
* enclave file descriptor to be further used for enclave
15251525
* resources handling e.g. memory regions and CPUs.
15261526
* @ne_pci_dev : Private data associated with the PCI device.
1527-
* @slot_uid: Generated unique slot id associated with an enclave.
1527+
* @slot_uid: User pointer to store the generated unique slot id
1528+
* associated with an enclave to.
15281529
*
15291530
* Context: Process context. This function is called with the ne_pci_dev enclave
15301531
* mutex held.
15311532
* Return:
15321533
* * Enclave fd on success.
15331534
* * Negative return value on failure.
15341535
*/
1535-
static int ne_create_vm_ioctl(struct ne_pci_dev *ne_pci_dev, u64 *slot_uid)
1536+
static int ne_create_vm_ioctl(struct ne_pci_dev *ne_pci_dev, u64 __user *slot_uid)
15361537
{
15371538
struct ne_pci_dev_cmd_reply cmd_reply = {};
15381539
int enclave_fd = -1;
@@ -1634,7 +1635,18 @@ static int ne_create_vm_ioctl(struct ne_pci_dev *ne_pci_dev, u64 *slot_uid)
16341635

16351636
list_add(&ne_enclave->enclave_list_entry, &ne_pci_dev->enclaves_list);
16361637

1637-
*slot_uid = ne_enclave->slot_uid;
1638+
if (copy_to_user(slot_uid, &ne_enclave->slot_uid, sizeof(ne_enclave->slot_uid))) {
1639+
/*
1640+
* As we're holding the only reference to 'enclave_file', fput()
1641+
* will call ne_enclave_release() which will do a proper cleanup
1642+
* of all so far allocated resources, leaving only the unused fd
1643+
* for us to free.
1644+
*/
1645+
fput(enclave_file);
1646+
put_unused_fd(enclave_fd);
1647+
1648+
return -EFAULT;
1649+
}
16381650

16391651
fd_install(enclave_fd, enclave_file);
16401652

@@ -1671,34 +1683,13 @@ static long ne_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
16711683
switch (cmd) {
16721684
case NE_CREATE_VM: {
16731685
int enclave_fd = -1;
1674-
struct file *enclave_file = NULL;
16751686
struct ne_pci_dev *ne_pci_dev = ne_devs.ne_pci_dev;
1676-
int rc = -EINVAL;
1677-
u64 slot_uid = 0;
1687+
u64 __user *slot_uid = (void __user *)arg;
16781688

16791689
mutex_lock(&ne_pci_dev->enclaves_list_mutex);
1680-
1681-
enclave_fd = ne_create_vm_ioctl(ne_pci_dev, &slot_uid);
1682-
if (enclave_fd < 0) {
1683-
rc = enclave_fd;
1684-
1685-
mutex_unlock(&ne_pci_dev->enclaves_list_mutex);
1686-
1687-
return rc;
1688-
}
1689-
1690+
enclave_fd = ne_create_vm_ioctl(ne_pci_dev, slot_uid);
16901691
mutex_unlock(&ne_pci_dev->enclaves_list_mutex);
16911692

1692-
if (copy_to_user((void __user *)arg, &slot_uid, sizeof(slot_uid))) {
1693-
enclave_file = fget(enclave_fd);
1694-
/* Decrement file refs to have release() called. */
1695-
fput(enclave_file);
1696-
fput(enclave_file);
1697-
put_unused_fd(enclave_fd);
1698-
1699-
return -EFAULT;
1700-
}
1701-
17021693
return enclave_fd;
17031694
}
17041695

0 commit comments

Comments
 (0)