aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2014-01-28 15:38:06 -0500
committerJan Kara <jack@suse.cz>2014-01-29 07:57:17 -0500
commit85816794240b9659e66e4d9b0df7c6e814e5f603 (patch)
treedd9a5103e62f15e74b2d7729e972d141845462aa
parent83c0e1b442b488571f4fef4a91c2fe52eed6c705 (diff)
fanotify: Fix use after free for permission events
Currently struct fanotify_event_info has been destroyed immediately after reporting its contents to userspace. However that is wrong for permission events because those need to stay around until userspace provides response which is filled back in fanotify_event_info. So change to code to free permission events only after we have got the response from userspace. Reported-and-tested-by: Jiri Kosina <jkosina@suse.cz> Reported-and-tested-by: Dave Jones <davej@fedoraproject.org> Signed-off-by: Jan Kara <jack@suse.cz>
-rw-r--r--fs/notify/fanotify/fanotify.c5
-rw-r--r--fs/notify/fanotify/fanotify.h7
-rw-r--r--fs/notify/fanotify/fanotify_user.c7
3 files changed, 17 insertions, 2 deletions
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index c7e5e8f54748..0e792f5e3147 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -192,14 +192,17 @@ static int fanotify_handle_event(struct fsnotify_group *group,
192 192
193 ret = fsnotify_add_notify_event(group, fsn_event, fanotify_merge); 193 ret = fsnotify_add_notify_event(group, fsn_event, fanotify_merge);
194 if (ret) { 194 if (ret) {
195 BUG_ON(mask & FAN_ALL_PERM_EVENTS);
195 /* Our event wasn't used in the end. Free it. */ 196 /* Our event wasn't used in the end. Free it. */
196 fsnotify_destroy_event(group, fsn_event); 197 fsnotify_destroy_event(group, fsn_event);
197 ret = 0; 198 ret = 0;
198 } 199 }
199 200
200#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS 201#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
201 if (mask & FAN_ALL_PERM_EVENTS) 202 if (mask & FAN_ALL_PERM_EVENTS) {
202 ret = fanotify_get_response_from_access(group, event); 203 ret = fanotify_get_response_from_access(group, event);
204 fsnotify_destroy_event(group, fsn_event);
205 }
203#endif 206#endif
204 return ret; 207 return ret;
205} 208}
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index 0e90174a116a..32a2f034fb94 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -4,6 +4,13 @@
4 4
5extern struct kmem_cache *fanotify_event_cachep; 5extern struct kmem_cache *fanotify_event_cachep;
6 6
7/*
8 * Lifetime of the structure differs for normal and permission events. In both
9 * cases the structure is allocated in fanotify_handle_event(). For normal
10 * events the structure is freed immediately after reporting it to userspace.
11 * For permission events we free it only after we receive response from
12 * userspace.
13 */
7struct fanotify_event_info { 14struct fanotify_event_info {
8 struct fsnotify_event fse; 15 struct fsnotify_event fse;
9 /* 16 /*
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 1fd66abe5740..b6175fa11bf8 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -319,7 +319,12 @@ static ssize_t fanotify_read(struct file *file, char __user *buf,
319 if (IS_ERR(kevent)) 319 if (IS_ERR(kevent))
320 break; 320 break;
321 ret = copy_event_to_user(group, kevent, buf); 321 ret = copy_event_to_user(group, kevent, buf);
322 fsnotify_destroy_event(group, kevent); 322 /*
323 * Permission events get destroyed after we
324 * receive response
325 */
326 if (!(kevent->mask & FAN_ALL_PERM_EVENTS))
327 fsnotify_destroy_event(group, kevent);
323 if (ret < 0) 328 if (ret < 0)
324 break; 329 break;
325 buf += ret; 330 buf += ret;