Commit 83ab4b46 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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

Pull vfs fixes from Christian Brauner:
 "cachefiles:

   - Export an existing and add a new cachefile helper to be used in
     filesystems to fix reference count bugs

   - Use the newly added fscache_ty_get_volume() helper to get a
     reference count on an fscache_volume to handle volumes that are
     about to be removed cleanly

   - After withdrawing a fscache_cache via FSCACHE_CACHE_IS_WITHDRAWN
     wait for all ongoing cookie lookups to complete and for the object
     count to reach zero

   - Propagate errors from vfs_getxattr() to avoid an infinite loop in
     cachefiles_check_volume_xattr() because it keeps seeing ESTALE

   - Don't send new requests when an object is dropped by raising
     CACHEFILES_ONDEMAND_OJBSTATE_DROPPING

   - Cancel all requests for an object that is about to be dropped

   - Wait for the ondemand_boject_worker to finish before dropping a
     cachefiles object to prevent use-after-free

   - Use cyclic allocation for message ids to better handle id recycling

   - Add missing lock protection when iterating through the xarray when
     polling

  netfs:

   - Use standard logging helpers for debug logging

  VFS:

   - Fix potential use-after-free in file locks during
     trace_posix_lock_inode(). The tracepoint could fire while another
     task raced it and freed the lock that was requested to be traced

   - Only increment the nr_dentry_negative counter for dentries that are
     present on the superblock LRU. Currently, DCACHE_LRU_LIST list is
     used to detect this case. However, the flag is also raised in
     combination with DCACHE_SHRINK_LIST to indicate that dentry->d_lru
     is used. So checking only DCACHE_LRU_LIST will lead to wrong
     nr_dentry_negative count. Fix the check to not count dentries that
     are on a shrink related list

  Misc:

   - hfsplus: fix an uninitialized value issue in copy_name

   - minix: fix minixfs_rename with HIGHMEM. It still uses kunmap() even
     though we switched it to kmap_local_page() a while ago"

* tag 'vfs-6.10-rc8.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  minixfs: Fix minixfs_rename with HIGHMEM
  hfsplus: fix uninit-value in copy_name
  vfs: don't mod negative dentry count when on shrinker list
  filelock: fix potential use-after-free in posix_lock_inode
  cachefiles: add missing lock protection when polling
  cachefiles: cyclic allocation of msg_id to avoid reuse
  cachefiles: wait for ondemand_object_worker to finish when dropping object
  cachefiles: cancel all requests for the object that is being dropped
  cachefiles: stop sending new request when dropping object
  cachefiles: propagate errors from vfs_getxattr() to avoid infinite loop
  cachefiles: fix slab-use-after-free in cachefiles_withdraw_cookie()
  cachefiles: fix slab-use-after-free in fscache_withdraw_volume()
  netfs, fscache: export fscache_put_volume() and add fscache_try_get_volume()
  netfs: Switch debug logging to pr_debug()
parents 9d9a2f29 3d1bec29
Loading
Loading
Loading
Loading
+44 −1
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include <linux/slab.h>
#include <linux/statfs.h>
#include <linux/namei.h>
#include <trace/events/fscache.h>
#include "internal.h"

/*
@@ -312,19 +313,59 @@ static void cachefiles_withdraw_objects(struct cachefiles_cache *cache)
}

/*
 * Withdraw volumes.
 * Withdraw fscache volumes.
 */
static void cachefiles_withdraw_fscache_volumes(struct cachefiles_cache *cache)
{
	struct list_head *cur;
	struct cachefiles_volume *volume;
	struct fscache_volume *vcookie;

	_enter("");
retry:
	spin_lock(&cache->object_list_lock);
	list_for_each(cur, &cache->volumes) {
		volume = list_entry(cur, struct cachefiles_volume, cache_link);

		if (atomic_read(&volume->vcookie->n_accesses) == 0)
			continue;

		vcookie = fscache_try_get_volume(volume->vcookie,
						 fscache_volume_get_withdraw);
		if (vcookie) {
			spin_unlock(&cache->object_list_lock);
			fscache_withdraw_volume(vcookie);
			fscache_put_volume(vcookie, fscache_volume_put_withdraw);
			goto retry;
		}
	}
	spin_unlock(&cache->object_list_lock);

	_leave("");
}

/*
 * Withdraw cachefiles volumes.
 */
static void cachefiles_withdraw_volumes(struct cachefiles_cache *cache)
{
	_enter("");

	for (;;) {
		struct fscache_volume *vcookie = NULL;
		struct cachefiles_volume *volume = NULL;

		spin_lock(&cache->object_list_lock);
		if (!list_empty(&cache->volumes)) {
			volume = list_first_entry(&cache->volumes,
						  struct cachefiles_volume, cache_link);
			vcookie = fscache_try_get_volume(volume->vcookie,
							 fscache_volume_get_withdraw);
			if (!vcookie) {
				spin_unlock(&cache->object_list_lock);
				cpu_relax();
				continue;
			}
			list_del_init(&volume->cache_link);
		}
		spin_unlock(&cache->object_list_lock);
@@ -332,6 +373,7 @@ static void cachefiles_withdraw_volumes(struct cachefiles_cache *cache)
			break;

		cachefiles_withdraw_volume(volume);
		fscache_put_volume(vcookie, fscache_volume_put_withdraw);
	}

	_leave("");
@@ -371,6 +413,7 @@ void cachefiles_withdraw_cache(struct cachefiles_cache *cache)
	pr_info("File cache on %s unregistering\n", fscache->name);

	fscache_withdraw_cache(fscache);
	cachefiles_withdraw_fscache_volumes(cache);

	/* we now have to destroy all the active objects pertaining to this
	 * cache - which we do by passing them off to thread pool to be
+2 −2
Original line number Diff line number Diff line
@@ -366,14 +366,14 @@ static __poll_t cachefiles_daemon_poll(struct file *file,

	if (cachefiles_in_ondemand_mode(cache)) {
		if (!xa_empty(&cache->reqs)) {
			rcu_read_lock();
			xas_lock(&xas);
			xas_for_each_marked(&xas, req, ULONG_MAX, CACHEFILES_REQ_NEW) {
				if (!cachefiles_ondemand_is_reopening_read(req)) {
					mask |= EPOLLIN;
					break;
				}
			}
			rcu_read_unlock();
			xas_unlock(&xas);
		}
	} else {
		if (test_bit(CACHEFILES_STATE_CHANGED, &cache->flags))
+3 −0
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ enum cachefiles_object_state {
	CACHEFILES_ONDEMAND_OBJSTATE_CLOSE, /* Anonymous fd closed by daemon or initial state */
	CACHEFILES_ONDEMAND_OBJSTATE_OPEN, /* Anonymous fd associated with object is available */
	CACHEFILES_ONDEMAND_OBJSTATE_REOPENING, /* Object that was closed and is being reopened. */
	CACHEFILES_ONDEMAND_OBJSTATE_DROPPING, /* Object is being dropped. */
};

