The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: Christian Brauner <brauner@kernel.org>
To: linux-fsdevel@vger.kernel.org
Cc: Jann Horn <jannh@google.com>, Chris Mason <mason@kernel.org>,
	 Josef Bacik <josef@toxicpanda.com>,
	Christoph Hellwig <hch@lst.de>,
	 linux-block@vger.kernel.org, linux-kernel@vger.kernel.org,
	 linux-nvme@lists.infradead.org,
	Alexander Viro <viro@zeniv.linux.org.uk>,
	 Jan Kara <jack@suse.cz>,
	linux-scsi@vger.kernel.org,  target-devel@vger.kernel.org,
	driver-core@lists.linux.dev,  linux-mm@kvack.org,
	bpf@vger.kernel.org,  linux-security-module@vger.kernel.org,
	selinux@vger.kernel.org,  apparmor@lists.ubuntu.com,
	linux-nfs@vger.kernel.org,  netdev@vger.kernel.org,
	fuse-devel@lists.linux.dev,  linux-efi@vger.kernel.org,
	xen-devel@lists.xenproject.org,  ocfs2-devel@lists.linux.dev,
	linux-s390@vger.kernel.org,  linuxppc-dev@lists.ozlabs.org,
	linux-usb@vger.kernel.org,  Amir Goldstein <amir73il@gmail.com>,
	linux-erofs@lists.ozlabs.org,  linux-kselftest@vger.kernel.org,
	 "Christian Brauner (Amutable)" <brauner@kernel.org>
Subject: [PATCH RFC v2 33/42] fs: add file_check_backing()
Date: Sat, 14 Nov 2026 14:02:14 +0100	[thread overview]
Message-ID: <20261114-work-loop-pseudo-v2-33-98639a2286fb@kernel.org> (raw)
In-Reply-To: <20261114-work-loop-pseudo-v2-0-98639a2286fb@kernel.org>

Loop, zloop, the nvme target, the scsi target, fuse passthrough, the usb
mass storage gadget, erofs and acct all take a file from userspace and
use it as storage. Currently, each of them has its own idea of what to
check before using a file like that:

- loop and the gadget check for a regular file or a block device, the
  nvme target, the scsi target and zloop take any file type
- loop checks the open mode, the gadget checks FMODE_CAN_READ, the nvme
  target, the scsi target and zloop only check for ->read_iter() and
  ->write_iter()
- apart from acct which refuses procfs and sysfs files none of them
  cares whether the file is a kernel interface

Add a helper that does all of it in one place. It only accepts regular
files and, if the caller passes S_IFBLK, block devices. It refuses files
on pseudo filesystems. And it checks that the file is open for what the
caller is going to do with it and implements ->read_iter() for reading
and ->write_iter() for writing.

O_PATH files are refused unless the caller passes FMODE_PATH. That is
for callers that only need the path of the file. fuse passthrough is
one of them and has accepted O_PATH file descriptors since commit
efad7153bf93 ("fuse: allow O_PATH fd for FUSE_DEV_IOC_BACKING_OPEN").

Block devices are fine even though bdevfs is a pseudo filesystem. A
block device that userspace hands in is a device node on devtmpfs or
some other filesystem. Files on bdevfs only come from
bdev_file_open_by_*() and never end up here.

The pseudo filesystem check looks at the inode that actually provides
the data via d_real_inode(). overlayfs accepts sysfs, cgroup2, debugfs,
tracefs, configfs and securityfs as lower layers and reads of such an
overlayfs file are served by the file on the pseudo filesystem. Checking
the overlayfs inode would miss that.

Note that the file type is taken from the inode of the file and not from
the host of ->f_mapping like loop and the gadget do it. A driver can
point ->f_mapping somewhere else in ->open(). It makes no difference for
block devices as both inodes are block devices.

No functional changes. Nothing calls the helper yet.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/read_write.c    | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fs.h |  2 ++
 2 files changed, 50 insertions(+)

diff --git a/fs/read_write.c b/fs/read_write.c
index 6767283108991..cedbe654add65 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -866,6 +866,54 @@ static ssize_t do_loop_readv_writev(struct file *filp, struct iov_iter *iter,
 	return ret;
 }
 
