Commit 2ef5971f authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'vfs-6.10-rc4.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull vfs fixes from Christian Brauner:
 "Misc:
   - Restore debugfs behavior of ignoring unknown mount options
   - Fix kernel doc for netfs_wait_for_oustanding_io()
   - Fix struct statx comment after new addition for this cycle
   - Fix a check in find_next_fd()

  iomap:
   - Fix data zeroing behavior when an extent spans the block that
     contains i_size
   - Restore i_size increasing in iomap_write_end() for now to avoid
     stale data exposure on xfs with a realtime device

  Cachefiles:
   - Remove unneeded fdtable.h include
   - Improve trace output for cachefiles_obj_{get,put}_ondemand_fd()
   - Remove requests from the request list to prevent accessing already
     freed requests
   - Fix UAF when issuing restore command while the daemon is still
     alive by adding an additional reference count to requests
   - Fix UAF by grabbing a reference during xarray lookup with xa_lock()
     held
   - Simplify error handling in cachefiles_ondemand_daemon_read()
   - Add consistency checks read and open requests to avoid crashes
   - Add a spinlock to protect ondemand_id variable which is used to
     determine whether an anonymous cachefiles fd has already been
     closed
   - Make on-demand reads killable allowing to handle broken cachefiles
     daemon better
   - Flush all requests after the kernel has been marked dead via
     CACHEFILES_DEAD to avoid hung-tasks
   - Ensure that closed requests are marked as such to avoid reusing
     them with a reopen request
   - Defer fd_install() until after copy_to_user() succeeded and thereby
     get rid of having to use close_fd()
   - Ensure that anonymous cachefiles on-demand fds are reused while
     they are valid to avoid pinning already freed cookies"

* tag 'vfs-6.10-rc4.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  iomap: Fix iomap_adjust_read_range for plen calculation
  iomap: keep on increasing i_size in iomap_write_end()
  cachefiles: remove unneeded include of <linux/fdtable.h>
  fs/file: fix the check in find_next_fd()
  cachefiles: make on-demand read killable
  cachefiles: flush all requests after setting CACHEFILES_DEAD
  cachefiles: Set object to close if ondemand_id < 0 in copen
  cachefiles: defer exposing anon_fd until after copy_to_user() succeeds
  cachefiles: never get a new anonymous fd if ondemand_id is valid
  cachefiles: add spin_lock for cachefiles_ondemand_info
  cachefiles: add consistency check for copen/cread
  cachefiles: remove err_put_fd label in cachefiles_ondemand_daemon_read()
  cachefiles: fix slab-use-after-free in cachefiles_ondemand_daemon_read()
  cachefiles: fix slab-use-after-free in cachefiles_ondemand_get_fd()
  cachefiles: remove requests from xarray during flushing requests
  cachefiles: add output string to cachefiles_obj_[get|put]_ondemand_fd
  statx: Update offset commentary for struct statx
  netfs: fix kernel doc for nets_wait_for_outstanding_io()
  debugfs: continue to ignore unknown mount options
parents 83a7eefe f5ceb1bb
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -133,7 +133,7 @@ static int cachefiles_daemon_open(struct inode *inode, struct file *file)
	return 0;
}

