diff options
author | Jason Gunthorpe <jgg@mellanox.com> | 2019-05-24 11:14:08 -0400 |
---|---|---|
committer | Jason Gunthorpe <jgg@mellanox.com> | 2019-06-24 16:38:18 -0400 |
commit | 14331726a3c47bb1649dab155a84610f509d414e (patch) | |
tree | 9dc1a6341e79bdaf3c3ac93bd6fa23ac8c316eae | |
parent | 2dcc3eb8ab50c9ca816cc60abfd94bea559d3e86 (diff) |
mm/hmm: Remove confusing comment and logic from hmm_release
hmm_release() is called exactly once per hmm. ops->release() cannot
accidentally trigger any action that would recurse back onto
hmm->mirrors_sem.
This fixes a use after-free race of the form:
CPU0 CPU1
hmm_release()
up_write(&hmm->mirrors_sem);
hmm_mirror_unregister(mirror)
down_write(&hmm->mirrors_sem);
up_write(&hmm->mirrors_sem);
kfree(mirror)
mirror->ops->release(mirror)
The only user we have today for ops->release is an empty function, so this
is unambiguously safe.
As a consequence of plugging this race drivers are not allowed to
register/unregister mirrors from within a release op.
Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Tested-by: Philip Yang <Philip.Yang@amd.com>
-rw-r--r-- | mm/hmm.c | 28 |
1 files changed, 9 insertions, 19 deletions
@@ -130,26 +130,16 @@ static void hmm_release(struct mmu_notifier *mn, struct mm_struct *mm) | |||
130 | */ | 130 | */ |
131 | WARN_ON(!list_empty_careful(&hmm->ranges)); | 131 | WARN_ON(!list_empty_careful(&hmm->ranges)); |
132 | 132 | ||
133 | down_write(&hmm->mirrors_sem); | 133 | down_read(&hmm->mirrors_sem); |
134 | mirror = list_first_entry_or_null(&hmm->mirrors, struct hmm_mirror, | 134 | list_for_each_entry(mirror, &hmm->mirrors, list) { |
135 | list); | 135 | /* |
136 | while (mirror) { | 136 | * Note: The driver is not allowed to trigger |
137 | list_del_init(&mirror->list); | 137 | * hmm_mirror_unregister() from this thread. |
138 | if (mirror->ops->release) { | 138 | */ |
139 | /* | 139 | if (mirror->ops->release) |
140 | * Drop mirrors_sem so the release callback can wait | ||
141 | * on any pending work that might itself trigger a | ||
142 | * mmu_notifier callback and thus would deadlock with | ||
143 | * us. | ||
144 | */ | ||
145 | up_write(&hmm->mirrors_sem); | ||
146 | mirror->ops->release(mirror); | 140 | mirror->ops->release(mirror); |
147 | down_write(&hmm->mirrors_sem); | ||
148 | } | ||
149 | mirror = list_first_entry_or_null(&hmm->mirrors, | ||
150 | struct hmm_mirror, list); | ||
151 | } | 141 | } |
152 | up_write(&hmm->mirrors_sem); | 142 | up_read(&hmm->mirrors_sem); |
153 | 143 | ||
154 | hmm_put(hmm); | 144 | hmm_put(hmm); |
155 | } | 145 | } |
@@ -279,7 +269,7 @@ void hmm_mirror_unregister(struct hmm_mirror *mirror) | |||
279 | struct hmm *hmm = mirror->hmm; | 269 | struct hmm *hmm = mirror->hmm; |
280 | 270 | ||
281 | down_write(&hmm->mirrors_sem); | 271 | down_write(&hmm->mirrors_sem); |
282 | list_del_init(&mirror->list); | 272 | list_del(&mirror->list); |
283 | up_write(&hmm->mirrors_sem); | 273 | up_write(&hmm->mirrors_sem); |
284 | hmm_put(hmm); | 274 | hmm_put(hmm); |
285 | } | 275 | } |