+/**
+ * file_check_backing - check that the kernel can use a file as storage
+ * @file: file that userspace handed in
+ * @mode: FMODE_READ and/or FMODE_WRITE the kernel will do I/O with, or
+ *        FMODE_PATH if only the path of @file is used
+ * @type: S_IFBLK if a block device is fine as well, 0 otherwise
+ *
+ * Call this where a file from userspace is taken as a backing file. Only
+ * regular files and, if @type is S_IFBLK, block devices are storage. Files
+ * on pseudo filesystems are kernel interfaces and are refused. The file
+ * must be open for @mode and implement ->read_iter() for reading and
+ * ->write_iter() for writing. O_PATH files are only fine with FMODE_PATH.
+ *
+ * Return: 0 on success, -EBADF if @file isn't open for @mode, -EINVAL if it
+ * can't be used as storage.
+ */
+int file_check_backing(const struct file *file, fmode_t mode, umode_t type)
+{
+	fmode_t io = mode & (FMODE_READ | FMODE_WRITE);
+	/* Drivers may point ->f_mapping at something else. */
+	umode_t fmt = file_inode(file)->i_mode & S_IFMT;
+
+	/* O_PATH files are only fine for callers that just use the path. */
+	if ((file->f_mode & FMODE_PATH) && !(mode & FMODE_PATH))
+		return -EBADF;
+
+	if ((file->f_mode & io) != io)
+		return -EBADF;
+
+	if (fmt != S_IFREG && !(type == S_IFBLK && fmt == S_IFBLK))
+		return -EINVAL;
+
+	/* Look through stacking filesystems at the inode with the data. */
+	if (d_real_inode(file->f_path.dentry)->i_sb->s_iflags & SB_I_PSEUDO)
+		return -EINVAL;
+
+	if ((mode & FMODE_READ) &&
+	    (!(file->f_mode & FMODE_CAN_READ) || !file->f_op->read_iter))
+		return -EINVAL;
+
+	if ((mode & FMODE_WRITE) &&
+	    (!(file->f_mode & FMODE_CAN_WRITE) || !file->f_op->write_iter))
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(file_check_backing);
+
 ssize_t vfs_iocb_iter_read(struct file *file, struct kiocb *iocb,
 			   struct iov_iter *iter)
 {
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 99caef43ff3c4..b1c7ebafbadbe 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2553,6 +2553,8 @@ static inline const struct inode *file_user_inode(const struct file *f)
 	return file_inode(f);
 }
 
+int file_check_backing(const struct file *file, fmode_t mode, umode_t type);
+
 static inline struct file *file_clone_open(struct file *file)
 {
 	return dentry_open(&file->f_path, file->f_flags, file->f_cred);

-- 
2.53.0


  parent reply	other threads:[~2026-11-14 13:09 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-14 13:01 [PATCH RFC v2 00/42] fs: don't use pseudo filesystem files as storage Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 01/42] loop: refuse backing files that aren't open for reading Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 02/42] nvmet: check that the file backend supports ->{read,write}_iter Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 03/42] target: fileio: check that the backing file " Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 04/42] fs: add SB_I_PSEUDO Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 05/42] kernfs: use fc_init_pseudo() Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 06/42] proc: " Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 07/42] binfmt_misc: " Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 08/42] mqueue: " Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 09/42] bpf: use fc_init_pseudo() for bpffs Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 10/42] nullfs: use fc_init_pseudo() Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 11/42] failfs: " Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 12/42] rust_binderfs: " Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 13/42] devpts: " Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 14/42] debugfs: " Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 15/42] tracefs: " Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 16/42] configfs: " Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 17/42] securityfs: " Christian Brauner
2026-11-14 13:01 ` [PATCH RFC v2 18/42] selinux: use fc_init_pseudo() for selinuxfs Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 19/42] smack: use fc_init_pseudo() for smackfs Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 20/42] apparmor: use fc_init_pseudo() for apparmorfs Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 21/42] nfsd: use fc_init_pseudo() Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 22/42] sunrpc: use fc_init_pseudo() for rpc_pipefs Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 23/42] fuse: use fc_init_pseudo() for fusectl Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 24/42] efivarfs: use fc_init_pseudo() Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 25/42] pstore: " Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 26/42] xenfs: " Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 27/42] openpromfs: " Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 28/42] ocfs2: use fc_init_pseudo() for dlmfs Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 29/42] s390/hypfs: use fc_init_pseudo() Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 30/42] powerpc/spufs: " Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 31/42] usb: gadget: f_fs: " Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 32/42] usb: gadget: gadgetfs: " Christian Brauner
2026-11-14 13:02 ` Christian Brauner [this message]
2026-11-14 13:02 ` [PATCH RFC v2 34/42] loop: refuse files on pseudo filesystems as backing files Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 35/42] zloop: refuse zone files on pseudo filesystems Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 36/42] nvmet: refuse files on pseudo filesystems as namespace backing files Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 37/42] target: fileio: refuse files on pseudo filesystems as " Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 38/42] fuse: refuse files on pseudo filesystems as passthrough " Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 39/42] usb: gadget: f_mass_storage: refuse files on pseudo filesystems Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 40/42] erofs: refuse files on pseudo filesystems as backing files Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 41/42] acct: refuse files on pseudo filesystems Christian Brauner
2026-11-14 13:02 ` [PATCH RFC v2 42/42] selftests/filesystems: add backing file tests for pseudo files Christian Brauner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261114-work-loop-pseudo-v2-33-98639a2286fb@kernel.org \
    --to=brauner@kernel.org \
    --cc=amir73il@gmail.com \
    --cc=apparmor@lists.ubuntu.com \
    --cc=bpf@vger.kernel.org \
    --cc=driver-core@lists.linux.dev \
    --cc=fuse-devel@lists.linux.dev \
    --cc=hch@lst.de \
    --cc=jack@suse.cz \
    --cc=jannh@google.com \
    --cc=josef@toxicpanda.com \
    --cc=linux-block@vger.kernel.org \
    --cc=linux-efi@vger.kernel.org \
    --cc=linux-erofs@lists.ozlabs.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=linux-nfs@vger.kernel.org \
    --cc=linux-nvme@lists.infradead.org \
    --cc=linux-s390@vger.kernel.org \
    --cc=linux-scsi@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=linuxppc-dev@lists.ozlabs.org \
    --cc=mason@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=ocfs2-devel@lists.linux.dev \
    --cc=selinux@vger.kernel.org \
    --cc=target-devel@vger.kernel.org \
    --cc=viro@zeniv.linux.org.uk \
    --cc=xen-devel@lists.xenproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox