All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v7 0/4] nvmet: avoid recursive configfs open
@ 2026-09-27  8:20 Runyu Xiao
  2026-09-27  8:20 ` [PATCH v7 1/4] fs: configfs: add helpers for opening non-configfs paths Runyu Xiao
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Runyu Xiao @ 2026-09-27  8:20 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Breno Leitao, Andreas Hindborg, Sagi Grimberg, Chaitanya Kulkarni,
	Keith Busch, Logan Gunthorpe, Martin K . Petersen, Lee Duncan,
	Hannes Reinecke, Nicholas Bellinger, linux-nvme, linux-scsi,
	target-devel, linux-kernel, stable, Runyu Xiao, Jianhao Xu

Several configfs store callbacks open user-configured paths while holding
the item's frag_sem.  If a configured path resolves into configfs, the open
can re-enter __configfs_open_file() and try to acquire the same
non-recursive semaphore again.

Add helpers in configfs for opening non-configfs paths, then use them in the
nvmet file-backed namespace and passthru paths.  The series also pins the
target-core db_root path and uses the root-relative helper for ALUA and APTPL
metadata, with a dedicated lock protecting the path and its configuration.

Changes since v6:
- Honor O_NOFOLLOW when resolving paths in configfs_file_open().
- Protect db_root and db_root_path against concurrent metadata writes and
  configfs updates.
- Return an error when a db_root update races with target device creation.

Checks:
- scripts/checkpatch.pl --strict: all 4 patches pass.
- Built the affected configfs, nvmet, and target-core objects.
- Applied the complete series to the stated base commit with git am.

Runyu Xiao (4):
  fs: configfs: add helpers for opening non-configfs paths
  nvmet: avoid recursive configfs open for file-backed namespaces
  nvmet: avoid recursive configfs open for passthru
  scsi: target: pin db_root for metadata writes

 drivers/nvme/target/io-cmd-file.c     |   3 +-
 drivers/nvme/target/passthru.c        |   3 +-
 drivers/target/target_core_alua.c     |  42 ++++----
 drivers/target/target_core_configfs.c | 142 +++++++++++++++++++++-----
 drivers/target/target_core_internal.h |   5 +
 drivers/target/target_core_pr.c       |  19 ++--
 fs/configfs/mount.c                   |  57 +++++++++++
 include/linux/configfs.h              |   5 +
 8 files changed, 219 insertions(+), 57 deletions(-)


base-commit: f741eaa40d2a8e199a31ceb683f30867537def70
-- 
2.34.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v7 1/4] fs: configfs: add helpers for opening non-configfs paths
  2026-09-27  8:20 [PATCH v7 0/4] nvmet: avoid recursive configfs open Runyu Xiao
@ 2026-09-27  8:20 ` Runyu Xiao
  2026-09-27  8:39   ` sashiko-bot
  2026-09-27  8:20 ` [PATCH v7 2/4] nvmet: avoid recursive configfs open for file-backed namespaces Runyu Xiao
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 7+ messages in thread
From: Runyu Xiao @ 2026-09-27  8:20 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Breno Leitao, Andreas Hindborg, Sagi Grimberg, Chaitanya Kulkarni,
	Keith Busch, Logan Gunthorpe, Martin K . Petersen, Lee Duncan,
	Hannes Reinecke, Nicholas Bellinger, linux-nvme, linux-scsi,
	target-devel, linux-kernel, stable, Runyu Xiao, Jianhao Xu

Configfs store callbacks hold frag_sem while they run.  Reopening a path
that resolves to configfs from such a callback can acquire the same
non-recursive semaphore again.

Add configfs_file_open() for configured pathnames and configfs_open_root()
for paths relative to a resolved root.  Reject configfs roots before using
file_open_root() so the normal open checks remain in place.

Assisted-by: LLM Codex
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
 fs/configfs/mount.c      | 57 ++++++++++++++++++++++++++++++++++++++++
 include/linux/configfs.h |  5 ++++
 2 files changed, 62 insertions(+)

diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index d8cac1cbf3bd5..aa79be05dd5bc 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/mount.h>
 #include <linux/fs_context.h>
