Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+20 −39
Original line number Diff line number Diff line
@@ -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) {