openat() jumps to out1 on three errors that happen after the lookup has succeeded, and none of them drops what the lookup took:
- create() failing leaves both the negative dentry that filename_create() returned and the parent path it filled in. - The -ENOENT branch dputs the dentry, which is path.dentry here, but not the vfsmount reference held alongside it. - The -EISDIR branch drops nothing at all. The leaked vfsmount reference keeps the file system busy for good: barebox:/ mount -t efivarfs none /efivarfs barebox:/ echo -o /efivarfs/Foo-8be4df61-93ca-11d2-aa0d-00e098032b8c x open: Operation not permitted barebox:/ umount /efivarfs umount: Device or resource busy efivarfs refuses to create variables outside barebox' vendor GUID, so a single mistyped GUID is enough to reach this; any file system whose create() can fail - a full FAT, a read-only mount - gets there the same way. Release the path on all three paths, as mknodat() already does for the same filename_create() result. Assisted-by: Claude:opus-5 Signed-off-by: Ahmad Fatoum <[email protected]> --- fs/fs.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/fs.c b/fs/fs.c index dc6c30802d89..ce41f23f880b 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -2748,8 +2748,11 @@ int openat(int dirfd, const char *pathname, int flags) if (d_is_negative(dentry)) { if (flags & O_CREAT) { error = create(path.dentry, dentry); - if (error) + if (error) { + dput(dentry); + path_put(&path); goto out1; + } /* repoint path.dentry from parent to newly created entry. * path.mnt already points at the correct vfsmount, even * for a dirfd of the root directory, so that's fine. @@ -2757,12 +2760,13 @@ int openat(int dirfd, const char *pathname, int flags) dput(path.dentry); path.dentry = dentry; } else { - dput(dentry); + path_put(&path); error = -ENOENT; goto out1; } } else if (d_is_dir(dentry)) { if (!(flags & (O_PATH | O_DIRECTORY)) && !dentry_is_tftp(dentry)) { + path_put(&path); error = -EISDIR; goto out1; } -- 2.47.3