+#include <linux/namei.h>
 #include <linux/pagemap.h>
 #include <linux/init.h>
 #include <linux/slab.h>
@@ -118,6 +119,62 @@ static struct file_system_type configfs_fs_type = {
 };
 MODULE_ALIAS_FS("configfs");
 
+/**
+ * configfs_open_root - open a path below a non-configfs root
+ * @root: resolved root path
+ * @name: path relative to @root
+ * @flags: open flags
+ * @mode: mode for a newly created file
+ *
+ * Use this from configfs store callbacks with a resolved non-configfs root
+ * to avoid re-entering configfs while the callback holds its fragment
+ * semaphore.
+ *
+ * Return: opened file, or an ERR_PTR() value.  Returns -EINVAL if @root
+ * is on configfs.
+ */
+struct file *configfs_open_root(const struct path *root, const char *name,
+				int flags, umode_t mode)
+{
+	if (root->dentry->d_sb->s_type == &configfs_fs_type)
+		return ERR_PTR(-EINVAL);
+
+	return file_open_root(root, name, flags, mode);
+}
+EXPORT_SYMBOL_GPL(configfs_open_root);
+
+/**
+ * configfs_file_open - open an existing non-configfs pathname
+ * @filename: pathname to open; it must already exist
+ * @flags: open flags for the existing pathname
+ * @mode: unused; creation is not supported
+ *
+ * Resolve @filename and reject configfs paths.  Use this from configfs
+ * store callbacks for existing configured paths.  O_NOFOLLOW is honored
+ * when resolving the final path component.
+ *
+ * Return: opened file, or an ERR_PTR() value.  Returns -EINVAL if the
+ * resolved path is on configfs.
+ */
+struct file *configfs_file_open(const char *filename, int flags, umode_t mode)
+{
+	struct file *file;
+	struct path path;
+	unsigned int lookup_flags = 0;
+	int ret;
+
+	if (!(flags & O_NOFOLLOW))
+		lookup_flags = LOOKUP_FOLLOW;
+	ret = kern_path(filename, lookup_flags, &path);
+	if (ret)
+		return ERR_PTR(ret);
+
+	file = configfs_open_root(&path, "", flags, mode);
+	path_put(&path);
+	return file;
+}
+EXPORT_SYMBOL_GPL(configfs_file_open);
+
 struct dentry *configfs_pin_fs(void)
 {
 	int err = simple_pin_fs(&configfs_fs_type, &configfs_mount,
diff --git a/include/linux/configfs.h b/include/linux/configfs.h
index ef65c75beeaad..2a803bb836b4d 100644
--- a/include/linux/configfs.h
+++ b/include/linux/configfs.h
@@ -34,6 +34,8 @@ struct configfs_group_operations;
 struct configfs_attribute;
 struct configfs_bin_attribute;
 struct configfs_subsystem;
+struct file;
+struct path;
 
 struct config_item {
 	char			*ci_name;
@@ -243,6 +245,9 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys);
 int configfs_register_group(struct config_group *parent_group,
 			    struct config_group *group);
 void configfs_unregister_group(struct config_group *group);
+struct file *configfs_open_root(const struct path *root, const char *name,
+				int flags, umode_t mode);
+struct file *configfs_file_open(const char *filename, int flags, umode_t mode);
 
 void configfs_remove_default_groups(struct config_group *group);
 
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v7 2/4] nvmet: avoid recursive configfs open for file-backed namespaces
  2026-09-27  8:20 [PATCH v7 0/4] nvmet: avoid recursive configfs open Runyu Xiao
  2026-09-27  8:20 ` [PATCH v7 1/4] fs: configfs: add helpers for opening non-configfs paths Runyu Xiao
@ 2026-09-27  8:20 ` Runyu Xiao
  2026-09-27  8:20 ` [PATCH v7 3/4] nvmet: avoid recursive configfs open for passthru Runyu Xiao
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Runyu Xiao @ 2026-09-27  8:20 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Breno Leitao, Andreas Hindborg, Sagi Grimberg, Chaitanya Kulkarni,
	Keith Busch, Logan Gunthorpe, Martin K . Petersen, Lee Duncan,
	Hannes Reinecke, Nicholas Bellinger, linux-nvme, linux-scsi,
	target-devel, linux-kernel, stable, Runyu Xiao, Jianhao Xu

nvmet_ns_enable_store() runs as a configfs store callback while configfs
holds the item frag_sem. File-backed namespace enable used filp_open() on
the configured device_path, so a path into configfs could re-enter
__configfs_open_file() and try to acquire the same semaphore again.

Use configfs_file_open() so the path is resolved before opening,
configfs-backed paths are rejected, and the resolved path is opened with
file_open_root() while retaining the normal open-time permission and
security checks.

Fixes: d5eff33ee6f8 ("nvmet: add simple file backed ns support")
Cc: stable@vger.kernel.org
Reviewed-by: Christoph Hellwig <hch@lst.de>
Assisted-by: LLM Codex
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
 drivers/nvme/target/io-cmd-file.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/nvme/target/io-cmd-file.c b/drivers/nvme/target/io-cmd-file.c
index 0b22d183f9279..2a4f25de94ba1 100644
--- a/drivers/nvme/target/io-cmd-file.c
+++ b/drivers/nvme/target/io-cmd-file.c
@@ -8,6 +8,7 @@
 #include <linux/uio.h>
 #include <linux/falloc.h>
 #include <linux/file.h>
+#include <linux/configfs.h>
 #include <linux/fs.h>
 #include "nvmet.h"
 
@@ -38,7 +39,7 @@ int nvmet_file_ns_enable(struct nvmet_ns *ns)
 	if (!ns->buffered_io)
 		flags |= O_DIRECT;
 
-	ns->file = filp_open(ns->device_path, flags, 0);
+	ns->file = configfs_file_open(ns->device_path, flags, 0);
 	if (IS_ERR(ns->file)) {
 		ret = PTR_ERR(ns->file);
 		pr_err("failed to open file %s: (%d)\n",
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v7 3/4] nvmet: avoid recursive configfs open for passthru
  2026-09-27  8:20 [PATCH v7 0/4] nvmet: avoid recursive configfs open Runyu Xiao
  2026-09-27  8:20 ` [PATCH v7 1/4] fs: configfs: add helpers for opening non-configfs paths Runyu Xiao
  2026-09-27  8:20 ` [PATCH v7 2/4] nvmet: avoid recursive configfs open for file-backed namespaces Runyu Xiao
@ 2026-09-27  8:20 ` Runyu Xiao
  2026-09-27  8:20 ` [PATCH v7 4/4] scsi: target: pin db_root for metadata writes Runyu Xiao
  2026-09-28  5:12 ` [PATCH v7 0/4] nvmet: avoid recursive configfs open Christoph Hellwig
  4 siblings, 0 replies; 7+ messages in thread
From: Runyu Xiao @ 2026-09-27  8:20 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Breno Leitao, Andreas Hindborg, Sagi Grimberg, Chaitanya Kulkarni,
	Keith Busch, Logan Gunthorpe, Martin K . Petersen, Lee Duncan,
	Hannes Reinecke, Nicholas Bellinger, linux-nvme, linux-scsi,
	target-devel, linux-kernel, stable, Runyu Xiao, Jianhao Xu

nvmet_passthru_enable_store() runs as a configfs store callback while
configfs holds the item frag_sem. Passthru enable used filp_open() on the
configured controller path, so a path into configfs could re-enter
__configfs_open_file() and try to acquire the same semaphore again.

Use configfs_file_open() so the path is resolved before opening,
configfs-backed paths are rejected, and the resolved path is opened with
file_open_root() while retaining the normal open-time permission and
security checks.

Fixes: cae5b01a2afc ("nvmet: introduce the passthru configfs interface")
Cc: stable@vger.kernel.org
Reviewed-by: Christoph Hellwig <hch@lst.de>
Assisted-by: LLM Codex
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
 drivers/nvme/target/passthru.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/nvme/target/passthru.c b/drivers/nvme/target/passthru.c
index fa6527c537e26..d60256004e6cf 100644
--- a/drivers/nvme/target/passthru.c
+++ b/drivers/nvme/target/passthru.c
@@ -9,6 +9,7 @@
  */
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 #include <linux/module.h>
+#include <linux/configfs.h>
 
 #include "../host/nvme.h"
 #include "nvmet.h"
@@ -602,7 +603,7 @@ int nvmet_passthru_ctrl_enable(struct nvmet_subsys *subsys)
 		goto out_unlock;
 	}
 
