aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2017-10-30 16:14:56 -0400
committerJan Kara <jack@suse.cz>2017-10-31 12:54:56 -0400
commitf37650f1c7c71cf5180b43229d13b421d81e7170 (patch)
treeff7aab8765170e57aaa8eb117b9c899017b02fe6
parent9a31d7ad997f55768c687974ce36b759065b49e5 (diff)
fanotify: fix fsnotify_prepare_user_wait() failure
If fsnotify_prepare_user_wait() fails, we leave the event on the notification list. Which will result in a warning in fsnotify_destroy_event() and later use-after-free. Instead of adding a new helper to remove the event from the list in this case, I opted to move the prepare/finish up into fanotify_handle_event(). This will allow these to be moved further out into the generic code later, and perhaps let us move to non-sleeping RCU. Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> Fixes: 05f0e38724e8 ("fanotify: Release SRCU lock when waiting for userspace response") Cc: <stable@vger.kernel.org> # v4.12 Signed-off-by: Jan Kara <jack@suse.cz>
-rw-r--r--fs/notify/fanotify/fanotify.c33
1 files changed, 20 insertions, 13 deletions
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index 2fa99aeaa095..df3f484e458a 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -64,19 +64,8 @@ static int fanotify_get_response(struct fsnotify_group *group,
64 64
65 pr_debug("%s: group=%p event=%p\n", __func__, group, event); 65 pr_debug("%s: group=%p event=%p\n", __func__, group, event);
66 66
67 /*
68 * fsnotify_prepare_user_wait() fails if we race with mark deletion.
69 * Just let the operation pass in that case.
70 */
71 if (!fsnotify_prepare_user_wait(iter_info)) {
72 event->response = FAN_ALLOW;
73 goto out;
74 }
75
76 wait_event(group->fanotify_data.access_waitq, event->response); 67 wait_event(group->fanotify_data.access_waitq, event->response);
77 68
78 fsnotify_finish_user_wait(iter_info);
79out:
80 /* userspace responded, convert to something usable */ 69 /* userspace responded, convert to something usable */
81 switch (event->response) { 70 switch (event->response) {
82 case FAN_ALLOW: 71 case FAN_ALLOW:
@@ -211,9 +200,21 @@ static int fanotify_handle_event(struct fsnotify_group *group,
211 pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode, 200 pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode,
212 mask); 201 mask);
213 202
203#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
204 if (mask & FAN_ALL_PERM_EVENTS) {
205 /*
206 * fsnotify_prepare_user_wait() fails if we race with mark
207 * deletion. Just let the operation pass in that case.
208 */
209 if (!fsnotify_prepare_user_wait(iter_info))
210 return 0;
211 }
212#endif
213
214 event = fanotify_alloc_event(inode, mask, data); 214 event = fanotify_alloc_event(inode, mask, data);
215 ret = -ENOMEM;
215 if (unlikely(!event)) 216 if (unlikely(!event))
216 return -ENOMEM; 217 goto finish;
217 218
218 fsn_event = &event->fse; 219 fsn_event = &event->fse;
219 ret = fsnotify_add_event(group, fsn_event, fanotify_merge); 220 ret = fsnotify_add_event(group, fsn_event, fanotify_merge);
@@ -223,7 +224,8 @@ static int fanotify_handle_event(struct fsnotify_group *group,
223 /* Our event wasn't used in the end. Free it. */ 224 /* Our event wasn't used in the end. Free it. */
224 fsnotify_destroy_event(group, fsn_event); 225 fsnotify_destroy_event(group, fsn_event);
225 226
226 return 0; 227 ret = 0;
228 goto finish;
227 } 229 }
228 230
229#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS 231#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
@@ -232,6 +234,11 @@ static int fanotify_handle_event(struct fsnotify_group *group,
232 iter_info); 234 iter_info);
233 fsnotify_destroy_event(group, fsn_event); 235 fsnotify_destroy_event(group, fsn_event);
234 } 236 }
237finish:
238 if (mask & FAN_ALL_PERM_EVENTS)
239 fsnotify_finish_user_wait(iter_info);
240#else
241finish:
235#endif 242#endif
236 return ret; 243 return ret;
237} 244}