* FAILED: patch "[PATCH] ksmbd: fix path resolution in ksmbd_vfs_kern_path_create" failed to apply to 6.12-stable tree
@ 2026-07-14 14:38 gregkh
2026-07-16 11:20 ` [PATCH 6.12.y 1/3] ksmbd_vfs_rename(): vfs_path_parent_lookup() accepts ERR_PTR() as name Sasha Levin
0 siblings, 1 reply; 4+ messages in thread
From: gregkh @ 2026-07-14 14:38 UTC (permalink / raw)
To: d.ornaghi97, linkinjeon, stfrench; +Cc: stable
The patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x 1c8951963d8ed357f70f59e0ad4ddce2199d2016
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026071450-trespass-dispense-fb1b@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 1c8951963d8ed357f70f59e0ad4ddce2199d2016 Mon Sep 17 00:00:00 2001
From: Davide Ornaghi <d.ornaghi97@gmail.com>
Date: Mon, 15 Jun 2026 20:35:01 +0900
Subject: [PATCH] ksmbd: fix path resolution in ksmbd_vfs_kern_path_create
The SMB2 open lookup is rooted at the share with LOOKUP_BENEATH, but the
create/mkdir/hardlink sink is not: ksmbd_vfs_kern_path_create() builds an
absolute path with convert_to_unix_name() and resolves it from AT_FDCWD
via start_creating_path(), so a ".." component is walked from the real
filesystem root and escapes the export.
An authenticated client races a missing path component so the rooted open
lookup returns -ENOENT (taking the create branch) while the same component
is present (a directory) when the create walk runs; the create then
resolves ".." out of the share.
Root the create walk at the share like the lookup and rename paths already
are: resolve the parent with vfs_path_parent_lookup(..., LOOKUP_BENEATH,
&share_conf->vfs_path) and create the final component with
start_creating_noperm(). convert_to_unix_name() then has no callers and is
removed.
Fixes: 265fd1991c1d ("ksmbd: use LOOKUP_BENEATH to prevent the out of share access")
Cc: stable@vger.kernel.org
Signed-off-by: Davide Ornaghi <d.ornaghi97@gmail.com>
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
diff --git a/fs/smb/server/misc.c b/fs/smb/server/misc.c
index a543ec9d3581..966004c414a8 100644
--- a/fs/smb/server/misc.c
+++ b/fs/smb/server/misc.c
@@ -283,39 +283,6 @@ char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename)
return ksmbd_casefold_sharename(um, name);
}
-/**
- * convert_to_unix_name() - convert windows name to unix format
- * @share: ksmbd_share_config pointer
- * @name: file name that is relative to share
- *
- * Return: converted name on success, otherwise NULL
- */
-char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name)
-{
- int no_slash = 0, name_len, path_len;
- char *new_name;
-
- if (name[0] == '/')
- name++;
-
- path_len = share->path_sz;
- name_len = strlen(name);
- new_name = kmalloc(path_len + name_len + 2, KSMBD_DEFAULT_GFP);
- if (!new_name)
- return new_name;
-
- memcpy(new_name, share->path, path_len);
- if (new_name[path_len - 1] != '/') {
- new_name[path_len] = '/';
- no_slash = 1;
- }
-
- memcpy(new_name + path_len + no_slash, name, name_len);
- path_len += name_len + no_slash;
- new_name[path_len] = 0x00;
- return new_name;
-}
-
char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
const struct nls_table *local_nls,
int *conv_len)
diff --git a/fs/smb/server/misc.h b/fs/smb/server/misc.h
index 13423696ae8c..3909104e18ad 100644
--- a/fs/smb/server/misc.h
+++ b/fs/smb/server/misc.h
@@ -25,7 +25,6 @@ void ksmbd_strip_last_slash(char *path);
void ksmbd_conv_path_to_windows(char *path);
char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name);
char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename);
-char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name);
#define KSMBD_DIR_INFO_ALIGNMENT 8
struct ksmbd_dir_info;
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index fe376453a519..74b0307cb100 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -1259,15 +1259,30 @@ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
unsigned int flags,
struct path *path)
{
- char *abs_name;
+ struct ksmbd_share_config *share_conf = work->tcon->share_conf;
+ struct qstr last;
struct dentry *dent;
+ int err;
- abs_name = convert_to_unix_name(work->tcon->share_conf, name);
- if (!abs_name)
- return ERR_PTR(-ENOMEM);
+ /* resolve the name beneath the share root so ".." cannot escape */
+ CLASS(filename_kernel, filename)(name);
- dent = start_creating_path(AT_FDCWD, abs_name, path, flags);
- kfree(abs_name);
+ err = vfs_path_parent_lookup(filename, flags | LOOKUP_BENEATH,
+ path, &last, &share_conf->vfs_path);
+ if (err)
+ return ERR_PTR(err);
+
+ err = mnt_want_write(path->mnt);
+ if (err) {
+ path_put(path);
+ return ERR_PTR(err);
+ }
+
+ dent = start_creating_noperm(path->dentry, &last);
+ if (IS_ERR(dent)) {
+ mnt_drop_write(path->mnt);
+ path_put(path);
+ }
return dent;
}
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 6.12.y 1/3] ksmbd_vfs_rename(): vfs_path_parent_lookup() accepts ERR_PTR() as name
2026-07-14 14:38 FAILED: patch "[PATCH] ksmbd: fix path resolution in ksmbd_vfs_kern_path_create" failed to apply to 6.12-stable tree gregkh
@ 2026-07-16 11:20 ` Sasha Levin
2026-07-16 11:20 ` [PATCH 6.12.y 2/3] vfs: make LAST_XXX private to fs/namei.c Sasha Levin
2026-07-16 11:20 ` [PATCH 6.12.y 3/3] ksmbd: fix path resolution in ksmbd_vfs_kern_path_create Sasha Levin
0 siblings, 2 replies; 4+ messages in thread
From: Sasha Levin @ 2026-07-16 11:20 UTC (permalink / raw)
To: stable; +Cc: Al Viro, Sasha Levin
From: Al Viro <viro@zeniv.linux.org.uk>
[ Upstream commit ba33ac100d3feb1efb43b32e63cc0c6430936aa3 ]
no need to check in the caller
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Stable-dep-of: 1c8951963d8e ("ksmbd: fix path resolution in ksmbd_vfs_kern_path_create")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/smb/server/vfs.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index eec509d3a7fa01..a630994187a775 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -708,10 +708,6 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
return -ENOMEM;
to = getname_kernel(newname);
- if (IS_ERR(to)) {
- err = PTR_ERR(to);
- goto revert_fsids;
- }
retry:
err = vfs_path_parent_lookup(to, lookup_flags | LOOKUP_BENEATH,
@@ -812,7 +808,6 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
}
out1:
putname(to);
-revert_fsids:
ksmbd_revert_fsids(work);
return err;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 6.12.y 2/3] vfs: make LAST_XXX private to fs/namei.c
2026-07-16 11:20 ` [PATCH 6.12.y 1/3] ksmbd_vfs_rename(): vfs_path_parent_lookup() accepts ERR_PTR() as name Sasha Levin
@ 2026-07-16 11:20 ` Sasha Levin
2026-07-16 11:20 ` [PATCH 6.12.y 3/3] ksmbd: fix path resolution in ksmbd_vfs_kern_path_create Sasha Levin
1 sibling, 0 replies; 4+ messages in thread
From: Sasha Levin @ 2026-07-16 11:20 UTC (permalink / raw)
To: stable
Cc: Jori Koolstra, Amir Goldstein, NeilBrown, Namjae Jeon,
Christian Brauner (Amutable), Sasha Levin
From: Jori Koolstra <jkoolstra@xs4all.nl>
[ Upstream commit f2f1dddccae50f7a1d088285c53c376e26cedf67 ]
The only user of LAST_XXX outside of fs/namei.c is fs/smb/server/vfs.c;
ksmbd_vfs_path_lookup() calls vfs_path_parent_lookup() and expects a
LAST_NORM last type (or it will be ENOENT). ksmbd_vfs_rename() also calls
vfs_path_parent_lookup() but forgets the LAST_NORM check.
It does not really make sense to have vfs_path_parent_lookup() expose
the last_type because it is only needed to ensure it is LAST_NORM. So
let's do this check in vfs_path_parent_lookup() instead and keep the
LAST_XXX internal to fs/namei.c. This changes the ENOENT errno in
ksmbd_vfs_path_lookup() to EINVAL, which matches better with how this is
handled by callers of filename_parentat().
Signed-off-by: Jori Koolstra <jkoolstra@xs4all.nl>
Link: https://patch.msgid.link/20260528175854.57626-1-jkoolstra@xs4all.nl
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Stable-dep-of: 1c8951963d8e ("ksmbd: fix path resolution in ksmbd_vfs_kern_path_create")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/namei.c | 20 ++++++++++++++++----
fs/smb/server/vfs.c | 15 +++------------
include/linux/namei.h | 7 +------
3 files changed, 20 insertions(+), 22 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index 1eb20cb5e58f9e..3552e5ad02a620 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -125,6 +125,11 @@
#define EMBEDDED_NAME_MAX (PATH_MAX - offsetof(struct filename, iname))
+/*
+ * Type of the last component on LOOKUP_PARENT
+ */
+enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT};
+
struct filename *
getname_flags(const char __user *filename, int flags)
{
@@ -2736,15 +2741,22 @@ EXPORT_SYMBOL(kern_path);
* @flags: lookup flags
* @parent: pointer to struct path to fill
* @last: last component
- * @type: type of the last component
* @root: pointer to struct path of the base directory
*/
int vfs_path_parent_lookup(struct filename *filename, unsigned int flags,
- struct path *parent, struct qstr *last, int *type,
+ struct path *parent, struct qstr *last,
const struct path *root)
{
- return __filename_parentat(AT_FDCWD, filename, flags, parent, last,
- type, root);
+ int type;
+ int err = __filename_parentat(AT_FDCWD, filename, flags, parent, last,
+ &type, root);
+ if (err)
+ return err;
+ if (unlikely(type != LAST_NORM)) {
+ path_put(parent);
+ return -EINVAL;
+ }
+ return 0;
}
EXPORT_SYMBOL(vfs_path_parent_lookup);
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index a630994187a775..8c79ad55fe44d7 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -18,7 +18,6 @@
#include <linux/vmalloc.h>
#include <linux/sched/xacct.h>
#include <linux/crc32c.h>
-#include <linux/namei.h>
#include "glob.h"
#include "oplock.h"
@@ -73,7 +72,7 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
struct qstr last;
struct filename *filename;
struct path *root_share_path = &share_conf->vfs_path;
- int err, type;
+ int err;
struct dentry *d;
if (pathname[0] == '\0') {
@@ -88,19 +87,13 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
return PTR_ERR(filename);
err = vfs_path_parent_lookup(filename, flags,
- parent_path, &last, &type,
+ parent_path, &last,
root_share_path);
if (err) {
putname(filename);
return err;
}
- if (unlikely(type != LAST_NORM)) {
- path_put(parent_path);
- putname(filename);
- return -ENOENT;
- }
-
err = mnt_want_write(parent_path->mnt);
if (err) {
path_put(parent_path);
@@ -701,7 +694,6 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
struct filename *to;
struct ksmbd_share_config *share_conf = work->tcon->share_conf;
struct ksmbd_file *parent_fp;
- int new_type;
int err, lookup_flags = LOOKUP_NO_SYMLINKS;
if (ksmbd_override_fsids(work))
@@ -711,8 +703,7 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
retry:
err = vfs_path_parent_lookup(to, lookup_flags | LOOKUP_BENEATH,
- &new_path, &new_last, &new_type,
- &share_conf->vfs_path);
+ &new_path, &new_last, &share_conf->vfs_path);
if (err)
goto out1;
diff --git a/include/linux/namei.h b/include/linux/namei.h
index 8ec8fed3bce81d..99b158ca0ce2ec 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -12,11 +12,6 @@ enum { MAX_NESTED_LINKS = 8 };
#define MAXSYMLINKS 40
-/*
- * Type of the last component on LOOKUP_PARENT
- */
-enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT};
-
/* pathwalk mode */
#define LOOKUP_FOLLOW 0x0001 /* follow links at the end */
#define LOOKUP_DIRECTORY 0x0002 /* require a directory */
@@ -63,7 +58,7 @@ extern void done_path_create(struct path *, struct dentry *);
extern struct dentry *kern_path_locked(const char *, struct path *);
extern struct dentry *user_path_locked_at(int , const char __user *, struct path *);
int vfs_path_parent_lookup(struct filename *filename, unsigned int flags,
- struct path *parent, struct qstr *last, int *type,
+ struct path *parent, struct qstr *last,
const struct path *root);
int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *,
unsigned int, struct path *);
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 6.12.y 3/3] ksmbd: fix path resolution in ksmbd_vfs_kern_path_create
2026-07-16 11:20 ` [PATCH 6.12.y 1/3] ksmbd_vfs_rename(): vfs_path_parent_lookup() accepts ERR_PTR() as name Sasha Levin
2026-07-16 11:20 ` [PATCH 6.12.y 2/3] vfs: make LAST_XXX private to fs/namei.c Sasha Levin
@ 2026-07-16 11:20 ` Sasha Levin
1 sibling, 0 replies; 4+ messages in thread
From: Sasha Levin @ 2026-07-16 11:20 UTC (permalink / raw)
To: stable; +Cc: Davide Ornaghi, Namjae Jeon, Steve French, Sasha Levin
From: Davide Ornaghi <d.ornaghi97@gmail.com>
[ Upstream commit 1c8951963d8ed357f70f59e0ad4ddce2199d2016 ]
The SMB2 open lookup is rooted at the share with LOOKUP_BENEATH, but the
create/mkdir/hardlink sink is not: ksmbd_vfs_kern_path_create() builds an
absolute path with convert_to_unix_name() and resolves it from AT_FDCWD
via start_creating_path(), so a ".." component is walked from the real
filesystem root and escapes the export.
An authenticated client races a missing path component so the rooted open
lookup returns -ENOENT (taking the create branch) while the same component
is present (a directory) when the create walk runs; the create then
resolves ".." out of the share.
Root the create walk at the share like the lookup and rename paths already
are: resolve the parent with vfs_path_parent_lookup(..., LOOKUP_BENEATH,
&share_conf->vfs_path) and create the final component with
start_creating_noperm(). convert_to_unix_name() then has no callers and is
removed.
Fixes: 265fd1991c1d ("ksmbd: use LOOKUP_BENEATH to prevent the out of share access")
Cc: stable@vger.kernel.org
Signed-off-by: Davide Ornaghi <d.ornaghi97@gmail.com>
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/smb/server/misc.c | 33 ---------------------------------
fs/smb/server/misc.h | 1 -
fs/smb/server/vfs.c | 36 ++++++++++++++++++++++++++++++------
3 files changed, 30 insertions(+), 40 deletions(-)
diff --git a/fs/smb/server/misc.c b/fs/smb/server/misc.c
index cb2a11ffb23fe3..53ddcae31f7194 100644
--- a/fs/smb/server/misc.c
+++ b/fs/smb/server/misc.c
@@ -278,39 +278,6 @@ char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename)
return ksmbd_casefold_sharename(um, name);
}
-/**
- * convert_to_unix_name() - convert windows name to unix format
- * @share: ksmbd_share_config pointer
- * @name: file name that is relative to share
- *
- * Return: converted name on success, otherwise NULL
- */
-char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name)
-{
- int no_slash = 0, name_len, path_len;
- char *new_name;
-
- if (name[0] == '/')
- name++;
-
- path_len = share->path_sz;
- name_len = strlen(name);
- new_name = kmalloc(path_len + name_len + 2, KSMBD_DEFAULT_GFP);
- if (!new_name)
- return new_name;
-
- memcpy(new_name, share->path, path_len);
- if (new_name[path_len - 1] != '/') {
- new_name[path_len] = '/';
- no_slash = 1;
- }
-
- memcpy(new_name + path_len + no_slash, name, name_len);
- path_len += name_len + no_slash;
- new_name[path_len] = 0x00;
- return new_name;
-}
-
char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
const struct nls_table *local_nls,
int *conv_len)
diff --git a/fs/smb/server/misc.h b/fs/smb/server/misc.h
index 1facfcd21200fd..53282a6d2436ca 100644
--- a/fs/smb/server/misc.h
+++ b/fs/smb/server/misc.h
@@ -22,7 +22,6 @@ void ksmbd_strip_last_slash(char *path);
void ksmbd_conv_path_to_windows(char *path);
char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name);
char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename);
-char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name);
#define KSMBD_DIR_INFO_ALIGNMENT 8
struct ksmbd_dir_info;
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index 8c79ad55fe44d7..3346985c53b32a 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -1309,15 +1309,39 @@ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
unsigned int flags,
struct path *path)
{
- char *abs_name;
+ struct ksmbd_share_config *share_conf = work->tcon->share_conf;
+ struct qstr last;
+ struct filename *filename;
struct dentry *dent;
+ int err;
- abs_name = convert_to_unix_name(work->tcon->share_conf, name);
- if (!abs_name)
- return ERR_PTR(-ENOMEM);
+ /* resolve the name beneath the share root so ".." cannot escape */
+ filename = getname_kernel(name);
+ if (IS_ERR(filename))
+ return ERR_CAST(filename);
+
+ err = vfs_path_parent_lookup(filename, flags | LOOKUP_BENEATH,
+ path, &last, &share_conf->vfs_path);
+ if (err) {
+ putname(filename);
+ return ERR_PTR(err);
+ }
- dent = kern_path_create(AT_FDCWD, abs_name, path, flags);
- kfree(abs_name);
+ err = mnt_want_write(path->mnt);
+ if (err) {
+ path_put(path);
+ putname(filename);
+ return ERR_PTR(err);
+ }
+
+ inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
+ dent = lookup_one_qstr_excl(&last, path->dentry, LOOKUP_CREATE);
+ if (IS_ERR(dent)) {
+ inode_unlock(path->dentry->d_inode);
+ mnt_drop_write(path->mnt);
+ path_put(path);
+ }
+ putname(filename);
return dent;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-07-16 11:20 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-14 14:38 FAILED: patch "[PATCH] ksmbd: fix path resolution in ksmbd_vfs_kern_path_create" failed to apply to 6.12-stable tree gregkh
2026-07-16 11:20 ` [PATCH 6.12.y 1/3] ksmbd_vfs_rename(): vfs_path_parent_lookup() accepts ERR_PTR() as name Sasha Levin
2026-07-16 11:20 ` [PATCH 6.12.y 2/3] vfs: make LAST_XXX private to fs/namei.c Sasha Levin
2026-07-16 11:20 ` [PATCH 6.12.y 3/3] ksmbd: fix path resolution in ksmbd_vfs_kern_path_create Sasha Levin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).