-	file = filp_open(subsys->passthru_ctrl_path, O_RDWR, 0);
+	file = configfs_file_open(subsys->passthru_ctrl_path, O_RDWR, 0);
 	if (IS_ERR(file)) {
 		ret = PTR_ERR(file);
 		goto out_unlock;
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v7 4/4] scsi: target: pin db_root for metadata writes
  2026-09-27  8:20 [PATCH v7 0/4] nvmet: avoid recursive configfs open Runyu Xiao
                   ` (2 preceding siblings ...)
  2026-09-27  8:20 ` [PATCH v7 3/4] nvmet: avoid recursive configfs open for passthru Runyu Xiao
@ 2026-09-27  8:20 ` Runyu Xiao
  2026-09-28  5:12 ` [PATCH v7 0/4] nvmet: avoid recursive configfs open Christoph Hellwig
  4 siblings, 0 replies; 7+ messages in thread
From: Runyu Xiao @ 2026-09-27  8:20 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Breno Leitao, Andreas Hindborg, Sagi Grimberg, Chaitanya Kulkarni,
	Keith Busch, Logan Gunthorpe, Martin K . Petersen, Lee Duncan,
	Hannes Reinecke, Nicholas Bellinger, linux-nvme, linux-scsi,
	target-devel, linux-kernel, stable, Runyu Xiao, Jianhao Xu

