Skip to content

Commit

Permalink
mm/hmm: fix race between hmm_mirror_unregister() and mmu_notifier cal…
Browse files Browse the repository at this point in the history
…lback

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/[email protected]
Signed-off-by: Ralph Campbell <[email protected]>
Signed-off-by: Jérôme Glisse <[email protected]>
Reviewed-by: John Hubbard <[email protected]>
Reviewed-by: Jérôme Glisse <[email protected]>
Reviewed-by: Balbir Singh <[email protected]>
Cc: <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Ralph Campbell authored and torvalds committed Oct 31, 2018
1 parent aab8d05 commit 86a2d59
Showing 1 changed file with 21 additions and 15 deletions.
36 changes: 21 additions & 15 deletions mm/hmm.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,29 +91,34 @@ static struct hmm *hmm_register(struct mm_struct *mm)
spin_lock_init(&hmm->lock);
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);
if (!mm->hmm)
mm->hmm = hmm;
else
cleanup = true;
spin_unlock(&mm->page_table_lock);

if (cleanup) {
mmu_notifier_unregister(&hmm->mmu_notifier, mm);
kfree(hmm);
}
if (cleanup)
goto error;

/*
* 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;

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)
Expand Down Expand Up @@ -278,12 +283,13 @@ void hmm_mirror_unregister(struct hmm_mirror *mirror)
if (!should_unregister || mm == NULL)
return;

mmu_notifier_unregister_no_release(&hmm->mmu_notifier, mm);

spin_lock(&mm->page_table_lock);
if (mm->hmm == hmm)
mm->hmm = NULL;
spin_unlock(&mm->page_table_lock);

mmu_notifier_unregister_no_release(&hmm->mmu_notifier, mm);
kfree(hmm);
}
EXPORT_SYMBOL(hmm_mirror_unregister);
Expand Down

0 comments on commit 86a2d59

Please sign in to comment.