static void cachefiles_flush_reqs(struct cachefiles_cache *cache)
void cachefiles_flush_reqs(struct cachefiles_cache *cache)
{
	struct xarray *xa = &cache->reqs;
	struct cachefiles_req *req;
@@ -159,6 +159,7 @@ static void cachefiles_flush_reqs(struct cachefiles_cache *cache)
	xa_for_each(xa, index, req) {
		req->error = -EIO;
		complete(&req->done);
		__xa_erase(xa, index);
	}
	xa_unlock(xa);

+5 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ struct cachefiles_ondemand_info {
	int				ondemand_id;
	enum cachefiles_object_state	state;
	struct cachefiles_object	*object;
	spinlock_t			lock;
};

/*
@@ -138,6 +139,7 @@ static inline bool cachefiles_in_ondemand_mode(struct cachefiles_cache *cache)
struct cachefiles_req {
	struct cachefiles_object *object;
	struct completion done;
	refcount_t ref;
	int error;
	struct cachefiles_msg msg;
};
@@ -186,6 +188,7 @@ extern int cachefiles_has_space(struct cachefiles_cache *cache,
 * daemon.c
 */
extern const struct file_operations cachefiles_daemon_fops;
extern void cachefiles_flush_reqs(struct cachefiles_cache *cache);
extern void cachefiles_get_unbind_pincount(struct cachefiles_cache *cache);
extern void cachefiles_put_unbind_pincount(struct cachefiles_cache *cache);

@@ -424,6 +427,8 @@ do { \
	pr_err("I/O Error: " FMT"\n", ##__VA_ARGS__);	\
	fscache_io_error((___cache)->cache);		\
	set_bit(CACHEFILES_DEAD, &(___cache)->flags);	\
	if (cachefiles_in_ondemand_mode(___cache))	\
		cachefiles_flush_reqs(___cache);	\
} while (0)

#define cachefiles_io_error_obj(object, FMT, ...)			\
+162 −56
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/fdtable.h>
#include <linux/anon_inodes.h>
#include <linux/uio.h>
#include "internal.h"

struct ondemand_anon_file {
	struct file *file;
	int fd;
};

static inline void cachefiles_req_put(struct cachefiles_req *req)
{
	if (refcount_dec_and_test(&req->ref))
		kfree(req);
}

static int cachefiles_ondemand_fd_release(struct inode *inode,
					  struct file *file)
{
	struct cachefiles_object *object = file->private_data;
	struct cachefiles_cache *cache = object->volume->cache;
	struct cachefiles_ondemand_info *info = object->ondemand;
	int object_id = info->ondemand_id;
	struct cachefiles_cache *cache;
	struct cachefiles_ondemand_info *info;
	int object_id;
	struct cachefiles_req *req;
	XA_STATE(xas, &cache->reqs, 0);
	XA_STATE(xas, NULL, 0);

	if (!object)
		return 0;

	info = object->ondemand;
	cache = object->volume->cache;
	xas.xa = &cache->reqs;

	xa_lock(&cache->reqs);
	spin_lock(&info->lock);
	object_id = info->ondemand_id;
	info->ondemand_id = CACHEFILES_ONDEMAND_ID_CLOSED;
	cachefiles_ondemand_set_object_close(object);
	spin_unlock(&info->lock);

	/* Only flush CACHEFILES_REQ_NEW marked req to avoid race with daemon_read */
	xas_for_each_marked(&xas, req, ULONG_MAX, CACHEFILES_REQ_NEW) {
@@ -76,12 +96,12 @@ static loff_t cachefiles_ondemand_fd_llseek(struct file *filp, loff_t pos,
}

static long cachefiles_ondemand_fd_ioctl(struct file *filp, unsigned int ioctl,
					 unsigned long arg)
					 unsigned long id)
{
	struct cachefiles_object *object = filp->private_data;
	struct cachefiles_cache *cache = object->volume->cache;
	struct cachefiles_req *req;
	unsigned long id;
	XA_STATE(xas, &cache->reqs, id);

	if (ioctl != CACHEFILES_IOC_READ_COMPLETE)
		return -EINVAL;
@@ -89,10 +109,15 @@ static long cachefiles_ondemand_fd_ioctl(struct file *filp, unsigned int ioctl,
	if (!test_bit(CACHEFILES_ONDEMAND_MODE, &cache->flags))
		return -EOPNOTSUPP;

	id = arg;
	req = xa_erase(&cache->reqs, id);
	if (!req)
	xa_lock(&cache->reqs);
	req = xas_load(&xas);
	if (!req || req->msg.opcode != CACHEFILES_OP_READ ||
	    req->object != object) {
		xa_unlock(&cache->reqs);
		return -EINVAL;
	}
	xas_store(&xas, NULL);
	xa_unlock(&cache->reqs);

	trace_cachefiles_ondemand_cread(object, id);
	complete(&req->done);
@@ -116,10 +141,12 @@ int cachefiles_ondemand_copen(struct cachefiles_cache *cache, char *args)
{
	struct cachefiles_req *req;
	struct fscache_cookie *cookie;
	struct cachefiles_ondemand_info *info;
	char *pid, *psize;
	unsigned long id;
	long size;
	int ret;
	XA_STATE(xas, &cache->reqs, 0);

	if (!test_bit(CACHEFILES_ONDEMAND_MODE, &cache->flags))
		return -EOPNOTSUPP;
@@ -143,10 +170,18 @@ int cachefiles_ondemand_copen(struct cachefiles_cache *cache, char *args)
	if (ret)
		return ret;

	req = xa_erase(&cache->reqs, id);
	if (!req)
	xa_lock(&cache->reqs);
	xas.xa_index = id;
	req = xas_load(&xas);
	if (!req || req->msg.opcode != CACHEFILES_OP_OPEN ||
	    !req->object->ondemand->ondemand_id) {
		xa_unlock(&cache->reqs);
		return -EINVAL;
	}
	xas_store(&xas, NULL);
	xa_unlock(&cache->reqs);

	info = req->object->ondemand;
	/* fail OPEN request if copen format is invalid */
	ret = kstrtol(psize, 0, &size);
	if (ret) {
@@ -166,6 +201,32 @@ int cachefiles_ondemand_copen(struct cachefiles_cache *cache, char *args)
		goto out;
	}

	spin_lock(&info->lock);
	/*
	 * The anonymous fd was closed before copen ? Fail the request.
	 *
	 *             t1             |             t2
	 * ---------------------------------------------------------
	 *                             cachefiles_ondemand_copen
	 *                             req = xa_erase(&cache->reqs, id)
	 * // Anon fd is maliciously closed.
	 * cachefiles_ondemand_fd_release
	 * xa_lock(&cache->reqs)
	 * cachefiles_ondemand_set_object_close(object)
	 * xa_unlock(&cache->reqs)
	 *                             cachefiles_ondemand_set_object_open
	 *                             // No one will ever close it again.
	 * cachefiles_ondemand_daemon_read
	 * cachefiles_ondemand_select_req
	 *
	 * Get a read req but its fd is already closed. The daemon can't
	 * issue a cread ioctl with an closed fd, then hung.
	 */
	if (info->ondemand_id == CACHEFILES_ONDEMAND_ID_CLOSED) {
		spin_unlock(&info->lock);
		req->error = -EBADFD;
		goto out;
	}
	cookie = req->object->cookie;
	cookie->object_size = size;
	if (size)
@@ -175,9 +236,15 @@ int cachefiles_ondemand_copen(struct cachefiles_cache *cache, char *args)
	trace_cachefiles_ondemand_copen(req->object, id, size);

	cachefiles_ondemand_set_object_open(req->object);
	spin_unlock(&info->lock);
	wake_up_all(&cache->daemon_pollwq);

out:
	spin_lock(&info->lock);
	/* Need to set object close to avoid reopen status continuing */
	if (info->ondemand_id == CACHEFILES_ONDEMAND_ID_CLOSED)
		cachefiles_ondemand_set_object_close(req->object);
	spin_unlock(&info->lock);
	complete(&req->done);
	return ret;
}
@@ -205,14 +272,14 @@ int cachefiles_ondemand_restore(struct cachefiles_cache *cache, char *args)
	return 0;
}

static int cachefiles_ondemand_get_fd(struct cachefiles_req *req)
static int cachefiles_ondemand_get_fd(struct cachefiles_req *req,
				      struct ondemand_anon_file *anon_file)
{
	struct cachefiles_object *object;
	struct cachefiles_cache *cache;
	struct cachefiles_open *load;
	struct file *file;
	u32 object_id;
	int ret, fd;
	int ret;

	object = cachefiles_grab_object(req->object,
			cachefiles_obj_get_ondemand_fd);
@@ -224,35 +291,53 @@ static int cachefiles_ondemand_get_fd(struct cachefiles_req *req)
	if (ret < 0)
		goto err;

	fd = get_unused_fd_flags(O_WRONLY);
	if (fd < 0) {
		ret = fd;
	anon_file->fd = get_unused_fd_flags(O_WRONLY);
	if (anon_file->fd < 0) {
		ret = anon_file->fd;
		goto err_free_id;
	}

	file = anon_inode_getfile("[cachefiles]", &cachefiles_ondemand_fd_fops,
				  object, O_WRONLY);
	if (IS_ERR(file)) {
		ret = PTR_ERR(file);
	anon_file->file = anon_inode_getfile("[cachefiles]",
				&cachefiles_ondemand_fd_fops, object, O_WRONLY);
	if (IS_ERR(anon_file->file)) {
		ret = PTR_ERR(anon_file->file);
		goto err_put_fd;
	}

	file->f_mode |= FMODE_PWRITE | FMODE_LSEEK;
	fd_install(fd, file);
	spin_lock(&object->ondemand->lock);
	if (object->ondemand->ondemand_id > 0) {
		spin_unlock(&object->ondemand->lock);
		/* Pair with check in cachefiles_ondemand_fd_release(). */
		anon_file->file->private_data = NULL;
		ret = -EEXIST;
		goto err_put_file;
	}

	anon_file->file->f_mode |= FMODE_PWRITE | FMODE_LSEEK;

	load = (void *)req->msg.data;
	load->fd = fd;
	load->fd = anon_file->fd;
	object->ondemand->ondemand_id = object_id;
	spin_unlock(&object->ondemand->lock);

	cachefiles_get_unbind_pincount(cache);
	trace_cachefiles_ondemand_open(object, &req->msg, load);
	return 0;

err_put_file:
	fput(anon_file->file);
	anon_file->file = NULL;
err_put_fd:
	put_unused_fd(fd);
	put_unused_fd(anon_file->fd);
	anon_file->fd = ret;
err_free_id:
	xa_erase(&cache->ondemand_ids, object_id);
err:
	spin_lock(&object->ondemand->lock);
	/* Avoid marking an opened object as closed. */
	if (object->ondemand->ondemand_id <= 0)
		cachefiles_ondemand_set_object_close(object);
	spin_unlock(&object->ondemand->lock);
	cachefiles_put_object(object, cachefiles_obj_put_ondemand_fd);
	return ret;
}
@@ -294,14 +379,28 @@ static struct cachefiles_req *cachefiles_ondemand_select_req(struct xa_state *xa
	return NULL;
}

static inline bool cachefiles_ondemand_finish_req(struct cachefiles_req *req,
						  struct xa_state *xas, int err)
{
	if (unlikely(!xas || !req))
		return false;

	if (xa_cmpxchg(xas->xa, xas->xa_index, req, NULL, 0) != req)
		return false;

	req->error = err;
	complete(&req->done);
	return true;
}

ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache,
					char __user *_buffer, size_t buflen)
{
	struct cachefiles_req *req;
	struct cachefiles_msg *msg;
	unsigned long id = 0;
	size_t n;
	int ret = 0;
	struct ondemand_anon_file anon_file;
	XA_STATE(xas, &cache->reqs, cache->req_id_next);

	xa_lock(&cache->reqs);
@@ -330,42 +429,37 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache,

	xas_clear_mark(&xas, CACHEFILES_REQ_NEW);
	cache->req_id_next = xas.xa_index + 1;
	refcount_inc(&req->ref);
	cachefiles_grab_object(req->object, cachefiles_obj_get_read_req);
	xa_unlock(&cache->reqs);

	id = xas.xa_index;

	if (msg->opcode == CACHEFILES_OP_OPEN) {
		ret = cachefiles_ondemand_get_fd(req);
		if (ret) {
			cachefiles_ondemand_set_object_close(req->object);
			goto error;
		}
		ret = cachefiles_ondemand_get_fd(req, &anon_file);
		if (ret)
			goto out;
	}

	msg->msg_id = id;
	msg->msg_id = xas.xa_index;
	msg->object_id = req->object->ondemand->ondemand_id;

	if (copy_to_user(_buffer, msg, n) != 0) {
	if (copy_to_user(_buffer, msg, n) != 0)
		ret = -EFAULT;
		goto err_put_fd;
	}

	/* CLOSE request has no reply */
	if (msg->opcode == CACHEFILES_OP_CLOSE) {
		xa_erase(&cache->reqs, id);
		complete(&req->done);
	if (msg->opcode == CACHEFILES_OP_OPEN) {
		if (ret < 0) {
			fput(anon_file.file);
			put_unused_fd(anon_file.fd);
			goto out;
		}

	return n;

err_put_fd:
	if (msg->opcode == CACHEFILES_OP_OPEN)
		close_fd(((struct cachefiles_open *)msg->data)->fd);
error:
	xa_erase(&cache->reqs, id);
	req->error = ret;
	complete(&req->done);
	return ret;
		fd_install(anon_file.fd, anon_file.file);
	}
out:
	cachefiles_put_object(req->object, cachefiles_obj_put_read_req);
	/* Remove error request and CLOSE request has no reply */
	if (ret || msg->opcode == CACHEFILES_OP_CLOSE)
		cachefiles_ondemand_finish_req(req, &xas, ret);
	cachefiles_req_put(req);
	return ret ? ret : n;
}

typedef int (*init_req_fn)(struct cachefiles_req *req, void *private);
@@ -395,6 +489,7 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object,
		goto out;
	}

	refcount_set(&req->ref, 1);
	req->object = object;
	init_completion(&req->done);
	req->msg.opcode = opcode;
@@ -454,9 +549,19 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object,
		goto out;

	wake_up_all(&cache->daemon_pollwq);
	wait_for_completion(&req->done);
wait:
	ret = wait_for_completion_killable(&req->done);
	if (!ret) {
		ret = req->error;
	kfree(req);
	} else {
		ret = -EINTR;
		if (!cachefiles_ondemand_finish_req(req, &xas, ret)) {
			/* Someone will complete it soon. */
			cpu_relax();
			goto wait;
		}
	}
	cachefiles_req_put(req);
	return ret;
out:
	/* Reset the object to close state in error handling path.
@@ -578,6 +683,7 @@ int cachefiles_ondemand_init_obj_info(struct cachefiles_object *object,
		return -ENOMEM;

	object->ondemand->object = object;
	spin_lock_init(&object->ondemand->lock);
	INIT_WORK(&object->ondemand->ondemand_work, ondemand_object_worker);
	return 0;
}
+9 −1
Original line number Diff line number Diff line
@@ -107,8 +107,16 @@ static int debugfs_parse_param(struct fs_context *fc, struct fs_parameter *param
	int opt;

	opt = fs_parse(fc, debugfs_param_specs, param, &result);
	if (opt < 0)
	if (opt < 0) {
		/*
                * We might like to report bad mount options here; but
                * traditionally debugfs has ignored all mount options
                */
		if (opt == -ENOPARAM)
			return 0;

		return opt;
	}

	switch (opt) {
	case Opt_uid:
+2 −2
Original line number Diff line number Diff line
@@ -486,12 +486,12 @@ struct files_struct init_files = {

static unsigned int find_next_fd(struct fdtable *fdt, unsigned int start)
{
	unsigned int maxfd = fdt->max_fds;
	unsigned int maxfd = fdt->max_fds; /* always multiple of BITS_PER_LONG */
	unsigned int maxbit = maxfd / BITS_PER_LONG;
	unsigned int bitbit = start / BITS_PER_LONG;

	bitbit = find_next_zero_bit(fdt->full_fds_bits, maxbit, bitbit) * BITS_PER_LONG;
	if (bitbit > maxfd)
	if (bitbit >= maxfd)
		return maxfd;
	if (bitbit > start)
		start = bitbit;
Loading