Skip to content

Commit 4c6f5cc

Browse files
author
Ming Lei
committed
fs: add kern_path_locked_negative()
JIRA: https://issues.redhat.com/browse/RHEL-83595 Conflicts: Pull the following line DEFINE_FREE(putname, struct filename *, if (!IS_ERR_OR_NULL(_T)) putname(_T)) from commit f9fde81 ("fs: support getname_maybe_null() in move_mount()") for avoiding build failure because this patch applies Automatic Cleanup. commit c86b300 Author: Christian Brauner <brauner@kernel.org> Date: Mon Apr 14 22:13:33 2025 +0200 fs: add kern_path_locked_negative() The audit code relies on the fact that kern_path_locked() returned a path even for a negative dentry. If it doesn't find a valid dentry it immediately calls: audit_find_parent(d_backing_inode(parent_path.dentry)); which assumes that parent_path.dentry is still valid. But it isn't since kern_path_locked() has been changed to path_put() also for a negative dentry. Fix this by adding a helper that implements the required audit semantics and allows us to fix the immediate bleeding. We can find a unified solution for this afterwards. Link: https://lore.kernel.org/20250414-rennt-wimmeln-f186c3a780f1@brauner Fixes: 1c3cb50 ("VFS: change kern_path_locked() and user_path_locked_at() to never return negative dentry") Reported-and-tested-by: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Christian Brauner <brauner@kernel.org> Signed-off-by: Ming Lei <ming.lei@redhat.com>
1 parent dbfe5f4 commit 4c6f5cc

File tree

4 files changed

+61
-22
lines changed

4 files changed

+61
-22
lines changed

fs/namei.c

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,27 +1622,20 @@ static struct dentry *lookup_dcache(const struct qstr *name,
16221622
return dentry;
16231623
}
16241624