struct cachefiles_ondemand_info {
@@ -128,6 +129,7 @@ struct cachefiles_cache {
	unsigned long			req_id_next;
	struct xarray			ondemand_ids;	/* xarray for ondemand_id allocation */
	u32				ondemand_id_next;
	u32				msg_id_next;
};

static inline bool cachefiles_in_ondemand_mode(struct cachefiles_cache *cache)
@@ -335,6 +337,7 @@ cachefiles_ondemand_set_object_##_state(struct cachefiles_object *object) \
CACHEFILES_OBJECT_STATE_FUNCS(open, OPEN);
CACHEFILES_OBJECT_STATE_FUNCS(close, CLOSE);
CACHEFILES_OBJECT_STATE_FUNCS(reopening, REOPENING);
CACHEFILES_OBJECT_STATE_FUNCS(dropping, DROPPING);

static inline bool cachefiles_ondemand_is_reopening_read(struct cachefiles_req *req)
{
+46 −6
Original line number Diff line number Diff line
@@ -517,7 +517,8 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object,
		 */
		xas_lock(&xas);

		if (test_bit(CACHEFILES_DEAD, &cache->flags)) {
		if (test_bit(CACHEFILES_DEAD, &cache->flags) ||
		    cachefiles_ondemand_object_is_dropping(object)) {
			xas_unlock(&xas);
			ret = -EIO;
			goto out;
@@ -534,13 +535,25 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object,
			goto out;
		}

		xas.xa_index = 0;
		/*
		 * Cyclically find a free xas to avoid msg_id reuse that would
		 * cause the daemon to successfully copen a stale msg_id.
		 */
		xas.xa_index = cache->msg_id_next;
		xas_find_marked(&xas, UINT_MAX, XA_FREE_MARK);
		if (xas.xa_node == XAS_RESTART) {
			xas.xa_index = 0;
			xas_find_marked(&xas, cache->msg_id_next - 1, XA_FREE_MARK);
		}
		if (xas.xa_node == XAS_RESTART)
			xas_set_err(&xas, -EBUSY);

		xas_store(&xas, req);
		if (xas_valid(&xas)) {
			cache->msg_id_next = xas.xa_index + 1;
			xas_clear_mark(&xas, XA_FREE_MARK);
			xas_set_mark(&xas, CACHEFILES_REQ_NEW);
		}
		xas_unlock(&xas);
	} while (xas_nomem(&xas, GFP_KERNEL));

@@ -568,7 +581,8 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object,
	 * If error occurs after creating the anonymous fd,
	 * cachefiles_ondemand_fd_release() will set object to close.
	 */
	if (opcode == CACHEFILES_OP_OPEN)
	if (opcode == CACHEFILES_OP_OPEN &&
	    !cachefiles_ondemand_object_is_dropping(object))
		cachefiles_ondemand_set_object_close(object);
	kfree(req);
	return ret;
@@ -667,8 +681,34 @@ int cachefiles_ondemand_init_object(struct cachefiles_object *object)

void cachefiles_ondemand_clean_object(struct cachefiles_object *object)
{
	unsigned long index;
	struct cachefiles_req *req;
	struct cachefiles_cache *cache;

	if (!object->ondemand)
		return;

	cachefiles_ondemand_send_req(object, CACHEFILES_OP_CLOSE, 0,
			cachefiles_ondemand_init_close_req, NULL);

	if (!object->ondemand->ondemand_id)
		return;

	/* Cancel all requests for the object that is being dropped. */
	cache = object->volume->cache;
	xa_lock(&cache->reqs);
	cachefiles_ondemand_set_object_dropping(object);
	xa_for_each(&cache->reqs, index, req) {
		if (req->object == object) {
			req->error = -EIO;
			complete(&req->done);
			__xa_erase(&cache->reqs, index);
		}
	}
	xa_unlock(&cache->reqs);

	/* Wait for ondemand_object_worker() to finish to avoid UAF. */
	cancel_work_sync(&object->ondemand->ondemand_work);
}

int cachefiles_ondemand_init_obj_info(struct cachefiles_object *object,
+0 −1
Original line number Diff line number Diff line
@@ -133,7 +133,6 @@ void cachefiles_free_volume(struct fscache_volume *vcookie)

void cachefiles_withdraw_volume(struct cachefiles_volume *volume)
{
	fscache_withdraw_volume(volume->vcookie);
	cachefiles_set_volume_xattr(volume);
	__cachefiles_free_volume(volume);
}
Loading