ALUA and persistent reservation metadata files are derived from the
configurable db_root string and opened with filp_open().  If db_root
points at configfs, a metadata update from a configfs store callback can
re-enter configfs while the callback still holds frag_sem.

A one-time pathname check can also be bypassed by retargeting a symlink.
Resolve db_root once and retain the resulting path while target devices
use it.  Use configfs_open_root() both to reject configfs roots and to
open metadata files relative to the pinned root.

Protect db_root and db_root_path with a dedicated mutex while metadata
paths are opened or the configured root is replaced.  Return an error if
target devices appear before a validated root can be published.

Resolve a new root outside target_devices_lock and recheck the device count
before publishing it, so the path walk does not occur under that lock.

Fixes: fdddf932269a ("target: use new "dbroot" target attribute")
Link: https://lore.kernel.org/r/20260818051442.1523210-1-runyu.xiao@seu.edu.cn
Cc: stable@vger.kernel.org
Assisted-by: LLM Codex
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
 drivers/target/target_core_alua.c     |  42 ++++----
 drivers/target/target_core_configfs.c | 142 +++++++++++++++++++++-----
 drivers/target/target_core_internal.h |   5 +
 drivers/target/target_core_pr.c       |  19 ++--
 4 files changed, 153 insertions(+), 55 deletions(-)

diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c
index 140154d93c430..c39e57c1ebf9e 100644
--- a/drivers/target/target_core_alua.c
+++ b/drivers/target/target_core_alua.c
@@ -18,7 +18,6 @@
 #include <linux/fcntl.h>
 #include <linux/file.h>
 #include <linux/fs.h>
-#include <linux/fs_struct.h>
 #include <linux/kthread.h>
 #include <scsi/scsi_proto.h>
 #include <linux/unaligned.h>
