[PATCH] x86/sgx: Do not attempt to unmap enclave VMAs if mm_struct is defunct

From: Sean Christopherson
Date: Mon Dec 17 2018 - 12:21:14 EST


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 <sean.j.christopherson@xxxxxxxxx>
---
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


--IJpNTDwzlM2Ie8A6--