Commit 352e3b24 authored by Al Viro's avatar Al Viro

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: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent ab72a702
...@@ -58,7 +58,9 @@ static struct fsnotify_event *get_one_event(struct fsnotify_group *group, ...@@ -58,7 +58,9 @@ static struct fsnotify_event *get_one_event(struct fsnotify_group *group,
return fsnotify_remove_notify_event(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; int client_fd;
struct file *new_file; struct file *new_file;
...@@ -98,7 +100,7 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) ...@@ -98,7 +100,7 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event)
put_unused_fd(client_fd); put_unused_fd(client_fd);
client_fd = PTR_ERR(new_file); client_fd = PTR_ERR(new_file);
} else { } else {
fd_install(client_fd, new_file); *file = new_file;
} }
return client_fd; return client_fd;
...@@ -106,13 +108,15 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) ...@@ -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, static int fill_event_metadata(struct fsnotify_group *group,
struct fanotify_event_metadata *metadata, struct fanotify_event_metadata *metadata,
struct fsnotify_event *event) struct fsnotify_event *event,
struct file **file)
{ {
int ret = 0; int ret = 0;
pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, pr_debug("%s: group=%p metadata=%p event=%p\n", __func__,
group, metadata, event); group, metadata, event);
*file = NULL;
metadata->event_len = FAN_EVENT_METADATA_LEN; metadata->event_len = FAN_EVENT_METADATA_LEN;
metadata->metadata_len = FAN_EVENT_METADATA_LEN; metadata->metadata_len = FAN_EVENT_METADATA_LEN;
metadata->vers = FANOTIFY_METADATA_VERSION; metadata->vers = FANOTIFY_METADATA_VERSION;
...@@ -121,7 +125,7 @@ static int fill_event_metadata(struct fsnotify_group *group, ...@@ -121,7 +125,7 @@ static int fill_event_metadata(struct fsnotify_group *group,
if (unlikely(event->mask & FAN_Q_OVERFLOW)) if (unlikely(event->mask & FAN_Q_OVERFLOW))
metadata->fd = FAN_NOFD; metadata->fd = FAN_NOFD;
else { else {
metadata->fd = create_fd(group, event); metadata->fd = create_fd(group, event, file);
if (metadata->fd < 0) if (metadata->fd < 0)
ret = metadata->fd; ret = metadata->fd;
} }
...@@ -220,25 +224,6 @@ static int prepare_for_access_response(struct fsnotify_group *group, ...@@ -220,25 +224,6 @@ static int prepare_for_access_response(struct fsnotify_group *group,
return 0; 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 #else
static int prepare_for_access_response(struct fsnotify_group *group, static int prepare_for_access_response(struct fsnotify_group *group,
struct fsnotify_event *event, struct fsnotify_event *event,
...@@ -247,12 +232,6 @@ static int prepare_for_access_response(struct fsnotify_group *group, ...@@ -247,12 +232,6 @@ static int prepare_for_access_response(struct fsnotify_group *group,
return 0; return 0;
} }
static void remove_access_response(struct fsnotify_group *group,
struct fsnotify_event *event,
__s32 fd)
{
return;
}
#endif #endif
static ssize_t copy_event_to_user(struct fsnotify_group *group, 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, ...@@ -260,31 +239,33 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
char __user *buf) char __user *buf)
{ {
struct fanotify_event_metadata fanotify_event_metadata; struct fanotify_event_metadata fanotify_event_metadata;
struct file *f;
int fd, ret; int fd, ret;
pr_debug("%s: group=%p event=%p\n", __func__, group, event); 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) if (ret < 0)
goto out; goto out;
fd = fanotify_event_metadata.fd; fd = fanotify_event_metadata.fd;
ret = prepare_for_access_response(group, event, fd);
if (ret)
goto out_close_fd;
ret = -EFAULT; ret = -EFAULT;
if (copy_to_user(buf, &fanotify_event_metadata, if (copy_to_user(buf, &fanotify_event_metadata,
fanotify_event_metadata.event_len)) 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; return fanotify_event_metadata.event_len;
out_kill_access_response:
remove_access_response(group, event, fd);
out_close_fd: out_close_fd:
if (fd != FAN_NOFD) if (fd != FAN_NOFD) {
sys_close(fd); put_unused_fd(fd);
fput(f);
}
out: out:
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
if (event->mask & FAN_ALL_PERM_EVENTS) { if (event->mask & FAN_ALL_PERM_EVENTS) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment