>From 7cfdf34ec5b70392216b24853d6b8cc5e3192a92 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 17 Dec 2018 09:21:14 -0800 Subject: [PATCH] x86/sgx: Do not attempt to unmap enclave VMAs if mm_struct is defunct Add a flag, SGX_ENCL_MM_RELEASED, to explicitly track the lifecycle of the enclave's associated mm_struct. Simply ensuring the mm_struct itself is valid is not sufficient as the VMAs and page tables can be removed after sgx_mmu_notifier_release() is invoked[1]. Note that this means mmu_notifier can't be unregistered until after do_unmap(), but that's true no matter what since the mmu_notifier holds the enclave's reference to mm_struct, i.e. this also fixes a potential use-after-free bug of the mm_struct. [1] https://www.spinics.net/lists/dri-devel/msg186827.html Signed-off-by: Sean Christopherson --- arch/x86/kernel/cpu/sgx/driver/driver.h | 1 + arch/x86/kernel/cpu/sgx/driver/encl.c | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/arch/x86/kernel/cpu/sgx/driver/driver.h b/arch/x86/kernel/cpu/sgx/driver/driver.h index 56f45cd433dd..d7c51284ef36 100644 --- a/arch/x86/kernel/cpu/sgx/driver/driver.h +++ b/arch/x86/kernel/cpu/sgx/driver/driver.h @@ -89,6 +89,7 @@ enum sgx_encl_flags { SGX_ENCL_DEBUG = BIT(1), SGX_ENCL_SUSPEND = BIT(2), SGX_ENCL_DEAD = BIT(3), + SGX_ENCL_MM_RELEASED = BIT(4), }; struct sgx_encl { diff --git a/arch/x86/kernel/cpu/sgx/driver/encl.c b/arch/x86/kernel/cpu/sgx/driver/encl.c index 923e31eb6552..77c5e65533fb 100644 --- a/arch/x86/kernel/cpu/sgx/driver/encl.c +++ b/arch/x86/kernel/cpu/sgx/driver/encl.c @@ -311,7 +311,7 @@ static void sgx_mmu_notifier_release(struct mmu_notifier *mn, container_of(mn, struct sgx_encl, mmu_notifier); mutex_lock(&encl->lock); - encl->flags |= SGX_ENCL_DEAD; + encl->flags |= SGX_ENCL_DEAD | SGX_ENCL_MM_RELEASED; mutex_unlock(&encl->lock); } @@ -967,10 +967,15 @@ static void sgx_encl_release_worker(struct work_struct *work) struct sgx_encl *encl = container_of(work, struct sgx_encl, work); unsigned long backing_size = encl->size + PAGE_SIZE; - down_write(&encl->mm->mmap_sem); - do_munmap(encl->mm, (unsigned long)encl->backing, backing_size + - (backing_size >> 5), NULL); - up_write(&encl->mm->mmap_sem); + if (!(encl->flags & SGX_ENCL_MM_RELEASED)) { + down_write(&encl->mm->mmap_sem); + do_munmap(encl->mm, (unsigned long)encl->backing, + backing_size + (backing_size >> 5), NULL); + up_write(&encl->mm->mmap_sem); + } + + if (encl->mmu_notifier.ops) + mmu_notifier_unregister(&encl->mmu_notifier, encl->mm); if (encl->tgid) put_pid(encl->tgid); @@ -990,9 +995,6 @@ void sgx_encl_release(struct kref *ref) { struct sgx_encl *encl = container_of(ref, struct sgx_encl, refcount); - if (encl->mmu_notifier.ops) - mmu_notifier_unregister(&encl->mmu_notifier, encl->mm); - if (encl->pm_notifier.notifier_call) unregister_pm_notifier(&encl->pm_notifier); -- 2.19.2