Commit 86a2d598 authored by Ralph Campbell's avatar Ralph Campbell Committed by Linus Torvalds

mm/hmm: fix race between hmm_mirror_unregister() and mmu_notifier callback

In hmm_mirror_unregister(), mm->hmm is set to NULL and then
mmu_notifier_unregister_no_release() is called.  That creates a small
window where mmu_notifier can call mmu_notifier_ops with mm->hmm equal to
NULL.  Fix this by first unregistering mmu notifier callbacks and then
setting mm->hmm to NULL.

Similarly in hmm_register(), set mm->hmm before registering mmu_notifier
callbacks so callback functions always see mm->hmm set.

Link: http://lkml.kernel.org/r/20181019160442.18723-4-jglisse@redhat.comSigned-off-by: default avatarRalph Campbell <rcampbell@nvidia.com>
Signed-off-by: default avatarJérôme Glisse <jglisse@redhat.com>
Reviewed-by: default avatarJohn Hubbard <jhubbard@nvidia.com>
Reviewed-by: default avatarJérôme Glisse <jglisse@redhat.com>
Reviewed-by: default avatarBalbir Singh <bsingharora@gmail.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent aab8d052
...@@ -91,16 +91,6 @@ static struct hmm *hmm_register(struct mm_struct *mm) ...@@ -91,16 +91,6 @@ static struct hmm *hmm_register(struct mm_struct *mm)
spin_lock_init(&hmm->lock); spin_lock_init(&hmm->lock);
hmm->mm = mm; hmm->mm = mm;
/*
* We should only get here if hold the mmap_sem in write mode ie on
* registration of first mirror through hmm_mirror_register()
*/
hmm->mmu_notifier.ops = &hmm_mmu_notifier_ops;
if (__mmu_notifier_register(&hmm->mmu_notifier, mm)) {
kfree(hmm);
return NULL;
}
spin_lock(&mm->page_table_lock); spin_lock(&mm->page_table_lock);
if (!mm->hmm) if (!mm->hmm)
mm->hmm = hmm; mm->hmm = hmm;
...@@ -108,12 +98,27 @@ static struct hmm *hmm_register(struct mm_struct *mm) ...@@ -108,12 +98,27 @@ static struct hmm *hmm_register(struct mm_struct *mm)
cleanup = true; cleanup = true;
spin_unlock(&mm->page_table_lock); spin_unlock(&mm->page_table_lock);
if (cleanup) { if (cleanup)
mmu_notifier_unregister(&hmm->mmu_notifier, mm); goto error;
kfree(hmm);
} /*
* We should only get here if hold the mmap_sem in write mode ie on
* registration of first mirror through hmm_mirror_register()
*/
hmm->mmu_notifier.ops = &hmm_mmu_notifier_ops;
if (__mmu_notifier_register(&hmm->mmu_notifier, mm))
goto error_mm;
return mm->hmm; return mm->hmm;
error_mm:
spin_lock(&mm->page_table_lock);
if (mm->hmm == hmm)
mm->hmm = NULL;
spin_unlock(&mm->page_table_lock);
error:
kfree(hmm);
return NULL;
} }
void hmm_mm_destroy(struct mm_struct *mm) void hmm_mm_destroy(struct mm_struct *mm)
...@@ -278,12 +283,13 @@ void hmm_mirror_unregister(struct hmm_mirror *mirror) ...@@ -278,12 +283,13 @@ void hmm_mirror_unregister(struct hmm_mirror *mirror)
if (!should_unregister || mm == NULL) if (!should_unregister || mm == NULL)
return; return;
mmu_notifier_unregister_no_release(&hmm->mmu_notifier, mm);
spin_lock(&mm->page_table_lock); spin_lock(&mm->page_table_lock);
if (mm->hmm == hmm) if (mm->hmm == hmm)
mm->hmm = NULL; mm->hmm = NULL;
spin_unlock(&mm->page_table_lock); spin_unlock(&mm->page_table_lock);
mmu_notifier_unregister_no_release(&hmm->mmu_notifier, mm);
kfree(hmm); kfree(hmm);
} }
EXPORT_SYMBOL(hmm_mirror_unregister); EXPORT_SYMBOL(hmm_mirror_unregister);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment