The most trivial example of a race condition can be demonstrated by this sequence where mm_list contains just one entry: CPU A CPU B -> sgx_release() -> sgx_mmu_notifier_release() -> list_del_rcu() <- list_del_rcu() -> kref_put() -> sgx_encl_release() -> synchronize_srcu() -> cleanup_srcu_struct() A sequence similar to this has also been spotted in tests under high stress: [ +0.000008] WARNING: CPU: 3 PID: 7620 at kernel/rcu/srcutree.c:374 cleanup_srcu_struct+0xed/0x100 Albeit not spotted in the tests, it's also entirely possible that the following scenario could happen: CPU A CPU B -> sgx_release() -> sgx_mmu_notifier_release() -> list_del_rcu() -> kref_put() -> sgx_encl_release() -> cleanup_srcu_struct() <- cleanup_srcu_struct() -> synchronize_srcu() This scenario would lead into use-after free in cleaup_srcu_struct(). Fix this by taking a reference to the enclave in sgx_mmu_notifier_release(). Cc: stable@xxxxxxxxxxxxxxx Fixes: 1728ab54b4be ("x86/sgx: Add a page reclaimer") Suggested-by: Sean Christopherson <seanjc@xxxxxxxxxx> Reported-by: Haitao Huang <haitao.huang@xxxxxxxxxxxxxxx> Signed-off-by: Jarkko Sakkinen <jarkko@xxxxxxxxxx> --- v5: - To make sure that the instance does not get deleted use kref_get() kref_put(). This also removes the need for additional synchronize_srcu(). v4: - Rewrite the commit message. - Just change the call order. *_expedited() is out of scope for this bug fix. v3: Fine-tuned tags, and added missing change log for v2. v2: Switch to synchronize_srcu_expedited(). arch/x86/kernel/cpu/sgx/encl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c index ee50a5010277..5ecbcf94ec2a 100644 --- a/arch/x86/kernel/cpu/sgx/encl.c +++ b/arch/x86/kernel/cpu/sgx/encl.c @@ -465,6 +465,7 @@ static void sgx_mmu_notifier_release(struct mmu_notifier *mn, spin_lock(&encl_mm->encl->mm_lock); list_for_each_entry(tmp, &encl_mm->encl->mm_list, list) { if (tmp == encl_mm) { + kref_get(&encl_mm->encl->refcount); list_del_rcu(&encl_mm->list); break; } @@ -474,6 +475,7 @@ static void sgx_mmu_notifier_release(struct mmu_notifier *mn, if (tmp == encl_mm) { synchronize_srcu(&encl_mm->encl->srcu); mmu_notifier_put(mn); + kref_put(&encl_mm->encl->refcount, sgx_encl_release); } } -- 2.30.0