1625-
/*
1626-
* Parent directory has inode locked exclusive. This is one
1627-
* and only case when ->lookup() gets called on non in-lookup
1628-
* dentries - as the matter of fact, this only gets called
1629-
* when directory is guaranteed to have no in-lookup children
1630-
* at all.
1631-
* Will return -ENOENT if name isn't found and LOOKUP_CREATE wasn't passed.
1632-
* Will return -EEXIST if name is found and LOOKUP_EXCL was passed.
1633-
*/
1634-
struct dentry *lookup_one_qstr_excl(const struct qstr *name,
1635-
struct dentry *base,
1636-
unsigned int flags)
1625+
static struct dentry *lookup_one_qstr_excl_raw(const struct qstr *name,
1626+
struct dentry *base,
1627+
unsigned int flags)
16371628
{
1638-
struct dentry *dentry = lookup_dcache(name, base, flags);
1629+
struct dentry *dentry;
16391630
struct dentry *old;
1640-
struct inode *dir = base->d_inode;
1631+
struct inode *dir;
16411632

1633+
dentry = lookup_dcache(name, base, flags);
16421634
if (dentry)
1643-
goto found;
1635+
return dentry;
16441636

16451637
/* Don't create child dentry for a dead directory. */
1638+
dir = base->d_inode;
16461639
if (unlikely(IS_DEADDIR(dir)))
16471640
return ERR_PTR(-ENOENT);
16481641

@@ -1655,7 +1648,24 @@ struct dentry *lookup_one_qstr_excl(const struct qstr *name,
16551648
dput(dentry);
16561649
dentry = old;
16571650
}
1658-
found:
1651+
return dentry;
1652+
}
1653+
1654+
/*
1655+
* Parent directory has inode locked exclusive. This is one
1656+
* and only case when ->lookup() gets called on non in-lookup
1657+
* dentries - as the matter of fact, this only gets called
1658+
* when directory is guaranteed to have no in-lookup children
1659+
* at all.
1660+
* Will return -ENOENT if name isn't found and LOOKUP_CREATE wasn't passed.
1661+
* Will return -EEXIST if name is found and LOOKUP_EXCL was passed.
1662+
*/
1663+
struct dentry *lookup_one_qstr_excl(const struct qstr *name,
1664+
struct dentry *base, unsigned int flags)
1665+
{
1666+
struct dentry *dentry;
1667+
1668+
dentry = lookup_one_qstr_excl_raw(name, base, flags);
16591669
if (IS_ERR(dentry))
16601670
return dentry;
16611671
if (d_is_negative(dentry) && !(flags & LOOKUP_CREATE)) {
@@ -2723,6 +2733,29 @@ static struct dentry *__kern_path_locked(int dfd, struct filename *name, struct
27232733
return d;
27242734
}
27252735

2736+
struct dentry *kern_path_locked_negative(const char *name, struct path *path)
2737+
{
2738+
struct filename *filename __free(putname) = getname_kernel(name);
2739+
struct dentry *d;
2740+
struct qstr last;
2741+
int type, error;
2742+
2743+
error = filename_parentat(AT_FDCWD, filename, 0, path, &last, &type);
2744+
if (error)
2745+
return ERR_PTR(error);
2746+
if (unlikely(type != LAST_NORM)) {
2747+
path_put(path);
2748+
return ERR_PTR(-EINVAL);
2749+
}
2750+
inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
2751+
d = lookup_one_qstr_excl_raw(&last, path->dentry, 0);
2752+
if (IS_ERR(d)) {
2753+
inode_unlock(path->dentry->d_inode);
2754+
path_put(path);
2755+
}
2756+
return d;
2757+
}
2758+
27262759
struct dentry *kern_path_locked(const char *name, struct path *path)
27272760
{
27282761
struct filename *filename = getname_kernel(name);

include/linux/fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2778,6 +2778,7 @@ static inline struct filename *getname_maybe_null(const char __user *name, int f
27782778
return __getname_maybe_null(name);
27792779
}
27802780
extern void putname(struct filename *name);
2781+
DEFINE_FREE(putname, struct filename *, if (!IS_ERR_OR_NULL(_T)) putname(_T))
27812782

27822783
extern int finish_open(struct file *file, struct dentry *dentry,
27832784
int (*open)(struct inode *, struct file *));

include/linux/namei.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ extern struct dentry *kern_path_create(int, const char *, struct path *, unsigne
6161
extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int);
6262
extern void done_path_create(struct path *, struct dentry *);
6363
extern struct dentry *kern_path_locked(const char *, struct path *);
64+
extern struct dentry *kern_path_locked_negative(const char *, struct path *);
6465
extern struct dentry *user_path_locked_at(int , const char __user *, struct path *);
6566
int vfs_path_parent_lookup(struct filename *filename, unsigned int flags,
6667
struct path *parent, struct qstr *last, int *type,

kernel/audit_watch.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -347,12 +347,17 @@ static void audit_remove_parent_watches(struct audit_parent *parent)
347347
/* Get path information necessary for adding watches. */
348348
static int audit_get_nd(struct audit_watch *watch, struct path *parent)
349349
{
350-
struct dentry *d = kern_path_locked(watch->path, parent);
350+
struct dentry *d;
351+
352+
d = kern_path_locked_negative(watch->path, parent);
351353
if (IS_ERR(d))
352354
return PTR_ERR(d);
353-
/* update watch filter fields */
354-
watch->dev = d->d_sb->s_dev;
355-
watch->ino = d_backing_inode(d)->i_ino;
355+
356+
if (d_is_positive(d)) {
357+
/* update watch filter fields */
358+
watch->dev = d->d_sb->s_dev;
359+
watch->ino = d_backing_inode(d)->i_ino;
360+
}
356361

357362
inode_unlock(d_backing_inode(parent->dentry));
358363
dput(d);
@@ -418,11 +423,10 @@ int audit_add_watch(struct audit_krule *krule, struct list_head **list)
418423
/* caller expects mutex locked */
419424
mutex_lock(&audit_filter_mutex);
420425

421-
if (ret && ret != -ENOENT) {
426+
if (ret) {
422427
audit_put_watch(watch);
423428
return ret;
424429
}
425-
ret = 0;
426430

427431
/* either find an old parent or attach a new one */
428432
parent = audit_find_parent(d_backing_inode(parent_path.dentry));

0 commit comments

Comments
 (0)