All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Runyu Xiao" <runyu.xiao@seu.edu.cn>
Cc: linux-scsi@vger.kernel.org
Subject: Re: [PATCH v2] scsi: target: file: avoid recursive configfs open in fd_init_prot()
Date: Tue, 18 Aug 2026 07:50:32 +0000	[thread overview]
Message-ID: <20260818075033.6E96F1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260818073539.1555605-1-runyu.xiao@seu.edu.cn>

Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [High] Double fetch of `d_name.len` allows concurrent rename to cause a heap buffer overflow and use-after-free
- [High] AB-BA deadlock between the directory inode lock (`i_rwsem`) and filesystem freeze protection (`sb_writers`)
- [High] Synthesizing a `struct path` using a bind mount's vfsmount and the underlying dcache parent creates an invalid VFS object state
- [High] Opening an existing protection sidecar file does not verify its file type, allowing an unprivileged user to panic the kernel
- [Medium] Usage of the unexported static function `lookup_one_qstr_excl()` causes build failures, and passing an uninitialized `qstr.hash` corrupts the dcache
- [Medium] Missing validation of the backing file's parent directory and hashed state after acquiring the parent inode lock
--

--- Patch [1]: [PATCH v2] scsi: target: file: avoid recursive configfs open in fd_init_prot() ---
commit dff9df1c1570ab279457bc3f5bedd541efae3c74
Author: Runyu Xiao <runyu.xiao@seu.edu.cn>
scsi: target: file: avoid recursive configfs open in fd_init_prot()

This commit addresses a potential recursive configfs open issue when creating
protection sidecar files. It introduces fd_open_prot_file() to lookup, and
either create or open the protection sidecar directly from the configured
backing file's parent directory, instead of using filp_open() which might
re-enter configfs.

> diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
> index 62ced9f5102ff..9872f8eda1ac4 100644
> --- a/drivers/target/target_core_file.c
> +++ b/drivers/target/target_core_file.c

[ ... ]

> @@ -86,6 +90,86 @@ static struct se_device *fd_alloc_device(struct se_hba *hba, const char *name)
>  	return &fd_dev->dev;
>  }
>  
> +static bool fd_backing_file_is_configfs(struct file *file)
> +{
> +	return d_real(file_dentry(file), D_REAL_DATA)->d_sb->s_magic ==
> +	       CONFIGFS_MAGIC;
> +}
> +
> +static int fd_open_prot_file(struct file *file, int flags, umode_t mode,
> +			     struct file **filep)
> +{
> +	struct dentry *backing_dentry = file_dentry(file);
> +	struct path parent_path = {
> +		.mnt = mntget(file->f_path.mnt),
> +		.dentry = dget_parent(backing_dentry),
> +	};

[Severity: High]
Does this create an invalid VFS object state if the backing file is a
file-level bind mount?

If the file was provided via a file-level bind mount, the vfsmount's root is
the file itself. Using dget_parent() escapes this root to the underlying parent
directory, and passing this disjoint path to dentry_open() or dentry_create()
could violate VFS invariants.

> +	struct path prot_path = { .mnt = parent_path.mnt };
> +	struct file *prot_file;
> +	struct qstr prot_qname;
> +	char *prot_name;
> +	size_t prot_name_len;
> +	int ret;
> +
> +	if (fd_backing_file_is_configfs(file)) {
> +		ret = -EINVAL;
> +		goto out_put_parent;
> +	}
> +
> +	prot_name_len = backing_dentry->d_name.len + strlen(".protection");
> +	if (prot_name_len > NAME_MAX) {
> +		ret = -ENAMETOOLONG;
> +		goto out_put_parent;
> +	}
> +
> +	prot_name = kmalloc(prot_name_len + 1, GFP_KERNEL);
> +	if (!prot_name) {
> +		ret = -ENOMEM;
> +		goto out_put_parent;
> +	}
> +
> +	memcpy(prot_name, backing_dentry->d_name.name, backing_dentry->d_name.len);

[Severity: High]
Is there a risk of a heap buffer overflow here?

The length of d_name is read without locks to allocate prot_name via kmalloc().

If an unprivileged user renames the backing file concurrently to a longer name,
couldn't the second fetch of d_name.len during memcpy() exceed the allocated
buffer size?

> +	memcpy(prot_name + backing_dentry->d_name.len, ".protection",
> +	       strlen(".protection") + 1);
> +	prot_qname = QSTR_INIT(prot_name, prot_name_len);
> +
> +	inode_lock_nested(d_inode(parent_path.dentry), I_MUTEX_PARENT);

[Severity: Medium]
Is there a missing check for concurrent renames or unlinks here?

The parent dentry was fetched locklessly via dget_parent(), but there is no
re-validation that backing_dentry->d_parent still matches parent_path.dentry,
or that the dentry is still hashed, after acquiring the inode lock.

> +	prot_path.dentry = lookup_one_qstr_excl(&prot_qname, parent_path.dentry,
> +						LOOKUP_CREATE);

[Severity: Medium]
Will this cause a build failure when compiled as a module?

lookup_one_qstr_excl() is a static function in fs/namei.c and is not exported
to modules.

Additionally, prot_qname is initialized above using QSTR_INIT() without
computing its hash. Does passing a qstr with a zero hash to low-level VFS
functions force dentries into bucket 0 and corrupt the dcache hash table?

> +	if (IS_ERR(prot_path.dentry)) {
> +		ret = PTR_ERR(prot_path.dentry);
> +		goto out_unlock;
> +	}
> +
> +	if (d_is_negative(prot_path.dentry)) {
> +		ret = mnt_want_write_file(file);

[Severity: High]
Does this invert the standard VFS lock ordering and risk an AB-BA deadlock?

The directory's i_rwsem is currently held via inode_lock_nested(), and
mnt_want_write_file() internally acquires sb_writers (read). The standard
order is sb_writers before i_rwsem, so this could deadlock against a
filesystem freeze which takes sb_writers exclusively.

> +		if (ret)
> +			goto out_dput;
> +
> +		prot_file = dentry_create(&prot_path, flags, mode, current_cred());
> +		mnt_drop_write_file(file);
> +	} else {
> +		prot_file = dentry_open(&prot_path, flags & ~O_CREAT,
> +					current_cred());

[Severity: High]
Is there a check missing to ensure the existing protection sidecar is a regular
file?

If an unprivileged user preemptively creates a symlink named with .protection,
opening it directly via dentry_open() triggers a WARN_ON in do_dentry_open()
because symlinks generally have a NULL i_fop. This can panic the kernel on
systems with panic_on_warn.

> +	}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260818073539.1555605-1-runyu.xiao@seu.edu.cn?part=1

  reply	other threads:[~2026-08-18  7:50 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-18  4:58 [PATCH] scsi: target: file: reject configfs-backed paths in configfs stores Runyu Xiao
2026-08-18  5:12 ` sashiko-bot
2026-08-18  7:35 ` [PATCH v2] scsi: target: file: avoid recursive configfs open in fd_init_prot() Runyu Xiao
2026-08-18  7:50   ` sashiko-bot [this message]
2026-08-18  9:13   ` [PATCH v3] " Runyu Xiao
2026-08-18  9:27     ` sashiko-bot
2026-08-18 10:16     ` [PATCH v4] " Runyu Xiao

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=20260818075033.6E96F1F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-scsi@vger.kernel.org \
    --cc=runyu.xiao@seu.edu.cn \
    --cc=sashiko-reviews@lists.linux.dev \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.