summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2012-08-19 12:30:45 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2012-09-26 21:08:52 -0400
commit352e3b249284235e00745f3e71fc348b913e5deb (patch)
tree4d0c7342c95cb30f59b706048486c72a78be9f77
parentab72a7028c0cc22731dc60beceb595b321d1cdb9 (diff)
fanotify: sanitize failure exits in copy_event_to_user()
* do copy_to_user() before prepare_for_access_response(); that kills the need in remove_access_response(). * don't do fd_install() until we are past the last possible failure exit. Don't use sys_close() on cleanup side - just put_unused_fd() and fput(). Less racy that way... Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/notify/fanotify/fanotify_user.c59
1 files changed, 20 insertions, 39 deletions
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index d4380366973..ea48693940f 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -58,7 +58,9 @@ static struct fsnotify_event *get_one_event(struct fsnotify_group *group,
return fsnotify_remove_notify_event(group);
}
-static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event)
+static int create_fd(struct fsnotify_group *group,
+ struct fsnotify_event *event,
+ struct file **file)
{
int client_fd;
struct file *new_file;
@@ -98,7 +100,7 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event)
put_unused_fd(client_fd);
client_fd = PTR_ERR(new_file);
} else {
- fd_install(client_fd, new_file);
+ *file = new_file;
}
return client_fd;
@@ -106,13 +108,15 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event)
static int fill_event_metadata(struct fsnotify_group *group,
struct fanotify_event_metadata *metadata,
- struct fsnotify_event *event)
+ struct fsnotify_event *event,
+ struct file **file)
{
int ret = 0;
pr_debug("%s: group=%p metadata=%p event=%p\n", __func__,
group, metadata, event);
+ *file = NULL;
metadata->event_len = FAN_EVENT_METADATA_LEN;
metadata->metadata_len = FAN_EVENT_METADATA_LEN;
metadata->vers = FANOTIFY_METADATA_VERSION;
@@ -121,7 +125,7 @@ static int fill_event_metadata(struct fsnotify_group *group,
if (unlikely(event->mask & FAN_Q_OVERFLOW))
metadata->fd = FAN_NOFD;
else {
- metadata->fd = create_fd(group, event);
+ metadata->fd = create_fd(group, event, file);
if (metadata->fd < 0)
ret = metadata->fd;
}
@@ -220,25 +224,6 @@ static int prepare_for_access_response(struct fsnotify_group *group,
return 0;
}
-static void remove_access_response(struct fsnotify_group *group,
- struct fsnotify_event *event,
- __s32 fd)
-{
- struct fanotify_response_event *re;
-
- if (!(event->mask & FAN_ALL_PERM_EVENTS))
- return;
-
- re = dequeue_re(group, fd);
- if (!re)
- return;
-
- BUG_ON(re->event != event);
-
- kmem_cache_free(fanotify_response_event_cache, re);
-
- return;
-}
#else
static int prepare_for_access_response(struct fsnotify_group *group,
struct fsnotify_event *event,
@@ -247,12 +232,6 @@ static int prepare_for_access_response(struct fsnotify_group *group,
return 0;
}
-static void remove_access_response(struct fsnotify_group *group,
- struct fsnotify_event *event,
- __s32 fd)
-{
- return;
-}
#endif
static ssize_t copy_event_to_user(struct fsnotify_group *group,
@@ -260,31 +239,33 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
char __user *buf)
{
struct fanotify_event_metadata fanotify_event_metadata;
+ struct file *f;
int fd, ret;
pr_debug("%s: group=%p event=%p\n", __func__, group, event);
- ret = fill_event_metadata(group, &fanotify_event_metadata, event);
+ ret = fill_event_metadata(group, &fanotify_event_metadata, event, &f);
if (ret < 0)
goto out;
fd = fanotify_event_metadata.fd;
- ret = prepare_for_access_response(group, event, fd);
- if (ret)
- goto out_close_fd;
-
ret = -EFAULT;
if (copy_to_user(buf, &fanotify_event_metadata,
fanotify_event_metadata.event_len))
- goto out_kill_access_response;
+ goto out_close_fd;
+
+ ret = prepare_for_access_response(group, event, fd);
+ if (ret)
+ goto out_close_fd;
+ fd_install(fd, f);
return fanotify_event_metadata.event_len;
-out_kill_access_response:
- remove_access_response(group, event, fd);
out_close_fd:
- if (fd != FAN_NOFD)
- sys_close(fd);
+ if (fd != FAN_NOFD) {
+ put_unused_fd(fd);
+ fput(f);
+ }
out:
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
if (event->mask & FAN_ALL_PERM_EVENTS) {