Commit 2bd5d41e authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull fuse updates from Miklos Szeredi:

 - Fix an issue with reusing the bdi in case of block based filesystems

 - Allow root (in init namespace) to access fuse filesystems in user
   namespaces if expicitly enabled with a module param

 - Misc fixes

* tag 'fuse-update-6.0' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: retire block-device-based superblock on force unmount
  vfs: function to prevent re-use of block-device-based superblocks
  virtio_fs: Modify format for virtio_fs_direct_access
  virtiofs: delete unused parameter for virtio_fs_cleanup_vqs
  fuse: Add module param for CAP_SYS_ADMIN access bypassing allow_other
  fuse: Remove the control interface for virtio-fs
  fuse: ioctl: translate ENOSYS
  fuse: limit nsec
  fuse: avoid unnecessary spinlock bump
  fuse: fix deadlock between atomic O_TRUNC and page invalidation
  fuse: write inode in fuse_release()
parents 65512eb0 247861c3
Loading
Loading
Loading
Loading
+24 −5
Original line number Diff line number Diff line
@@ -279,7 +279,7 @@ How are requirements fulfilled?
	the filesystem or not.

	Note that the *ptrace* check is not strictly necessary to
	prevent B/2/i, it is enough to check if mount owner has enough
	prevent C/2/i, it is enough to check if mount owner has enough
	privilege to send signal to the process accessing the
	filesystem, since *SIGSTOP* can be used to get a similar effect.

@@ -288,10 +288,29 @@ I think these limitations are unacceptable?

If a sysadmin trusts the users enough, or can ensure through other
measures, that system processes will never enter non-privileged
mounts, it can relax the last limitation with a 'user_allow_other'
config option.  If this config option is set, the mounting user can
add the 'allow_other' mount option which disables the check for other
users' processes.
mounts, it can relax the last limitation in several ways:

  - With the 'user_allow_other' config option. If this config option is
    set, the mounting user can add the 'allow_other' mount option which
    disables the check for other users' processes.

    User namespaces have an unintuitive interaction with 'allow_other':
    an unprivileged user - normally restricted from mounting with
    'allow_other' - could do so in a user namespace where they're
    privileged. If any process could access such an 'allow_other' mount
    this would give the mounting user the ability to manipulate
    processes in user namespaces where they're unprivileged. For this
    reason 'allow_other' restricts access to users in the same userns
    or a descendant.

  - With the 'allow_sys_admin_access' module option. If this option is
    set, super user's processes have unrestricted access to mounts
    irrespective of allow_other setting or user namespace of the
    mounting user.

Note that both of these relaxations expose the system to potential
information leak or *DoS* as described in points B and C/2/i-ii in the
preceding section.

Kernel - userspace interface
============================
+2 −2
Original line number Diff line number Diff line
@@ -258,7 +258,7 @@ int fuse_ctl_add_conn(struct fuse_conn *fc)
	struct dentry *parent;
	char name[32];

	if (!fuse_control_sb)
	if (!fuse_control_sb || fc->no_control)
		return 0;

	parent = fuse_control_sb->s_root;
@@ -296,7 +296,7 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc)
{
	int i;

	if (!fuse_control_sb)
	if (!fuse_control_sb || fc->no_control)
		return;

	for (i = fc->ctl_ndents - 1; i >= 0; i--) {
+1 −1
Original line number Diff line number Diff line
@@ -138,9 +138,9 @@ static struct fuse_dax_mapping *alloc_dax_mapping(struct fuse_conn_dax *fcd)
		WARN_ON(fcd->nr_free_ranges <= 0);
		fcd->nr_free_ranges--;
	}
	__kick_dmap_free_worker(fcd, 0);
	spin_unlock(&fcd->lock);

	kick_dmap_free_worker(fcd, 0);
	return dmap;
}

+15 −1
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/pagemap.h>
#include <linux/file.h>
#include <linux/fs_context.h>
#include <linux/moduleparam.h>
#include <linux/sched.h>
#include <linux/namei.h>
#include <linux/slab.h>
@@ -21,6 +22,11 @@
#include <linux/types.h>
#include <linux/kernel.h>