@@ -862,15 +861,18 @@ static int core_alua_write_tpg_metadata(
 	loff_t pos = 0;
 	int ret;
 
-	if (tsk_is_kthread(current)) {
-		scoped_with_init_fs()
-			file = filp_open(path, O_RDWR | O_CREAT | O_TRUNC, 0600);
-	} else {
-		file = filp_open(path, O_RDWR | O_CREAT | O_TRUNC, 0600);
+	mutex_lock(&db_root_lock);
+	if (!db_root_path.dentry) {
+		mutex_unlock(&db_root_lock);
+		pr_err("db_root is not initialized for ALUA metadata path: %s\n",
+		       path);
+		return -ENODEV;
 	}
-
+	file = configfs_open_root(&db_root_path, path,
+				  O_RDWR | O_CREAT | O_TRUNC, 0600);
+	mutex_unlock(&db_root_lock);
 	if (IS_ERR(file)) {
-		pr_err("filp_open(%s) for ALUA metadata failed\n", path);
+		pr_err("configfs_open_root(%s) for ALUA metadata failed\n", path);
 		return -ENODEV;
 	}
 	ret = kernel_write(file, md_buf, md_buf_len, &pos);
@@ -905,9 +907,9 @@ static int core_alua_update_tpg_primary_metadata(
 			tg_pt_gp->tg_pt_gp_alua_access_status);
 
 	rc = -ENOMEM;
-	path = kasprintf(GFP_KERNEL, "%s/alua/tpgs_%s/%s", db_root,
-			&wwn->unit_serial[0],
-			config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item));
+	path = kasprintf(GFP_KERNEL, "alua/tpgs_%s/%s",
+			 &wwn->unit_serial[0],
+			 config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item));
 	if (path) {
 		rc = core_alua_write_tpg_metadata(path, md_buf, len);
 		kfree(path);
@@ -1196,16 +1198,16 @@ static int core_alua_update_tpg_secondary_metadata(struct se_lun *lun)
 			lun->lun_tg_pt_secondary_stat);
 
 	if (se_tpg->se_tpg_tfo->tpg_get_tag != NULL) {
-		path = kasprintf(GFP_KERNEL, "%s/alua/%s/%s+%hu/lun_%llu",
-				db_root, se_tpg->se_tpg_tfo->fabric_name,
-				se_tpg->se_tpg_tfo->tpg_get_wwn(se_tpg),
-				se_tpg->se_tpg_tfo->tpg_get_tag(se_tpg),
-				lun->unpacked_lun);
+		path = kasprintf(GFP_KERNEL, "alua/%s/%s+%hu/lun_%llu",
+				 se_tpg->se_tpg_tfo->fabric_name,
+				 se_tpg->se_tpg_tfo->tpg_get_wwn(se_tpg),
+				 se_tpg->se_tpg_tfo->tpg_get_tag(se_tpg),
+				 lun->unpacked_lun);
 	} else {
-		path = kasprintf(GFP_KERNEL, "%s/alua/%s/%s/lun_%llu",
-				db_root, se_tpg->se_tpg_tfo->fabric_name,
-				se_tpg->se_tpg_tfo->tpg_get_wwn(se_tpg),
-				lun->unpacked_lun);
+		path = kasprintf(GFP_KERNEL, "alua/%s/%s/lun_%llu",
+				 se_tpg->se_tpg_tfo->fabric_name,
+				 se_tpg->se_tpg_tfo->tpg_get_wwn(se_tpg),
+				 lun->unpacked_lun);
 	}
 	if (!path) {
 		rc = -ENOMEM;
diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c
index 2b19a956007b7..31cd1ed42b135 100644
--- a/drivers/target/target_core_configfs.c
+++ b/drivers/target/target_core_configfs.c
@@ -96,57 +96,126 @@ static ssize_t target_core_item_version_show(struct config_item *item,
 CONFIGFS_ATTR_RO(target_core_item_, version);
 
 char db_root[DB_ROOT_LEN] = DB_ROOT_DEFAULT;
-static char db_root_stage[DB_ROOT_LEN];
+struct path db_root_path;
+/* Protect db_root and db_root_path. */
+DEFINE_MUTEX(db_root_lock);
+
+static int target_validate_db_root(const char *path_str, struct path *path)
+{
+	struct file *file;
+	int ret;
+
+	ret = kern_path(path_str, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, path);
+	if (ret) {
+		pr_err("db_root: cannot open: %s\n", path_str);
+		if (ret == -ENOTDIR)
+			pr_err("db_root: not a directory: %s\n", path_str);
+		return ret;
+	}
+
+	file = configfs_open_root(path, "", O_RDONLY, 0);
+	if (IS_ERR(file)) {
+		ret = PTR_ERR(file);
+		path_put(path);
+		*path = (struct path){};
+		if (ret != -EINVAL)
+			return ret;
+
+		pr_err("db_root: configfs is not a valid target database root: %s\n",
+		       path_str);
+		return -EINVAL;
+	}
+
+	path_put(path);
+	*path = file->f_path;
+	path_get(path);
+	fput(file);
+
+	return 0;
+}
 
 static ssize_t target_core_item_dbroot_show(struct config_item *item,
 					    char *page)
 {
-	return sprintf(page, "%s\n", db_root);
+	ssize_t ret;
+
+	mutex_lock(&db_root_lock);
+	ret = sprintf(page, "%s\n", db_root);
+	mutex_unlock(&db_root_lock);
+
+	return ret;
 }
 
 static ssize_t target_core_item_dbroot_store(struct config_item *item,
 					const char *page, size_t count)
 {
+	char *db_root_stage;
 	ssize_t read_bytes;
 	ssize_t r = -EINVAL;
 	struct path path = {};
+	struct path old_path = {};
+	bool have_old_path = false;
 
 	mutex_lock(&target_devices_lock);
 	if (target_devices) {
 		pr_err("db_root: cannot be changed because it's in use\n");
-		goto unlock;
+		mutex_unlock(&target_devices_lock);
+		return r;
 	}
+	mutex_unlock(&target_devices_lock);
 
 	if (count > (DB_ROOT_LEN - 1)) {
 		pr_err("db_root: count %d exceeds DB_ROOT_LEN-1: %u\n",
 		       (int)count, DB_ROOT_LEN - 1);
-		goto unlock;
+		return r;
 	}
 
+	db_root_stage = kmalloc(DB_ROOT_LEN, GFP_KERNEL);
+	if (!db_root_stage)
+		return -ENOMEM;
+
 	read_bytes = scnprintf(db_root_stage, DB_ROOT_LEN, "%s", page);
 	if (!read_bytes)
-		goto unlock;
+		goto free_stage;
 
 	if (db_root_stage[read_bytes - 1] == '\n')
 		db_root_stage[read_bytes - 1] = '\0';
 
 	/* validate new db root before accepting it */
-	r = kern_path(db_root_stage, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
-	if (r) {
-		pr_err("db_root: cannot open: %s\n", db_root_stage);
-		if (r == -ENOTDIR)
-			pr_err("db_root: not a directory: %s\n", db_root_stage);
-		goto unlock;
+	r = target_validate_db_root(db_root_stage, &path);
+	if (r)
+		goto free_stage;
+
+	mutex_lock(&target_devices_lock);
+	if (target_devices) {
+		pr_err("db_root: cannot be changed because it's in use\n");
+		r = -EINVAL;
+		goto unlock_put;
 	}
-	path_put(&path);
 
+	mutex_lock(&db_root_lock);
+	have_old_path = db_root_path.dentry;
+	if (have_old_path)
+		old_path = db_root_path;
+	db_root_path = path;
+	path = (struct path){};
 	strscpy(db_root, db_root_stage);
 	pr_debug("Target_Core_ConfigFS: db_root set to %s\n", db_root);
+	mutex_unlock(&db_root_lock);
 
 	r = read_bytes;
 
-unlock:
+unlock_put:
 	mutex_unlock(&target_devices_lock);
+	if (path.dentry)
+		path_put(&path);
+	if (have_old_path)
+		path_put(&old_path);
+	kfree(db_root_stage);
+	return r;
+
+free_stage:
+	kfree(db_root_stage);
 	return r;
 }
 
@@ -3722,23 +3791,37 @@ void target_setup_backend_cits(struct target_backend *tb)
 
 static void target_init_dbroot(void)
 {
-	struct file *fp;
+	const char *db_root_stage;
+	struct path path = {};
+	int ret;
 
-	snprintf(db_root_stage, DB_ROOT_LEN, DB_ROOT_PREFERRED);
-	fp = filp_open(db_root_stage, O_RDONLY, 0);
-	if (IS_ERR(fp)) {
-		pr_err("db_root: cannot open: %s\n", db_root_stage);
-		return;
-	}
-	if (!S_ISDIR(file_inode(fp)->i_mode)) {
-		filp_close(fp, NULL);
-		pr_err("db_root: not a valid directory: %s\n", db_root_stage);
-		return;
+	db_root_stage = DB_ROOT_PREFERRED;
+	ret = target_validate_db_root(db_root_stage, &path);
+	if (ret) {
+		db_root_stage = DB_ROOT_DEFAULT;
+		ret = target_validate_db_root(db_root_stage, &path);
+		if (ret)
+			return;
 	}
-	filp_close(fp, NULL);
 
+	mutex_lock(&db_root_lock);
+	db_root_path = path;
 	strscpy(db_root, db_root_stage);
 	pr_debug("Target_Core_ConfigFS: db_root set to %s\n", db_root);
+	mutex_unlock(&db_root_lock);
+}
+
+static void target_release_db_root(void)
+{
+	struct path path;
+
+	mutex_lock(&db_root_lock);
+	path = db_root_path;
+	db_root_path = (struct path){};
+	mutex_unlock(&db_root_lock);
+
+	if (path.dentry)
+		path_put(&path);
 }
 
 static int __init target_core_init_configfs(void)
@@ -3797,6 +3880,10 @@ static int __init target_core_init_configfs(void)
 	/*
 	 * Register the target_core_mod subsystem with configfs.
 	 */
+	/* Resolve db_root before making the configfs attributes visible. */
+	scoped_with_kernel_creds()
+		target_init_dbroot();
+
 	ret = configfs_register_subsystem(subsys);
 	if (ret < 0) {
 		pr_err("Error %d while registering subsystem %s\n",
@@ -3821,9 +3908,6 @@ static int __init target_core_init_configfs(void)
 	if (ret < 0)
 		goto out;
 
-	scoped_with_kernel_creds()
-		target_init_dbroot();
-
 	return 0;
 
 out:
@@ -3832,6 +3916,7 @@ static int __init target_core_init_configfs(void)
 	core_dev_release_virtual_lun0();
 	rd_module_exit();
 out_global:
+	target_release_db_root();
 	if (default_lu_gp) {
 		core_alua_free_lu_gp(default_lu_gp);
 		default_lu_gp = NULL;
@@ -3861,6 +3946,7 @@ static void __exit target_core_exit_configfs(void)
 	core_dev_release_virtual_lun0();
 	rd_module_exit();
 	target_xcopy_release_pt();
+	target_release_db_root();
 	release_se_kmem_caches();
 }
 
diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h
index f0886ea290345..502788ba732dd 100644
--- a/drivers/target/target_core_internal.h
+++ b/drivers/target/target_core_internal.h
@@ -4,6 +4,7 @@
 
 #include <linux/configfs.h>
 #include <linux/list.h>
+#include <linux/mutex.h>
 #include <linux/types.h>
 #include <target/target_core_base.h>
 
@@ -171,6 +172,10 @@ extern struct se_portal_group xcopy_pt_tpg;
 #define	DB_ROOT_DEFAULT		"/var/target"
 #define	DB_ROOT_PREFERRED	"/etc/target"
 
+struct path;
+
 extern char db_root[];
+extern struct path db_root_path;
+extern struct mutex db_root_lock;
 
 #endif /* TARGET_CORE_INTERNAL_H */
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index 25b1bcacc0c8f..36d20732382f1 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -18,7 +18,6 @@
 #include <linux/file.h>
 #include <linux/fcntl.h>
 #include <linux/fs.h>
-#include <linux/fs_struct.h>
 #include <scsi/scsi_proto.h>
 #include <linux/unaligned.h>
 
@@ -1965,16 +1964,22 @@ static int __core_scsi3_write_aptpl_to_file(
 	int ret;
 	loff_t pos = 0;
 
-	path = kasprintf(GFP_KERNEL, "%s/pr/aptpl_%s", db_root,
-			&wwn->unit_serial[0]);
+	path = kasprintf(GFP_KERNEL, "pr/aptpl_%s", &wwn->unit_serial[0]);
 	if (!path)
 		return -ENOMEM;
 
-	scoped_with_init_fs()
-		file = filp_open(path, flags, 0600);
+	mutex_lock(&db_root_lock);
+	if (!db_root_path.dentry) {
+		mutex_unlock(&db_root_lock);
+		pr_err("db_root is not initialized for APTPL metadata path: %s\n",
+		       path);
+		kfree(path);
+		return -ENODEV;
+	}
+	file = configfs_open_root(&db_root_path, path, flags, 0600);
+	mutex_unlock(&db_root_lock);
 	if (IS_ERR(file)) {
-		pr_err("filp_open(%s) for APTPL metadata"
-			" failed\n", path);
+		pr_err("configfs_open_root(%s) for APTPL metadata failed\n", path);
 		kfree(path);
 		return PTR_ERR(file);
 	}
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v7 1/4] fs: configfs: add helpers for opening non-configfs paths
  2026-09-27  8:20 ` [PATCH v7 1/4] fs: configfs: add helpers for opening non-configfs paths Runyu Xiao
@ 2026-09-27  8:39   ` sashiko-bot
  0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-09-27  8:39 UTC (permalink / raw)
  To: Runyu Xiao; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] fs: deadlock bypass in configfs_open_root() via mountpoint traversal
--

commit 493a740e66f883a25d1e14e7e3f3596fe9f2721a
Author: Runyu Xiao <runyu.xiao@seu.edu.cn>

fs: configfs: add helpers for opening non-configfs paths

This commit adds configfs_file_open() and configfs_open_root() to allow
configfs store callbacks to open non-configfs paths without re-entering
configfs. It checks if the provided root is on configfs to prevent
acquiring the non-recursive fragment semaphore again.

Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>

> diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
> --- a/fs/configfs/mount.c
> +++ b/fs/configfs/mount.c

[ ... ]

> +struct file *configfs_open_root(const struct path *root, const char *name,
> +				int flags, umode_t mode)
> +{
> +	if (root->dentry->d_sb->s_type == &configfs_fs_type)
> +		return ERR_PTR(-EINVAL);
> +
> +	return file_open_root(root, name, flags, mode);

[Severity: Medium]
Could this bypass the deadlock prevention if a configfs mount exists inside the
starting directory?

The check against configfs_fs_type only validates the starting root directory's
superblock. However, file_open_root() can traverse mountpoints.

If a user mounts configfs inside the non-configfs root directory, the final
resolved file could still be on configfs.

If this happens during a store callback that already holds frag_sem,
file_open_root() opening the file will trigger configfs_open_file(). This
would try to acquire down_read(&frag->frag_sem) again, which could result
in a deadlock.

> +}
> +EXPORT_SYMBOL_GPL(configfs_open_root);

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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v7 0/4] nvmet: avoid recursive configfs open
  2026-09-27  8:20 [PATCH v7 0/4] nvmet: avoid recursive configfs open Runyu Xiao
                   ` (3 preceding siblings ...)
  2026-09-27  8:20 ` [PATCH v7 4/4] scsi: target: pin db_root for metadata writes Runyu Xiao
@ 2026-09-28  5:12 ` Christoph Hellwig
  4 siblings, 0 replies; 7+ messages in thread
From: Christoph Hellwig @ 2026-09-28  5:12 UTC (permalink / raw)
  To: Runyu Xiao
  Cc: Christoph Hellwig, Breno Leitao, Andreas Hindborg, Sagi Grimberg,
	Chaitanya Kulkarni, Keith Busch, Logan Gunthorpe,
	Martin K . Petersen, Lee Duncan, Hannes Reinecke,
	Nicholas Bellinger, linux-nvme, linux-scsi, target-devel,
	linux-kernel, stable, Jianhao Xu

Looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>



^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-09-28  5:12 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-27  8:20 [PATCH v7 0/4] nvmet: avoid recursive configfs open Runyu Xiao
2026-09-27  8:20 ` [PATCH v7 1/4] fs: configfs: add helpers for opening non-configfs paths Runyu Xiao
2026-09-27  8:39   ` sashiko-bot
2026-09-27  8:20 ` [PATCH v7 2/4] nvmet: avoid recursive configfs open for file-backed namespaces Runyu Xiao
2026-09-27  8:20 ` [PATCH v7 3/4] nvmet: avoid recursive configfs open for passthru Runyu Xiao
2026-09-27  8:20 ` [PATCH v7 4/4] scsi: target: pin db_root for metadata writes Runyu Xiao
2026-09-28  5:12 ` [PATCH v7 0/4] nvmet: avoid recursive configfs open Christoph Hellwig

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.