drivers/nvme/target/io-cmd-file.c | 19 ++++++++++++++++++- drivers/nvme/target/passthru.c | 17 ++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-)
The nvmet configfs store callbacks run while configfs holds the item's frag_sem. Two enable paths then call filp_open() on user-controlled paths: - file-backed namespace enable via device_path - passthru controller enable via passthru_ctrl_path If either path points back into configfs, the open path re-enters __configfs_open_file() and tries to take the same frag_sem again. Resolve the configured path before opening it, reject configfs-backed paths, and use dentry_open() on the resolved path. This prevents the configfs recursion without changing valid backend users. Runyu Xiao (2): nvmet: avoid recursive configfs open for file-backed namespaces nvmet: avoid recursive configfs open for passthru drivers/nvme/target/io-cmd-file.c | 19 ++++++++++++++++++- drivers/nvme/target/passthru.c | 17 ++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) -- 2.34.1
This v2 moves the configfs path check into a shared helper so nvmet can reject configfs-backed paths without string comparisons, then reuses it from both nvmet open paths. Runyu Xiao (3): fs: configfs: add helper to identify configfs paths nvmet: avoid recursive configfs open for file-backed namespaces nvmet: avoid recursive configfs open for passthru drivers/nvme/target/io-cmd-file.c | 20 +++++++++++++++++++- drivers/nvme/target/passthru.c | 17 ++++++++++++++++- fs/configfs/mount.c | 6 ++++++ include/linux/configfs.h | 2 ++ 4 files changed, 43 insertions(+), 2 deletions(-) -- 2.34.1
Several nvmet paths need to identify configfs-backed resolved paths while
already running under configfs callbacks. Keep the classification in
configfs itself so callers can reuse the filesystem-type test instead of
open-coding string comparisons.
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
fs/configfs/mount.c | 6 ++++++
include/linux/configfs.h | 2 ++
2 files changed, 8 insertions(+)
diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index 456c4a2efb53..1204421b61df 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -120,6 +120,12 @@ static struct file_system_type configfs_fs_type = {
};
MODULE_ALIAS_FS("configfs");
+bool configfs_path_is_configfs(const struct path *path)
+{
+ return path->dentry->d_sb->s_type == &configfs_fs_type;
+}
+EXPORT_SYMBOL_GPL(configfs_path_is_configfs);
+
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 698520b1bfdb..b3426c952835 100644
--- a/include/linux/configfs.h
+++ b/include/linux/configfs.h
@@ -34,6 +34,7 @@ struct configfs_group_operations;
struct configfs_attribute;
struct configfs_bin_attribute;
struct configfs_subsystem;
+struct path;
struct config_item {
char *ci_name;
@@ -243,6 +244,7 @@ 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);
+bool configfs_path_is_configfs(const struct path *path);
void configfs_remove_default_groups(struct config_group *group);
--
2.34.1
On Wed, Aug 19, 2026 at 11:52:26PM +0800, Runyu Xiao wrote: > Several nvmet paths need to identify configfs-backed resolved paths while > already running under configfs callbacks. Keep the classification in > configfs itself so callers can reuse the filesystem-type test instead of > open-coding string comparisons. Looks good: Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
nvmet_ns_enable_store() runs under configfs frag_sem. If a file-backed
namespace path resolves into configfs, filp_open() can re-enter configfs
and recurse on the same semaphore.
Reject configfs-backed paths after kern_path() and open the resolved path
with dentry_open() instead.
Fixes: d5eff33ee6f8 ("nvmet: add simple file backed ns support")
Cc: stable@vger.kernel.org
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
drivers/nvme/target/io-cmd-file.c | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/drivers/nvme/target/io-cmd-file.c b/drivers/nvme/target/io-cmd-file.c
index 2d068439b129..4ba3ebb82e6a 100644
--- a/drivers/nvme/target/io-cmd-file.c
+++ b/drivers/nvme/target/io-cmd-file.c
@@ -8,7 +8,9 @@
#include <linux/uio.h>
#include <linux/falloc.h>
#include <linux/file.h>
+#include <linux/configfs.h>
#include <linux/fs.h>
+#include <linux/namei.h>
#include "nvmet.h"
#define NVMET_MIN_MPOOL_OBJ 16
@@ -33,12 +35,28 @@ void nvmet_file_ns_disable(struct nvmet_ns *ns)
int nvmet_file_ns_enable(struct nvmet_ns *ns)
{
int flags = O_RDWR | O_LARGEFILE;
+ struct path path;
int ret = 0;
if (!ns->buffered_io)
flags |= O_DIRECT;
- ns->file = filp_open(ns->device_path, flags, 0);
+ ret = kern_path(ns->device_path, LOOKUP_FOLLOW, &path);
+ if (ret) {
+ pr_err("failed to open file %s: (%d)\n",
+ ns->device_path, ret);
+ return ret;
+ }
+
+ if (configfs_path_is_configfs(&path)) {
+ pr_err("configfs paths cannot back namespace %s\n",
+ ns->device_path);
+ path_put(&path);
+ return -EINVAL;
+ }
+
+ ns->file = dentry_open(&path, flags, current_cred());
+ path_put(&path);
if (IS_ERR(ns->file)) {
ret = PTR_ERR(ns->file);
pr_err("failed to open file %s: (%d)\n",
--
2.34.1
> + ret = kern_path(ns->device_path, LOOKUP_FOLLOW, &path);
> + if (ret) {
> + pr_err("failed to open file %s: (%d)\n",
> + ns->device_path, ret);
> + return ret;
> + }
> +
> + if (configfs_path_is_configfs(&path)) {
> + pr_err("configfs paths cannot back namespace %s\n",
> + ns->device_path);
> + path_put(&path);
> + return -EINVAL;
> + }
> +
> + ns->file = dentry_open(&path, flags, current_cred());
This section of code really should have a helper in configfs. And should
also be used in other callers like the SCSI target patches you also sent
out.
The nvmet configfs store callbacks hold the item's frag_sem while they enable file-backed namespaces or passthru controllers. Both paths open a user-configured pathname. If it resolves into configfs, the open path can re-enter __configfs_open_file() and try to acquire the same frag_sem again. Add a configfs helper for identifying resolved configfs paths, then use it from both nvmet backend open paths. The resolved path is opened directly after the check so the backend does not perform a second pathname walk. Changes since v2: - Keep the filesystem type comparison in fs/configfs and export one helper. - Use the helper from both nvmet callers. - Use file_open_root() for already-resolved paths so standard open-time permission checks are preserved. - Rebase the series on Linux 7.3-rc2. Runyu Xiao (3): fs: configfs: add helper to identify configfs paths nvmet: avoid recursive configfs open for file-backed namespaces nvmet: avoid recursive configfs open for passthru drivers/nvme/target/io-cmd-file.c | 20 +++++++++++++++++++- drivers/nvme/target/passthru.c | 17 ++++++++++++++++- fs/configfs/mount.c | 6 ++++++ include/linux/configfs.h | 2 ++ 4 files changed, 43 insertions(+), 2 deletions(-) -- 2.34.1
Keep the configfs filesystem type check in configfs so callers can
classify resolved paths without comparing filesystem name strings or
using filesystem-specific magic numbers.
Export the helper for configfs users that need to reject paths into the
configuration filesystem while running from a configfs callback.
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
Assisted-by: LLM Codex
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
fs/configfs/mount.c | 6 ++++++
include/linux/configfs.h | 2 ++
2 files changed, 8 insertions(+)
diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index d8cac1cbf..8abbe3afa 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -118,6 +118,12 @@ static struct file_system_type configfs_fs_type = {
};
MODULE_ALIAS_FS("configfs");
+bool configfs_path_is_configfs(const struct path *path)
+{
+ return path->dentry->d_sb->s_type == &configfs_fs_type;
+}
+EXPORT_SYMBOL_GPL(configfs_path_is_configfs);
+
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 ef65c75be..55df734da 100644
--- a/include/linux/configfs.h
+++ b/include/linux/configfs.h
@@ -34,6 +34,7 @@ struct configfs_group_operations;
struct configfs_attribute;
struct configfs_bin_attribute;
struct configfs_subsystem;
+struct path;
struct config_item {
char *ci_name;
@@ -243,6 +244,7 @@ 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);
+bool configfs_path_is_configfs(const struct path *path);
void configfs_remove_default_groups(struct config_group *group);
--
2.34.1
nvmet_ns_enable_store() runs as a configfs store callback while configfs
holds the item's frag_sem. A file-backed namespace then opens the
configured device_path with filp_open(). If the path resolves into
configfs, the open path re-enters __configfs_open_file() and attempts to
acquire the same frag_sem again.
Resolve the configured path first, reject paths resolved on configfs, and
open the resolved path with file_open_root(). This preserves the standard
open-time permission checks without performing a second pathname walk. Use
the configfs helper for the filesystem type check so this caller shares the
classification with other configfs users.
Fixes: d5eff33ee6f8 ("nvmet: add simple file backed ns support")
Cc: stable@vger.kernel.org
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
Assisted-by: LLM Codex
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
drivers/nvme/target/io-cmd-file.c | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/drivers/nvme/target/io-cmd-file.c b/drivers/nvme/target/io-cmd-file.c
index 0b22d183f..fbe58aa4a 100644
--- a/drivers/nvme/target/io-cmd-file.c
+++ b/drivers/nvme/target/io-cmd-file.c
@@ -8,7 +8,9 @@
#include <linux/uio.h>
#include <linux/falloc.h>
#include <linux/file.h>
+#include <linux/configfs.h>
#include <linux/fs.h>
+#include <linux/namei.h>
#include "nvmet.h"
#define NVMET_MIN_MPOOL_OBJ 16
@@ -33,12 +35,28 @@ void nvmet_file_ns_disable(struct nvmet_ns *ns)
int nvmet_file_ns_enable(struct nvmet_ns *ns)
{
int flags = O_RDWR | O_LARGEFILE;
+ struct path path;
int ret = 0;
if (!ns->buffered_io)
flags |= O_DIRECT;
- ns->file = filp_open(ns->device_path, flags, 0);
+ ret = kern_path(ns->device_path, LOOKUP_FOLLOW, &path);
+ if (ret) {
+ pr_err("failed to open file %s: (%d)\n",
+ ns->device_path, ret);
+ return ret;
+ }
+
+ if (configfs_path_is_configfs(&path)) {
+ pr_err("configfs paths cannot back namespace %s\n",
+ ns->device_path);
+ path_put(&path);
+ return -EINVAL;
+ }
+
+ ns->file = file_open_root(&path, "", flags, 0);
+ path_put(&path);
if (IS_ERR(ns->file)) {
ret = PTR_ERR(ns->file);
pr_err("failed to open file %s: (%d)\n",
--
2.34.1
On Thu, Sep 10, 2026 at 12:53:59PM +0800, Runyu Xiao wrote:
> + ret = kern_path(ns->device_path, LOOKUP_FOLLOW, &path);
> + if (ret) {
> + pr_err("failed to open file %s: (%d)\n",
> + ns->device_path, ret);
> + return ret;
> + }
> +
> + if (configfs_path_is_configfs(&path)) {
> + pr_err("configfs paths cannot back namespace %s\n",
> + ns->device_path);
> + path_put(&path);
> + return -EINVAL;
> + }
> +
> + ns->file = file_open_root(&path, "", flags, 0);
Can we please still have this in the helper?
On Wed, Sep 2, 2026 at 12:32:02PM +0200, Christoph Hellwig wrote: > This section of code really should have a helper in configfs. And should > also be used in other callers like the SCSI target patches you also sent > out. On Thu, Sep 10, 2026 at 07:26:04AM +0200, Christoph Hellwig wrote: > + ns->file = file_open_root(&path, "", flags, 0); > > Can we please still have this in the helper? Thanks, understood. I will move the path lookup, configfs filesystem check, and file_open_root() call into the common configfs helper, and update both nvmet callers to use it. I will also keep the target-core caller on the resolved-root path so the db_root TOCTOU fix is preserved, rather than reopening db_root from its mutable pathname. I will send v4 in this thread. Thanks, Runyu
On Mon, Sep 21, 2026 at 04:17:45PM +0800, Runyu Xiao wrote: > I will send v4 in this thread. Please always send new version in separate threads.
nvmet_passthru_enable_store() runs as a configfs store callback while
configfs holds the item's frag_sem. Enabling passthru then opens the
configured passthru_ctrl_path with filp_open(). If that path resolves
into configfs, the open path re-enters __configfs_open_file() and attempts
to acquire the same frag_sem again.
Resolve the configured path first, reject paths resolved on configfs, and
open the resolved path with file_open_root(). This preserves the standard
open-time permission checks without performing a second pathname walk. Use
the configfs helper for the filesystem type check so this caller shares the
classification with other configfs users.
Fixes: cae5b01a2afc ("nvmet: introduce the passthru configfs interface")
Cc: stable@vger.kernel.org
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
Assisted-by: LLM Codex
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
drivers/nvme/target/passthru.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/drivers/nvme/target/passthru.c b/drivers/nvme/target/passthru.c
index fa6527c53..aa1778c9e 100644
--- a/drivers/nvme/target/passthru.c
+++ b/drivers/nvme/target/passthru.c
@@ -9,6 +9,8 @@
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
+#include <linux/configfs.h>
+#include <linux/namei.h>
#include "../host/nvme.h"
#include "nvmet.h"
@@ -588,6 +590,7 @@ int nvmet_passthru_ctrl_enable(struct nvmet_subsys *subsys)
{
struct nvme_ctrl *ctrl;
struct file *file;
+ struct path path;
int ret = -EINVAL;
void *old;
@@ -602,7 +605,19 @@ int nvmet_passthru_ctrl_enable(struct nvmet_subsys *subsys)
goto out_unlock;
}
- file = filp_open(subsys->passthru_ctrl_path, O_RDWR, 0);
+ ret = kern_path(subsys->passthru_ctrl_path, LOOKUP_FOLLOW, &path);
+ if (ret)
+ goto out_unlock;
+
+ if (configfs_path_is_configfs(&path)) {
+ pr_err("configfs paths cannot back passthru controller %s\n",
+ subsys->passthru_ctrl_path);
+ path_put(&path);
+ goto out_unlock;
+ }
+
+ file = file_open_root(&path, "", O_RDWR, 0);
+ path_put(&path);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto out_unlock;
--
2.34.1
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
nvmet_passthru_ctrl_enable() runs under configfs frag_sem. If the
configured passthru controller path resolves into configfs, filp_open()
can re-enter configfs and recurse on the same semaphore.
Reject configfs-backed paths after kern_path() and open the resolved path
with dentry_open() instead.
Fixes: cae5b01a2afc ("nvmet: introduce the passthru configfs interface")
Cc: stable@vger.kernel.org
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
drivers/nvme/target/passthru.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/drivers/nvme/target/passthru.c b/drivers/nvme/target/passthru.c
index 0c361b1e3566..b3c57bf033e1 100644
--- a/drivers/nvme/target/passthru.c
+++ b/drivers/nvme/target/passthru.c
@@ -9,6 +9,8 @@
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
+#include <linux/configfs.h>
+#include <linux/namei.h>
#include "../host/nvme.h"
#include "nvmet.h"
@@ -578,6 +580,7 @@ int nvmet_passthru_ctrl_enable(struct nvmet_subsys *subsys)
{
struct nvme_ctrl *ctrl;
struct file *file;
+ struct path path;
int ret = -EINVAL;
void *old;
@@ -592,7 +595,19 @@ int nvmet_passthru_ctrl_enable(struct nvmet_subsys *subsys)
goto out_unlock;
}
- file = filp_open(subsys->passthru_ctrl_path, O_RDWR, 0);
+ ret = kern_path(subsys->passthru_ctrl_path, LOOKUP_FOLLOW, &path);
+ if (ret)
+ goto out_unlock;
+
+ if (configfs_path_is_configfs(&path)) {
+ pr_err("configfs paths cannot back passthru controller %s\n",
+ subsys->passthru_ctrl_path);
+ path_put(&path);
+ goto out_unlock;
+ }
+
+ file = dentry_open(&path, O_RDWR, current_cred());
+ path_put(&path);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto out_unlock;
--
2.34.1
© 2016 - 2026 Red Hat, Inc.