static bool __read_mostly allow_sys_admin_access;
module_param(allow_sys_admin_access, bool, 0644);
MODULE_PARM_DESC(allow_sys_admin_access,
		 "Allow users with CAP_SYS_ADMIN in initial userns to bypass allow_other access check");

static void fuse_advise_use_readdirplus(struct inode *dir)
{
	struct fuse_inode *fi = get_fuse_inode(dir);
@@ -537,6 +543,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
	struct fuse_file *ff;
	void *security_ctx = NULL;
	u32 security_ctxlen;
	bool trunc = flags & O_TRUNC;

	/* Userspace expects S_IFREG in create mode */
	BUG_ON((mode & S_IFMT) != S_IFREG);
@@ -561,7 +568,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
	inarg.mode = mode;
	inarg.umask = current_umask();

	if (fm->fc->handle_killpriv_v2 && (flags & O_TRUNC) &&
	if (fm->fc->handle_killpriv_v2 && trunc &&
	    !(flags & O_EXCL) && !capable(CAP_FSETID)) {
		inarg.open_flags |= FUSE_OPEN_KILL_SUIDGID;
	}
@@ -623,6 +630,10 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
	} else {
		file->private_data = ff;
		fuse_finish_open(inode, file);
		if (fm->fc->atomic_o_trunc && trunc)
			truncate_pagecache(inode, 0);
		else if (!(ff->open_flags & FOPEN_KEEP_CACHE))
			invalidate_inode_pages2(inode->i_mapping);
	}
	return err;

@@ -1224,6 +1235,9 @@ int fuse_allow_current_process(struct fuse_conn *fc)
{
	const struct cred *cred;

	if (allow_sys_admin_access && capable(CAP_SYS_ADMIN))
		return 1;

	if (fc->allow_other)
		return current_in_userns(fc->user_ns);

+26 −13
Original line number Diff line number Diff line
@@ -210,13 +210,9 @@ void fuse_finish_open(struct inode *inode, struct file *file)
		fi->attr_version = atomic64_inc_return(&fc->attr_version);
		i_size_write(inode, 0);
		spin_unlock(&fi->lock);
		truncate_pagecache(inode, 0);
		file_update_time(file);
		fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);
	} else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) {
		invalidate_inode_pages2(inode->i_mapping);
	}

	if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache)
		fuse_link_write_file(file);
}
@@ -239,30 +235,38 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)
	if (err)
		return err;

	if (is_wb_truncate || dax_truncate) {
	if (is_wb_truncate || dax_truncate)
		inode_lock(inode);
		fuse_set_nowrite(inode);
	}

	if (dax_truncate) {
		filemap_invalidate_lock(inode->i_mapping);
		err = fuse_dax_break_layouts(inode, 0, 0);
		if (err)
			goto out;
			goto out_inode_unlock;
	}

	if (is_wb_truncate || dax_truncate)
		fuse_set_nowrite(inode);

	err = fuse_do_open(fm, get_node_id(inode), file, isdir);
	if (!err)
		fuse_finish_open(inode, file);

out:
	if (is_wb_truncate || dax_truncate)
		fuse_release_nowrite(inode);
	if (!err) {
		struct fuse_file *ff = file->private_data;

		if (fc->atomic_o_trunc && (file->f_flags & O_TRUNC))
			truncate_pagecache(inode, 0);
		else if (!(ff->open_flags & FOPEN_KEEP_CACHE))
			invalidate_inode_pages2(inode->i_mapping);
	}
	if (dax_truncate)
		filemap_invalidate_unlock(inode->i_mapping);

	if (is_wb_truncate | dax_truncate) {
		fuse_release_nowrite(inode);
out_inode_unlock:
	if (is_wb_truncate || dax_truncate)
		inode_unlock(inode);
	}

	return err;
}
@@ -338,6 +342,15 @@ static int fuse_open(struct inode *inode, struct file *file)

static int fuse_release(struct inode *inode, struct file *file)
{
	struct fuse_conn *fc = get_fuse_conn(inode);

	/*
	 * Dirty pages might remain despite write_inode_now() call from
	 * fuse_flush() due to writes racing with the close.
	 */
	if (fc->writeback_cache)
		write_inode_now(inode, 1);

	fuse_release_common(file, false);

	/* return value is ignored by VFS */
Loading