aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Gunthorpe <jgg@mellanox.com>2019-05-24 11:14:08 -0400
committerJason Gunthorpe <jgg@mellanox.com>2019-06-24 16:38:18 -0400
commit14331726a3c47bb1649dab155a84610f509d414e (patch)
tree9dc1a6341e79bdaf3c3ac93bd6fa23ac8c316eae
parent2dcc3eb8ab50c9ca816cc60abfd94bea559d3e86 (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.c28
1 files changed, 9 insertions, 19 deletions
diff --git a/mm/hmm.c b/mm/hmm.c
index c30aa9403dbe..b224ea635a77 100644
--- a/mm/hmm.c
+++ b/mm/hmm.c
@@ -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}