* [PATCH v3 1/4] namei: Fix use after free in kern_path_locked
2021-09-08 0:04 [PATCH v3 0/4] namei: fix use-after-free and adjust calling conventions Stephen Brennan
@ 2021-09-08 0:04 ` Stephen Brennan
2021-09-08 0:04 ` [PATCH v3 2/4] namei: Rename __filename_parentat() Stephen Brennan
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Stephen Brennan @ 2021-09-08 0:04 UTC (permalink / raw)
To: Alexander Viro, Jens Axboe, Dmitry Kadashev
Cc: Stephen Brennan, linux-fsdevel, linux-kernel
In 0ee50b47532a ("namei: change filename_parentat() calling
conventions"), filename_parentat() was made to always call putname() on
the filename before returning, and kern_path_locked() was migrated to
this calling convention. However, kern_path_locked() uses the "last"
parameter to lookup and potentially create a new dentry. The last
parameter contains the last component of the path and points within the
filename, which was recently freed at the end of filename_parentat().
Thus, when kern_path_locked() calls __lookup_hash(), it is using the
filename after it has already been freed.
Switch to using __filename_parentat() to avoid freeing the filename, and
wrap the function with a getname and putname instead. Remove
filename_parentat() as it is now unused, and it is inherently broken.
Fixes: 0ee50b47532a ("namei: change filename_parentat() calling conventions")
Link: https://lore.kernel.org/linux-fsdevel/YTfVE7IbbTV71Own@zeniv-ca.linux.org.uk/
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Reported-by: syzbot+fb0d60a179096e8c2731@syzkaller.appspotmail.com
Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
---
fs/namei.c | 25 ++++++++++++-------------
1 file changed, 12 insertions(+), 13 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index d049d3972695..d6340fabaab4 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2538,25 +2538,15 @@ static int __filename_parentat(int dfd, struct filename *name,
return retval;
}
-static int filename_parentat(int dfd, struct filename *name,
- unsigned int flags, struct path *parent,
- struct qstr *last, int *type)
-{
- int retval = __filename_parentat(dfd, name, flags, parent, last, type);
-
- putname(name);
- return retval;
-}
-
/* does lookup, returns the object with parent locked */
-struct dentry *kern_path_locked(const char *name, struct path *path)
+static struct dentry *__kern_path_locked(struct filename *name,
+ struct path *path)
{
struct dentry *d;
struct qstr last;
int type, error;
- error = filename_parentat(AT_FDCWD, getname_kernel(name), 0, path,
- &last, &type);
+ error = __filename_parentat(AT_FDCWD, name, 0, path, &last, &type);
if (error)
return ERR_PTR(error);
if (unlikely(type != LAST_NORM)) {
@@ -2572,6 +2562,15 @@ struct dentry *kern_path_locked(const char *name, struct path *path)
return d;
}
+struct dentry *kern_path_locked(const char *name, struct path *path)
+{
+ struct filename *filename = getname_kernel(name);
+ struct dentry *res = __kern_path_locked(filename, path);
+
+ putname(filename);
+ return res;
+}
+
int kern_path(const char *name, unsigned int flags, struct path *path)
{
return filename_lookup(AT_FDCWD, getname_kernel(name),
--
2.30.2
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 2/4] namei: Rename __filename_parentat()
2021-09-08 0:04 [PATCH v3 0/4] namei: fix use-after-free and adjust calling conventions Stephen Brennan
2021-09-08 0:04 ` [PATCH v3 1/4] namei: Fix use after free in kern_path_locked Stephen Brennan
@ 2021-09-08 0:04 ` Stephen Brennan
2021-09-08 0:04 ` [PATCH v3 3/4] namei: Standardize callers of filename_lookup() Stephen Brennan
2021-09-08 0:04 ` [PATCH v3 4/4] namei: Standardize callers of filename_create() Stephen Brennan
3 siblings, 0 replies; 5+ messages in thread
From: Stephen Brennan @ 2021-09-08 0:04 UTC (permalink / raw)
To: Alexander Viro; +Cc: Stephen Brennan, linux-fsdevel, linux-kernel
Now that filename_parentat() is gone, rename __filename_parentat() to
the original name.
Link: https://lore.kernel.org/linux-fsdevel/YS9D4AlEsaCxLFV0@infradead.org/
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
Co-authored-by: Dmitry Kadashev <dkadashev@gmail.com>
---
fs/namei.c | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index d6340fabaab4..33f60d1b3a87 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2514,9 +2514,10 @@ static int path_parentat(struct nameidata *nd, unsigned flags,
return err;
}
-static int __filename_parentat(int dfd, struct filename *name,
- unsigned int flags, struct path *parent,
- struct qstr *last, int *type)
+/* Note: this does not consume "name" */
+static int filename_parentat(int dfd, struct filename *name,
+ unsigned int flags, struct path *parent,
+ struct qstr *last, int *type)
{
int retval;
struct nameidata nd;
@@ -2546,7 +2547,7 @@ static struct dentry *__kern_path_locked(struct filename *name,
struct qstr last;
int type, error;
- error = __filename_parentat(AT_FDCWD, name, 0, path, &last, &type);
+ error = filename_parentat(AT_FDCWD, name, 0, path, &last, &type);
if (error)
return ERR_PTR(error);
if (unlikely(type != LAST_NORM)) {
@@ -3633,7 +3634,7 @@ static struct dentry *__filename_create(int dfd, struct filename *name,
*/
lookup_flags &= LOOKUP_REVAL;
- error = __filename_parentat(dfd, name, lookup_flags, path, &last, &type);
+ error = filename_parentat(dfd, name, lookup_flags, path, &last, &type);
if (error)
return ERR_PTR(error);
@@ -3995,7 +3996,7 @@ int do_rmdir(int dfd, struct filename *name)
int type;
unsigned int lookup_flags = 0;
retry:
- error = __filename_parentat(dfd, name, lookup_flags, &path, &last, &type);
+ error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type);
if (error)
goto exit1;
@@ -4134,7 +4135,7 @@ int do_unlinkat(int dfd, struct filename *name)
struct inode *delegated_inode = NULL;
unsigned int lookup_flags = 0;
retry:
- error = __filename_parentat(dfd, name, lookup_flags, &path, &last, &type);
+ error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type);
if (error)
goto exit1;
@@ -4682,13 +4683,13 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
target_flags = 0;
retry:
- error = __filename_parentat(olddfd, from, lookup_flags, &old_path,
- &old_last, &old_type);
+ error = filename_parentat(olddfd, from, lookup_flags, &old_path,
+ &old_last, &old_type);
if (error)
goto put_names;
- error = __filename_parentat(newdfd, to, lookup_flags, &new_path, &new_last,
- &new_type);
+ error = filename_parentat(newdfd, to, lookup_flags, &new_path, &new_last,
+ &new_type);
if (error)
goto exit1;
--
2.30.2
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 3/4] namei: Standardize callers of filename_lookup()
2021-09-08 0:04 [PATCH v3 0/4] namei: fix use-after-free and adjust calling conventions Stephen Brennan
2021-09-08 0:04 ` [PATCH v3 1/4] namei: Fix use after free in kern_path_locked Stephen Brennan
2021-09-08 0:04 ` [PATCH v3 2/4] namei: Rename __filename_parentat() Stephen Brennan
@ 2021-09-08 0:04 ` Stephen Brennan
2021-09-08 0:04 ` [PATCH v3 4/4] namei: Standardize callers of filename_create() Stephen Brennan
3 siblings, 0 replies; 5+ messages in thread
From: Stephen Brennan @ 2021-09-08 0:04 UTC (permalink / raw)
To: Alexander Viro; +Cc: Stephen Brennan, linux-fsdevel, linux-kernel
filename_lookup() has two variants, one which drops the caller's
reference to filename (filename_lookup), and one which does
not (__filename_lookup). This can be confusing as it's unusual to drop a
caller's reference. Remove filename_lookup, rename __filename_lookup
to filename_lookup, and convert all callers. The cost is a few slightly
longer functions, but the clarity is greater.
Link: https://lore.kernel.org/linux-fsdevel/YS+dstZ3xfcLxhoB@zeniv-ca.linux.org.uk/
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
---
fs/fs_parser.c | 1 -
fs/namei.c | 41 ++++++++++++++++++++++++-----------------
2 files changed, 24 insertions(+), 18 deletions(-)
diff --git a/fs/fs_parser.c b/fs/fs_parser.c
index 980d44fd3a36..3df07c0e32b3 100644
--- a/fs/fs_parser.c
+++ b/fs/fs_parser.c
@@ -165,7 +165,6 @@ int fs_lookup_param(struct fs_context *fc,
return invalf(fc, "%s: not usable as path", param->key);
}
- f->refcnt++; /* filename_lookup() drops our ref. */
ret = filename_lookup(param->dirfd, f, flags, _path, NULL);
if (ret < 0) {
errorf(fc, "%s: Lookup failure for '%s'", param->key, f->name);
diff --git a/fs/namei.c b/fs/namei.c
index 33f60d1b3a87..7bd3d1c90e52 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2467,7 +2467,7 @@ static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path
return err;
}
-static int __filename_lookup(int dfd, struct filename *name, unsigned flags,
+int filename_lookup(int dfd, struct filename *name, unsigned flags,
struct path *path, struct path *root)
{
int retval;
@@ -2488,15 +2488,6 @@ static int __filename_lookup(int dfd, struct filename *name, unsigned flags,
return retval;
}
-int filename_lookup(int dfd, struct filename *name, unsigned flags,
- struct path *path, struct path *root)
-{
- int retval = __filename_lookup(dfd, name, flags, path, root);
-
- putname(name);
- return retval;
-}
-
/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
static int path_parentat(struct nameidata *nd, unsigned flags,
struct path *parent)
@@ -2574,8 +2565,14 @@ struct dentry *kern_path_locked(const char *name, struct path *path)
int kern_path(const char *name, unsigned int flags, struct path *path)
{
- return filename_lookup(AT_FDCWD, getname_kernel(name),
- flags, path, NULL);
+ struct filename *filename;
+ int ret;
+
+ filename = getname_kernel(name);
+ ret = filename_lookup(AT_FDCWD, filename, flags, path, NULL);
+ putname(filename);
+ return ret;
+
}
EXPORT_SYMBOL(kern_path);
@@ -2591,10 +2588,15 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
const char *name, unsigned int flags,
struct path *path)
{
+ struct filename *filename;
struct path root = {.mnt = mnt, .dentry = dentry};
+ int ret;
+
+ filename = getname_kernel(name);
/* the first argument of filename_lookup() is ignored with root */
- return filename_lookup(AT_FDCWD, getname_kernel(name),
- flags , path, &root);
+ ret = filename_lookup(AT_FDCWD, filename, flags, path, &root);
+ putname(filename);
+ return ret;
}
EXPORT_SYMBOL(vfs_path_lookup);
@@ -2798,8 +2800,13 @@ int path_pts(struct path *path)
int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
struct path *path, int *empty)
{
- return filename_lookup(dfd, getname_flags(name, flags, empty),
- flags, path, NULL);
+ struct filename *filename;
+ int ret;
+
+ filename = getname_flags(name, flags, empty);
+ ret = filename_lookup(dfd, filename, flags, path, NULL);
+ putname(filename);
+ return ret;
}
EXPORT_SYMBOL(user_path_at_empty);
@@ -4424,7 +4431,7 @@ int do_linkat(int olddfd, struct filename *old, int newdfd,
if (flags & AT_SYMLINK_FOLLOW)
how |= LOOKUP_FOLLOW;
retry:
- error = __filename_lookup(olddfd, old, how, &old_path, NULL);
+ error = filename_lookup(olddfd, old, how, &old_path, NULL);
if (error)
goto out_putnames;
--
2.30.2
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 4/4] namei: Standardize callers of filename_create()
2021-09-08 0:04 [PATCH v3 0/4] namei: fix use-after-free and adjust calling conventions Stephen Brennan
` (2 preceding siblings ...)
2021-09-08 0:04 ` [PATCH v3 3/4] namei: Standardize callers of filename_lookup() Stephen Brennan
@ 2021-09-08 0:04 ` Stephen Brennan
3 siblings, 0 replies; 5+ messages in thread
From: Stephen Brennan @ 2021-09-08 0:04 UTC (permalink / raw)
To: Alexander Viro; +Cc: Stephen Brennan, linux-fsdevel, linux-kernel
filename_create() has two variants, one which drops the caller's
reference to filename (filename_create) and one which does
not (__filename_create). This can be confusing as it's unusual to drop a
caller's reference. Remove filename_create, rename __filename_create
to filename_create, and convert all callers.
Link: https://lore.kernel.org/linux-fsdevel/f6238254-35bd-7e97-5b27-21050c745874@oracle.com/
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
---
fs/namei.c | 38 ++++++++++++++++++++------------------
1 file changed, 20 insertions(+), 18 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index 7bd3d1c90e52..88cdc379255c 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3625,8 +3625,8 @@ struct file *do_file_open_root(const struct path *root,
return file;
}
-static struct dentry *__filename_create(int dfd, struct filename *name,
- struct path *path, unsigned int lookup_flags)
+static struct dentry *filename_create(int dfd, struct filename *name,
+ struct path *path, unsigned int lookup_flags)
{
struct dentry *dentry = ERR_PTR(-EEXIST);
struct qstr last;
@@ -3694,20 +3694,16 @@ static struct dentry *__filename_create(int dfd, struct filename *name,
return dentry;
}
-static inline struct dentry *filename_create(int dfd, struct filename *name,
- struct path *path, unsigned int lookup_flags)
-{
- struct dentry *res = __filename_create(dfd, name, path, lookup_flags);
-
- putname(name);
- return res;
-}
-
struct dentry *kern_path_create(int dfd, const char *pathname,
struct path *path, unsigned int lookup_flags)
{
- return filename_create(dfd, getname_kernel(pathname),
- path, lookup_flags);
+ struct filename *filename;
+ struct dentry *dentry;
+
+ filename = getname_kernel(pathname);
+ dentry = filename_create(dfd, filename, path, lookup_flags);
+ putname(filename);
+ return dentry;
}
EXPORT_SYMBOL(kern_path_create);
@@ -3723,7 +3719,13 @@ EXPORT_SYMBOL(done_path_create);
inline struct dentry *user_path_create(int dfd, const char __user *pathname,
struct path *path, unsigned int lookup_flags)
{
- return filename_create(dfd, getname(pathname), path, lookup_flags);
+ struct filename *filename;
+ struct dentry *dentry;
+
+ filename = getname(pathname);
+ dentry = filename_create(dfd, filename, path, lookup_flags);
+ putname(filename);
+ return dentry;
}
EXPORT_SYMBOL(user_path_create);
@@ -3804,7 +3806,7 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
if (error)
goto out1;
retry:
- dentry = __filename_create(dfd, name, &path, lookup_flags);
+ dentry = filename_create(dfd, name, &path, lookup_flags);
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto out1;
@@ -3904,7 +3906,7 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode)
unsigned int lookup_flags = LOOKUP_DIRECTORY;
retry:
- dentry = __filename_create(dfd, name, &path, lookup_flags);
+ dentry = filename_create(dfd, name, &path, lookup_flags);
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto out_putname;
@@ -4271,7 +4273,7 @@ int do_symlinkat(struct filename *from, int newdfd, struct filename *to)
goto out_putnames;
}
retry:
- dentry = __filename_create(newdfd, to, &path, lookup_flags);
+ dentry = filename_create(newdfd, to, &path, lookup_flags);
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto out_putnames;
@@ -4435,7 +4437,7 @@ int do_linkat(int olddfd, struct filename *old, int newdfd,
if (error)
goto out_putnames;
- new_dentry = __filename_create(newdfd, new, &new_path,
+ new_dentry = filename_create(newdfd, new, &new_path,
(how & LOOKUP_REVAL));
error = PTR_ERR(new_dentry);
if (IS_ERR(new_dentry))
--
2.30.2
^ permalink raw reply related [flat|nested] 5+ messages in thread