From 24df85ffb9712cd6060588f6e08defcda5986efe Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 31 Oct 2025 13:16:03 -0400 Subject: allow to use CLASS() for struct filename * Not all users match that model, but most of them do. By the end of the series we'll be left with very few irregular ones... Added: CLASS(filename, name)(user_path) => getname(user_path) CLASS(filename_kernel, name)(string) => getname_kernel(string) CLASS(filename_flags, name)(user_path, flags) => getname_flags(user_path, flags) CLASS(filename_uflags, name)(user_path, flags) => getname_uflags(user_path, flags) CLASS(filename_maybe_null, name)(user_path, flags) => getname_maybe_null(user_path, flags) all with putname() as destructor. "flags" in filename_flags is in LOOKUP_... space, only LOOKUP_EMPTY matters. "flags" in filename_uflags and filename_maybe_null is in AT_...... space, and only AT_EMPTY_PATH matters. filename_flags conventions might be worth reconsidering later (it might or might not be better off with boolean instead) Signed-off-by: Al Viro --- include/linux/fs.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index f5c9cf28c4dc..d49b969ab432 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2517,6 +2517,12 @@ static inline struct filename *refname(struct filename *name) return name; } +DEFINE_CLASS(filename, struct filename *, putname(_T), getname(p), const char __user *p) +EXTEND_CLASS(filename, _kernel, getname_kernel(p), const char *p) +EXTEND_CLASS(filename, _flags, getname_flags(p, f), const char __user *p, unsigned int f) +EXTEND_CLASS(filename, _uflags, getname_uflags(p, f), const char __user *p, unsigned int f) +EXTEND_CLASS(filename, _maybe_null, getname_maybe_null(p, f), const char __user *p, unsigned int f) + extern int finish_open(struct file *file, struct dentry *dentry, int (*open)(struct inode *, struct file *)); extern int finish_no_open(struct file *file, struct dentry *dentry); -- cgit v1.2.3 From 41670a5900a8866b8cab52ab5936b5e9ef06fe91 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 1 Nov 2025 01:54:52 -0400 Subject: get rid of audit_reusename() Originally we tried to avoid multiple insertions into audit names array during retry loop by a cute hack - memorize the userland pointer and if there already is a match, just grab an extra reference to it. Cute as it had been, it had problems - two identical pointers had audit aux entries merged, two identical strings did not. Having different behaviour for syscalls that differ only by addresses of otherwise identical string arguments is obviously wrong - if nothing else, compiler can decide to merge identical string literals. Besides, this hack does nothing for non-audited processes - they get a fresh copy for retry. It's not time-critical, but having behaviour subtly differ that way is bogus. These days we have very few places that import filename more than once (9 functions total) and it's easy to massage them so we get rid of all re-imports. With that done, we don't need audit_reusename() anymore. There's no need to memorize userland pointer either. Acked-by: Paul Moore Signed-off-by: Al Viro --- fs/namei.c | 11 +++-------- include/linux/audit.h | 11 ----------- include/linux/fs.h | 1 - kernel/auditsc.c | 23 ----------------------- 4 files changed, 3 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/fs/namei.c b/fs/namei.c index 4595b355b3ce..3ba712032f55 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -125,9 +125,8 @@ #define EMBEDDED_NAME_MAX (PATH_MAX - offsetof(struct filename, iname)) -static inline void initname(struct filename *name, const char __user *uptr) +static inline void initname(struct filename *name) { - name->uptr = uptr; name->aname = NULL; atomic_set(&name->refcnt, 1); } @@ -139,10 +138,6 @@ getname_flags(const char __user *filename, int flags) char *kname; int len; - result = audit_reusename(filename); - if (result) - return result; - result = __getname(); if (unlikely(!result)) return ERR_PTR(-ENOMEM); @@ -210,7 +205,7 @@ getname_flags(const char __user *filename, int flags) return ERR_PTR(-ENAMETOOLONG); } } - initname(result, filename); + initname(result); audit_getname(result); return result; } @@ -268,7 +263,7 @@ struct filename *getname_kernel(const char * filename) return ERR_PTR(-ENAMETOOLONG); } memcpy((char *)result->name, filename, len); - initname(result, NULL); + initname(result); audit_getname(result); return result; } diff --git a/include/linux/audit.h b/include/linux/audit.h index 536f8ee8da81..d936a604d056 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -316,7 +316,6 @@ extern void __audit_uring_exit(int success, long code); extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3); extern void __audit_syscall_exit(int ret_success, long ret_value); -extern struct filename *__audit_reusename(const __user char *uptr); extern void __audit_getname(struct filename *name); extern void __audit_inode(struct filename *name, const struct dentry *dentry, unsigned int flags); @@ -380,12 +379,6 @@ static inline void audit_syscall_exit(void *pt_regs) __audit_syscall_exit(success, return_code); } } -static inline struct filename *audit_reusename(const __user char *name) -{ - if (unlikely(!audit_dummy_context())) - return __audit_reusename(name); - return NULL; -} static inline void audit_getname(struct filename *name) { if (unlikely(!audit_dummy_context())) @@ -624,10 +617,6 @@ static inline struct audit_context *audit_context(void) { return NULL; } -static inline struct filename *audit_reusename(const __user char *name) -{ - return NULL; -} static inline void audit_getname(struct filename *name) { } static inline void audit_inode(struct filename *name, diff --git a/include/linux/fs.h b/include/linux/fs.h index d49b969ab432..abe9c95c4874 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2411,7 +2411,6 @@ extern struct kobject *fs_kobj; struct audit_names; struct filename { const char *name; /* pointer to actual string */ - const __user char *uptr; /* original userland pointer */ atomic_t refcnt; struct audit_names *aname; const char iname[]; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index dd0563a8e0be..67d8da927381 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2169,29 +2169,6 @@ static struct audit_names *audit_alloc_name(struct audit_context *context, return aname; } -/** - * __audit_reusename - fill out filename with info from existing entry - * @uptr: userland ptr to pathname - * - * Search the audit_names list for the current audit context. If there is an - * existing entry with a matching "uptr" then return the filename - * associated with that audit_name. If not, return NULL. - */ -struct filename * -__audit_reusename(const __user char *uptr) -{ - struct audit_context *context = audit_context(); - struct audit_names *n; - - list_for_each_entry(n, &context->names_list, list) { - if (!n->name) - continue; - if (n->name->uptr == uptr) - return refname(n->name); - } - return NULL; -} - /** * __audit_getname - add a name to the list * @name: name to add -- cgit v1.2.3 From c3a3577cdb351e74d6ff6bc328c3bee18ce69298 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 19 Nov 2025 19:19:24 -0500 Subject: struct filename: use names_cachep only for getname() and friends Instances of struct filename come from names_cachep (via __getname()). That is done by getname_flags() and getname_kernel() and these two are the main callers of __getname(). However, there are other callers that simply want to allocate PATH_MAX bytes for uses that have nothing to do with struct filename. We want saner allocation rules for long pathnames, so that struct filename would *always* come from names_cachep, with the out-of-line pathname getting kmalloc'ed. For that we need to be able to change the size of objects allocated by getname_flags()/getname_kernel(). That requires the rest of __getname() users to stop using names_cachep; we could explicitly switch all of those to kmalloc(), but that would cause quite a bit of noise. So the plan is to switch getname_...() to new helpers and turn __getname() into a wrapper for kmalloc(). Remaining __getname() users could be converted to explicit kmalloc() at leisure, hopefully along with figuring out what size do they really want - PATH_MAX is an overkill for some of them, used out of laziness ("we have a convenient helper that does 4K allocations and that's large enough, let's use it"). As a side benefit, names_cachep is no longer used outside of fs/namei.c, so we can move it there and be done with that. Signed-off-by: Al Viro --- fs/dcache.c | 8 +------- fs/internal.h | 2 ++ fs/namei.c | 37 ++++++++++++++++++++++++++++--------- include/linux/fs.h | 6 ++---- 4 files changed, 33 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/fs/dcache.c b/fs/dcache.c index dc2fff4811d1..cf865c12cdf9 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -3290,10 +3290,6 @@ static void __init dcache_init(void) runtime_const_init(ptr, dentry_hashtable); } -/* SLAB cache for __getname() consumers */ -struct kmem_cache *names_cachep __ro_after_init; -EXPORT_SYMBOL(names_cachep); - void __init vfs_caches_init_early(void) { int i; @@ -3307,9 +3303,7 @@ void __init vfs_caches_init_early(void) void __init vfs_caches_init(void) { - names_cachep = kmem_cache_create_usercopy("names_cache", PATH_MAX, 0, - SLAB_HWCACHE_ALIGN|SLAB_PANIC, 0, PATH_MAX, NULL); - + filename_init(); dcache_init(); inode_init(); files_init(); diff --git a/fs/internal.h b/fs/internal.h index 7267aa0926a1..c7a34412399e 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -72,6 +72,8 @@ struct dentry *start_dirop(struct dentry *parent, struct qstr *name, unsigned int lookup_flags); int lookup_noperm_common(struct qstr *qname, struct dentry *base); +void __init filename_init(void); + /* * namespace.c */ diff --git a/fs/namei.c b/fs/namei.c index 953cd254216d..f0be36e257a7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -125,6 +125,25 @@ #define EMBEDDED_NAME_MAX (PATH_MAX - offsetof(struct filename, iname)) +/* SLAB cache for struct filename instances */ +static struct kmem_cache *names_cachep __ro_after_init; + +void __init filename_init(void) +{ + names_cachep = kmem_cache_create_usercopy("names_cache", PATH_MAX, 0, + SLAB_HWCACHE_ALIGN|SLAB_PANIC, 0, PATH_MAX, NULL); +} + +static inline struct filename *alloc_filename(void) +{ + return kmem_cache_alloc(names_cachep, GFP_KERNEL); +} + +static inline void free_filename(struct filename *p) +{ + kmem_cache_free(names_cachep, p); +} + static inline void initname(struct filename *name) { name->aname = NULL; @@ -164,7 +183,7 @@ getname_flags(const char __user *filename, int flags) char *kname; int len; - result = __getname(); + result = alloc_filename(); if (unlikely(!result)) return ERR_PTR(-ENOMEM); @@ -181,13 +200,13 @@ getname_flags(const char __user *filename, int flags) */ if (unlikely(len <= 0)) { if (unlikely(len < 0)) { - __putname(result); + free_filename(result); return ERR_PTR(len); } /* The empty path is special. */ if (!(flags & LOOKUP_EMPTY)) { - __putname(result); + free_filename(result); return ERR_PTR(-ENOENT); } } @@ -201,7 +220,7 @@ getname_flags(const char __user *filename, int flags) if (unlikely(len == EMBEDDED_NAME_MAX)) { struct filename *p = getname_long(result, filename); if (IS_ERR(p)) { - __putname(result); + free_filename(result); return p; } result = p; @@ -242,7 +261,7 @@ struct filename *getname_kernel(const char * filename) struct filename *result; int len = strlen(filename) + 1; - result = __getname(); + result = alloc_filename(); if (unlikely(!result)) return ERR_PTR(-ENOMEM); @@ -254,13 +273,13 @@ struct filename *getname_kernel(const char * filename) tmp = kmalloc(size, GFP_KERNEL); if (unlikely(!tmp)) { - __putname(result); + free_filename(result); return ERR_PTR(-ENOMEM); } tmp->name = (char *)result; result = tmp; } else { - __putname(result); + free_filename(result); return ERR_PTR(-ENAMETOOLONG); } memcpy((char *)result->name, filename, len); @@ -287,10 +306,10 @@ void putname(struct filename *name) } if (unlikely(name->name != name->iname)) { - __putname(name->name); + free_filename((struct filename *)name->name); kfree(name); } else - __putname(name); + free_filename(name); } EXPORT_SYMBOL(putname); diff --git a/include/linux/fs.h b/include/linux/fs.h index abe9c95c4874..997d515bab32 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2539,10 +2539,8 @@ static inline int finish_open_simple(struct file *file, int error) extern void __init vfs_caches_init_early(void); extern void __init vfs_caches_init(void); -extern struct kmem_cache *names_cachep; - -#define __getname() kmem_cache_alloc(names_cachep, GFP_KERNEL) -#define __putname(name) kmem_cache_free(names_cachep, (void *)(name)) +#define __getname() kmalloc(PATH_MAX, GFP_KERNEL) +#define __putname(name) kfree(name) void emergency_thaw_all(void); extern int sync_filesystem(struct super_block *); -- cgit v1.2.3 From 8c888b31903cc2acfbf054c23d702caf68857810 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 19 Nov 2025 19:45:04 -0500 Subject: struct filename: saner handling of long names Always allocate struct filename from names_cachep, long name or short; short names would be embedded into struct filename. Longer ones do not cannibalize the original struct filename - put them into kmalloc'ed buffers (PATH_MAX-sized for import from userland, strlen() + 1 - for ones originating kernel-side, where we know the length beforehand). Cutoff length for short names is chosen so that struct filename would be 192 bytes long - that's both a multiple of 64 and large enough to cover the majority of real-world uses. Simplifies logics in getname()/putname() and friends. [fixed an embarrassing braino in EMBEDDED_NAME_MAX, first reported by Dan Carpenter] Signed-off-by: Al Viro --- fs/namei.c | 87 +++++++++++++++++++++--------------------------------- include/linux/fs.h | 10 +++++-- 2 files changed, 41 insertions(+), 56 deletions(-) (limited to 'include') diff --git a/fs/namei.c b/fs/namei.c index f0be36e257a7..57043b81fe27 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -123,15 +123,14 @@ * PATH_MAX includes the nul terminator --RR. */ -#define EMBEDDED_NAME_MAX (PATH_MAX - offsetof(struct filename, iname)) - /* SLAB cache for struct filename instances */ static struct kmem_cache *names_cachep __ro_after_init; void __init filename_init(void) { - names_cachep = kmem_cache_create_usercopy("names_cache", PATH_MAX, 0, - SLAB_HWCACHE_ALIGN|SLAB_PANIC, 0, PATH_MAX, NULL); + names_cachep = kmem_cache_create_usercopy("names_cache", sizeof(struct filename), 0, + SLAB_HWCACHE_ALIGN|SLAB_PANIC, offsetof(struct filename, iname), + EMBEDDED_NAME_MAX, NULL); } static inline struct filename *alloc_filename(void) @@ -150,30 +149,23 @@ static inline void initname(struct filename *name) atomic_set(&name->refcnt, 1); } -static struct filename *getname_long(struct filename *old, - const char __user *filename) +static int getname_long(struct filename *name, const char __user *filename) { int len; - /* - * size is chosen that way we to guarantee that - * p->iname[0] is within the same object and that - * p->name can't be equal to p->iname, no matter what. - */ - const size_t size = offsetof(struct filename, iname[1]); - struct filename *p __free(kfree) = kzalloc(size, GFP_KERNEL); + char *p __free(kfree) = kmalloc(PATH_MAX, GFP_KERNEL); if (unlikely(!p)) - return ERR_PTR(-ENOMEM); + return -ENOMEM; - memmove(old, &old->iname, EMBEDDED_NAME_MAX); - p->name = (char *)old; - len = strncpy_from_user((char *)old + EMBEDDED_NAME_MAX, + memcpy(p, &name->iname, EMBEDDED_NAME_MAX); + len = strncpy_from_user(p + EMBEDDED_NAME_MAX, filename + EMBEDDED_NAME_MAX, PATH_MAX - EMBEDDED_NAME_MAX); if (unlikely(len < 0)) - return ERR_PTR(len); + return len; if (unlikely(len == PATH_MAX - EMBEDDED_NAME_MAX)) - return ERR_PTR(-ENAMETOOLONG); - return no_free_ptr(p); + return -ENAMETOOLONG; + name->name = no_free_ptr(p); + return 0; } struct filename * @@ -199,16 +191,9 @@ getname_flags(const char __user *filename, int flags) * Handle both empty path and copy failure in one go. */ if (unlikely(len <= 0)) { - if (unlikely(len < 0)) { - free_filename(result); - return ERR_PTR(len); - } - /* The empty path is special. */ - if (!(flags & LOOKUP_EMPTY)) { - free_filename(result); - return ERR_PTR(-ENOENT); - } + if (!len && !(flags & LOOKUP_EMPTY)) + len = -ENOENT; } /* @@ -217,14 +202,13 @@ getname_flags(const char __user *filename, int flags) * names_cache allocation for the pathname, and re-do the copy from * userland. */ - if (unlikely(len == EMBEDDED_NAME_MAX)) { - struct filename *p = getname_long(result, filename); - if (IS_ERR(p)) { - free_filename(result); - return p; - } - result = p; + if (unlikely(len == EMBEDDED_NAME_MAX)) + len = getname_long(result, filename); + if (unlikely(len < 0)) { + free_filename(result); + return ERR_PTR(len); } + initname(result); audit_getname(result); return result; @@ -260,29 +244,26 @@ struct filename *getname_kernel(const char * filename) { struct filename *result; int len = strlen(filename) + 1; + char *p; + + if (unlikely(len > PATH_MAX)) + return ERR_PTR(-ENAMETOOLONG); result = alloc_filename(); if (unlikely(!result)) return ERR_PTR(-ENOMEM); if (len <= EMBEDDED_NAME_MAX) { - result->name = (char *)result->iname; - } else if (len <= PATH_MAX) { - const size_t size = offsetof(struct filename, iname[1]); - struct filename *tmp; - - tmp = kmalloc(size, GFP_KERNEL); - if (unlikely(!tmp)) { + p = (char *)result->iname; + memcpy(p, filename, len); + } else { + p = kmemdup(filename, len, GFP_KERNEL); + if (unlikely(!p)) { free_filename(result); return ERR_PTR(-ENOMEM); } - tmp->name = (char *)result; - result = tmp; - } else { - free_filename(result); - return ERR_PTR(-ENAMETOOLONG); } - memcpy((char *)result->name, filename, len); + result->name = p; initname(result); audit_getname(result); return result; @@ -305,11 +286,9 @@ void putname(struct filename *name) return; } - if (unlikely(name->name != name->iname)) { - free_filename((struct filename *)name->name); - kfree(name); - } else - free_filename(name); + if (unlikely(name->name != name->iname)) + kfree(name->name); + free_filename(name); } EXPORT_SYMBOL(putname); diff --git a/include/linux/fs.h b/include/linux/fs.h index 997d515bab32..f0f1e8034539 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2409,13 +2409,19 @@ extern struct kobject *fs_kobj; /* fs/open.c */ struct audit_names; -struct filename { + +struct __filename_head { const char *name; /* pointer to actual string */ atomic_t refcnt; struct audit_names *aname; - const char iname[]; +}; +#define EMBEDDED_NAME_MAX (192 - sizeof(struct __filename_head)) +struct filename { + struct __filename_head; + const char iname[EMBEDDED_NAME_MAX]; }; static_assert(offsetof(struct filename, iname) % sizeof(long) == 0); +static_assert(sizeof(struct filename) % 64 == 0); static inline struct mnt_idmap *file_mnt_idmap(const struct file *file) { -- cgit v1.2.3 From 7ca83f8ebe86706c4164ac21871f4a5adb86a430 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 1 Dec 2025 09:32:26 +0100 Subject: fs: hide names_cache behind runtime const machinery s/names_cachep/names_cache/ for consistency with dentry cache. Signed-off-by: Mateusz Guzik Signed-off-by: Al Viro --- fs/namei.c | 16 ++++++++++------ include/asm-generic/vmlinux.lds.h | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/fs/namei.c b/fs/namei.c index 57043b81fe27..06d60808b0ff 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -41,6 +41,8 @@ #include #include +#include + #include "internal.h" #include "mount.h" @@ -124,23 +126,25 @@ */ /* SLAB cache for struct filename instances */ -static struct kmem_cache *names_cachep __ro_after_init; +static struct kmem_cache *__names_cache __ro_after_init; +#define names_cache runtime_const_ptr(__names_cache) void __init filename_init(void) { - names_cachep = kmem_cache_create_usercopy("names_cache", sizeof(struct filename), 0, - SLAB_HWCACHE_ALIGN|SLAB_PANIC, offsetof(struct filename, iname), - EMBEDDED_NAME_MAX, NULL); + __names_cache = kmem_cache_create_usercopy("names_cache", sizeof(struct filename), 0, + SLAB_HWCACHE_ALIGN|SLAB_PANIC, offsetof(struct filename, iname), + EMBEDDED_NAME_MAX, NULL); + runtime_const_init(ptr, __names_cache); } static inline struct filename *alloc_filename(void) { - return kmem_cache_alloc(names_cachep, GFP_KERNEL); + return kmem_cache_alloc(names_cache, GFP_KERNEL); } static inline void free_filename(struct filename *p) { - kmem_cache_free(names_cachep, p); + kmem_cache_free(names_cache, p); } static inline void initname(struct filename *name) diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 8ca130af301f..eeb070f330bd 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -972,7 +972,8 @@ #define RUNTIME_CONST_VARIABLES \ RUNTIME_CONST(shift, d_hash_shift) \ RUNTIME_CONST(ptr, dentry_hashtable) \ - RUNTIME_CONST(ptr, __dentry_cache) + RUNTIME_CONST(ptr, __dentry_cache) \ + RUNTIME_CONST(ptr, __names_cache) /* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */ #define KUNIT_TABLE() \ -- cgit v1.2.3 From 9fa3ec84587c5eca7580eafc27eee332bc3a5a0e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 1 Nov 2025 20:29:06 -0400 Subject: allow incomplete imports of filenames There are two filename-related problems in io_uring and its interplay with audit. Filenames are imported when request is submitted and used when it is processed. Unfortunately, the latter may very well happen in a different thread. In that case the reference to filename is put into the wrong audit_context - that of submitting thread, not the processing one. Audit logics is called by the latter, and it really wants to be able to find the names in audit_context current (== processing) thread. Another related problem is the headache with refcounts - normally all references to given struct filename are visible only to one thread (the one that uses that struct filename). io_uring violates that - an extra reference is stashed in audit_context of submitter. It gets dropped when submitter returns to userland, which can happen simultaneously with processing thread deciding to drop the reference it got. We paper over that by making refcount atomic, but that means pointless headache for everyone. Solution: the notion of partially imported filenames. Namely, already copied from userland, but *not* exposed to audit yet. io_uring can create that in submitter thread, and complete the import (obtaining the usual reference to struct filename) in processing thread. Object: struct delayed_filename. Primitives for working with it: delayed_getname(&delayed_filename, user_string) - copies the name from userland, returning 0 and stashing the address of (still incomplete) struct filename in delayed_filename on success and returning -E... on error. delayed_getname_uflags(&delayed_filename, user_string, atflags) - similar, in the same relation to delayed_getname() as getname_uflags() is to getname() complete_getname(&delayed_filename) - completes the import of filename stashed in delayed_filename and returns struct filename to caller, emptying delayed_filename. CLASS(filename_complete_delayed, name)(&delayed_filename) - variant of CLASS(filename) with complete_getname() for constructor. dismiss_delayed_filename(&delayed_filename) - destructor; drops whatever might be stashed in delayed_filename, emptying it. putname_to_delayed(&delayed_filename, name) - if name is shared, stashes its copy into delayed_filename and drops the reference to name, otherwise stashes the name itself in there. Signed-off-by: Al Viro --- fs/namei.c | 66 ++++++++++++++++++++++++++++++--- include/linux/fs.h | 12 ++++++ io_uring/fs.c | 101 +++++++++++++++++++++++++++------------------------ io_uring/openclose.c | 26 ++++++------- io_uring/statx.c | 17 ++++----- io_uring/xattr.c | 30 +++++---------- 6 files changed, 157 insertions(+), 95 deletions(-) (limited to 'include') diff --git a/fs/namei.c b/fs/namei.c index f1a2161bd691..b76cc43fe89d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -172,8 +172,8 @@ static int getname_long(struct filename *name, const char __user *filename) return 0; } -struct filename * -getname_flags(const char __user *filename, int flags) +static struct filename * +do_getname(const char __user *filename, int flags, bool incomplete) { struct filename *result; char *kname; @@ -214,10 +214,17 @@ getname_flags(const char __user *filename, int flags) } initname(result); - audit_getname(result); + if (likely(!incomplete)) + audit_getname(result); return result; } +struct filename * +getname_flags(const char __user *filename, int flags) +{ + return do_getname(filename, flags, false); +} + struct filename *getname_uflags(const char __user *filename, int uflags) { int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0; @@ -242,7 +249,7 @@ struct filename *__getname_maybe_null(const char __user *pathname) return no_free_ptr(name); } -struct filename *getname_kernel(const char * filename) +static struct filename *do_getname_kernel(const char *filename, bool incomplete) { struct filename *result; int len = strlen(filename) + 1; @@ -267,9 +274,15 @@ struct filename *getname_kernel(const char * filename) } result->name = p; initname(result); - audit_getname(result); + if (likely(!incomplete)) + audit_getname(result); return result; } + +struct filename *getname_kernel(const char *filename) +{ + return do_getname_kernel(filename, false); +} EXPORT_SYMBOL(getname_kernel); void putname(struct filename *name) @@ -294,6 +307,49 @@ void putname(struct filename *name) } EXPORT_SYMBOL(putname); +static inline int __delayed_getname(struct delayed_filename *v, + const char __user *string, int flags) +{ + v->__incomplete_filename = do_getname(string, flags, true); + return PTR_ERR_OR_ZERO(v->__incomplete_filename); +} + +int delayed_getname(struct delayed_filename *v, const char __user *string) +{ + return __delayed_getname(v, string, 0); +} + +int delayed_getname_uflags(struct delayed_filename *v, const char __user *string, + int uflags) +{ + int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0; + return __delayed_getname(v, string, flags); +} + +int putname_to_delayed(struct delayed_filename *v, struct filename *name) +{ + if (likely(atomic_read(&name->refcnt) == 1)) { + v->__incomplete_filename = name; + return 0; + } + v->__incomplete_filename = do_getname_kernel(name->name, true); + putname(name); + return PTR_ERR_OR_ZERO(v->__incomplete_filename); +} + +void dismiss_delayed_filename(struct delayed_filename *v) +{ + putname(no_free_ptr(v->__incomplete_filename)); +} + +struct filename *complete_getname(struct delayed_filename *v) +{ + struct filename *res = no_free_ptr(v->__incomplete_filename); + if (!IS_ERR(res)) + audit_getname(res); + return res; +} + /** * check_acl - perform ACL permission checking * @idmap: idmap of the mount the inode was found from diff --git a/include/linux/fs.h b/include/linux/fs.h index f0f1e8034539..f1612a7dffd0 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2516,6 +2516,17 @@ static inline struct filename *getname_maybe_null(const char __user *name, int f extern void putname(struct filename *name); DEFINE_FREE(putname, struct filename *, if (!IS_ERR_OR_NULL(_T)) putname(_T)) +struct delayed_filename { + struct filename *__incomplete_filename; // don't touch +}; +#define INIT_DELAYED_FILENAME(ptr) \ + ((void)(*(ptr) = (struct delayed_filename){})) +int delayed_getname(struct delayed_filename *, const char __user *); +int delayed_getname_uflags(struct delayed_filename *v, const char __user *, int); +void dismiss_delayed_filename(struct delayed_filename *); +int putname_to_delayed(struct delayed_filename *, struct filename *); +struct filename *complete_getname(struct delayed_filename *); + static inline struct filename *refname(struct filename *name) { atomic_inc(&name->refcnt); @@ -2527,6 +2538,7 @@ EXTEND_CLASS(filename, _kernel, getname_kernel(p), const char *p) EXTEND_CLASS(filename, _flags, getname_flags(p, f), const char __user *p, unsigned int f) EXTEND_CLASS(filename, _uflags, getname_uflags(p, f), const char __user *p, unsigned int f) EXTEND_CLASS(filename, _maybe_null, getname_maybe_null(p, f), const char __user *p, unsigned int f) +EXTEND_CLASS(filename, _complete_delayed, complete_getname(p), struct delayed_filename *p) extern int finish_open(struct file *file, struct dentry *dentry, int (*open)(struct inode *, struct file *)); diff --git a/io_uring/fs.c b/io_uring/fs.c index 37079a414eab..c04c6282210a 100644 --- a/io_uring/fs.c +++ b/io_uring/fs.c @@ -19,8 +19,8 @@ struct io_rename { struct file *file; int old_dfd; int new_dfd; - struct filename *oldpath; - struct filename *newpath; + struct delayed_filename oldpath; + struct delayed_filename newpath; int flags; }; @@ -28,22 +28,22 @@ struct io_unlink { struct file *file; int dfd; int flags; - struct filename *filename; + struct delayed_filename filename; }; struct io_mkdir { struct file *file; int dfd; umode_t mode; - struct filename *filename; + struct delayed_filename filename; }; struct io_link { struct file *file; int old_dfd; int new_dfd; - struct filename *oldpath; - struct filename *newpath; + struct delayed_filename oldpath; + struct delayed_filename newpath; int flags; }; @@ -51,6 +51,7 @@ int io_renameat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { struct io_rename *ren = io_kiocb_to_cmd(req, struct io_rename); const char __user *oldf, *newf; + int err; if (sqe->buf_index || sqe->splice_fd_in) return -EINVAL; @@ -63,14 +64,14 @@ int io_renameat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) ren->new_dfd = READ_ONCE(sqe->len); ren->flags = READ_ONCE(sqe->rename_flags); - ren->oldpath = getname(oldf); - if (IS_ERR(ren->oldpath)) - return PTR_ERR(ren->oldpath); + err = delayed_getname(&ren->oldpath, oldf); + if (unlikely(err)) + return err; - ren->newpath = getname(newf); - if (IS_ERR(ren->newpath)) { - putname(ren->oldpath); - return PTR_ERR(ren->newpath); + err = delayed_getname(&ren->newpath, newf); + if (unlikely(err)) { + dismiss_delayed_filename(&ren->oldpath); + return err; } req->flags |= REQ_F_NEED_CLEANUP; @@ -85,8 +86,9 @@ int io_renameat(struct io_kiocb *req, unsigned int issue_flags) WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); - ret = do_renameat2(ren->old_dfd, ren->oldpath, ren->new_dfd, - ren->newpath, ren->flags); + ret = do_renameat2(ren->old_dfd, complete_getname(&ren->oldpath), + ren->new_dfd, complete_getname(&ren->newpath), + ren->flags); req->flags &= ~REQ_F_NEED_CLEANUP; io_req_set_res(req, ret, 0); @@ -97,14 +99,15 @@ void io_renameat_cleanup(struct io_kiocb *req) { struct io_rename *ren = io_kiocb_to_cmd(req, struct io_rename); - putname(ren->oldpath); - putname(ren->newpath); + dismiss_delayed_filename(&ren->oldpath); + dismiss_delayed_filename(&ren->newpath); } int io_unlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { struct io_unlink *un = io_kiocb_to_cmd(req, struct io_unlink); const char __user *fname; + int err; if (sqe->off || sqe->len || sqe->buf_index || sqe->splice_fd_in) return -EINVAL; @@ -118,9 +121,9 @@ int io_unlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) return -EINVAL; fname = u64_to_user_ptr(READ_ONCE(sqe->addr)); - un->filename = getname(fname); - if (IS_ERR(un->filename)) - return PTR_ERR(un->filename); + err = delayed_getname(&un->filename, fname); + if (unlikely(err)) + return err; req->flags |= REQ_F_NEED_CLEANUP; req->flags |= REQ_F_FORCE_ASYNC; @@ -135,9 +138,9 @@ int io_unlinkat(struct io_kiocb *req, unsigned int issue_flags) WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); if (un->flags & AT_REMOVEDIR) - ret = do_rmdir(un->dfd, un->filename); + ret = do_rmdir(un->dfd, complete_getname(&un->filename)); else - ret = do_unlinkat(un->dfd, un->filename); + ret = do_unlinkat(un->dfd, complete_getname(&un->filename)); req->flags &= ~REQ_F_NEED_CLEANUP; io_req_set_res(req, ret, 0); @@ -148,13 +151,14 @@ void io_unlinkat_cleanup(struct io_kiocb *req) { struct io_unlink *ul = io_kiocb_to_cmd(req, struct io_unlink); - putname(ul->filename); + dismiss_delayed_filename(&ul->filename); } int io_mkdirat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { struct io_mkdir *mkd = io_kiocb_to_cmd(req, struct io_mkdir); const char __user *fname; + int err; if (sqe->off || sqe->rw_flags || sqe->buf_index || sqe->splice_fd_in) return -EINVAL; @@ -165,9 +169,9 @@ int io_mkdirat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) mkd->mode = READ_ONCE(sqe->len); fname = u64_to_user_ptr(READ_ONCE(sqe->addr)); - mkd->filename = getname(fname); - if (IS_ERR(mkd->filename)) - return PTR_ERR(mkd->filename); + err = delayed_getname(&mkd->filename, fname); + if (unlikely(err)) + return err; req->flags |= REQ_F_NEED_CLEANUP; req->flags |= REQ_F_FORCE_ASYNC; @@ -181,7 +185,7 @@ int io_mkdirat(struct io_kiocb *req, unsigned int issue_flags) WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); - ret = do_mkdirat(mkd->dfd, mkd->filename, mkd->mode); + ret = do_mkdirat(mkd->dfd, complete_getname(&mkd->filename), mkd->mode); req->flags &= ~REQ_F_NEED_CLEANUP; io_req_set_res(req, ret, 0); @@ -192,13 +196,14 @@ void io_mkdirat_cleanup(struct io_kiocb *req) { struct io_mkdir *md = io_kiocb_to_cmd(req, struct io_mkdir); - putname(md->filename); + dismiss_delayed_filename(&md->filename); } int io_symlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { struct io_link *sl = io_kiocb_to_cmd(req, struct io_link); const char __user *oldpath, *newpath; + int err; if (sqe->len || sqe->rw_flags || sqe->buf_index || sqe->splice_fd_in) return -EINVAL; @@ -209,14 +214,14 @@ int io_symlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) oldpath = u64_to_user_ptr(READ_ONCE(sqe->addr)); newpath = u64_to_user_ptr(READ_ONCE(sqe->addr2)); - sl->oldpath = getname(oldpath); - if (IS_ERR(sl->oldpath)) - return PTR_ERR(sl->oldpath); + err = delayed_getname(&sl->oldpath, oldpath); + if (unlikely(err)) + return err; - sl->newpath = getname(newpath); - if (IS_ERR(sl->newpath)) { - putname(sl->oldpath); - return PTR_ERR(sl->newpath); + err = delayed_getname(&sl->newpath, newpath); + if (unlikely(err)) { + dismiss_delayed_filename(&sl->oldpath); + return err; } req->flags |= REQ_F_NEED_CLEANUP; @@ -231,7 +236,8 @@ int io_symlinkat(struct io_kiocb *req, unsigned int issue_flags) WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); - ret = do_symlinkat(sl->oldpath, sl->new_dfd, sl->newpath); + ret = do_symlinkat(complete_getname(&sl->oldpath), sl->new_dfd, + complete_getname(&sl->newpath)); req->flags &= ~REQ_F_NEED_CLEANUP; io_req_set_res(req, ret, 0); @@ -242,6 +248,7 @@ int io_linkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { struct io_link *lnk = io_kiocb_to_cmd(req, struct io_link); const char __user *oldf, *newf; + int err; if (sqe->buf_index || sqe->splice_fd_in) return -EINVAL; @@ -254,14 +261,14 @@ int io_linkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) newf = u64_to_user_ptr(READ_ONCE(sqe->addr2)); lnk->flags = READ_ONCE(sqe->hardlink_flags); - lnk->oldpath = getname_uflags(oldf, lnk->flags); - if (IS_ERR(lnk->oldpath)) - return PTR_ERR(lnk->oldpath); + err = delayed_getname_uflags(&lnk->oldpath, oldf, lnk->flags); + if (unlikely(err)) + return err; - lnk->newpath = getname(newf); - if (IS_ERR(lnk->newpath)) { - putname(lnk->oldpath); - return PTR_ERR(lnk->newpath); + err = delayed_getname(&lnk->newpath, newf); + if (unlikely(err)) { + dismiss_delayed_filename(&lnk->oldpath); + return err; } req->flags |= REQ_F_NEED_CLEANUP; @@ -276,8 +283,8 @@ int io_linkat(struct io_kiocb *req, unsigned int issue_flags) WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); - ret = do_linkat(lnk->old_dfd, lnk->oldpath, lnk->new_dfd, - lnk->newpath, lnk->flags); + ret = do_linkat(lnk->old_dfd, complete_getname(&lnk->oldpath), + lnk->new_dfd, complete_getname(&lnk->newpath), lnk->flags); req->flags &= ~REQ_F_NEED_CLEANUP; io_req_set_res(req, ret, 0); @@ -288,6 +295,6 @@ void io_link_cleanup(struct io_kiocb *req) { struct io_link *sl = io_kiocb_to_cmd(req, struct io_link); - putname(sl->oldpath); - putname(sl->newpath); + dismiss_delayed_filename(&sl->oldpath); + dismiss_delayed_filename(&sl->newpath); } diff --git a/io_uring/openclose.c b/io_uring/openclose.c index 15dde9bd6ff6..aa3acb06247f 100644 --- a/io_uring/openclose.c +++ b/io_uring/openclose.c @@ -23,7 +23,7 @@ struct io_open { struct file *file; int dfd; u32 file_slot; - struct filename *filename; + struct delayed_filename filename; struct open_how how; unsigned long nofile; }; @@ -67,12 +67,9 @@ static int __io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe open->dfd = READ_ONCE(sqe->fd); fname = u64_to_user_ptr(READ_ONCE(sqe->addr)); - open->filename = getname(fname); - if (IS_ERR(open->filename)) { - ret = PTR_ERR(open->filename); - open->filename = NULL; + ret = delayed_getname(&open->filename, fname); + if (unlikely(ret)) return ret; - } req->flags |= REQ_F_NEED_CLEANUP; open->file_slot = READ_ONCE(sqe->file_index); @@ -121,6 +118,7 @@ int io_openat2(struct io_kiocb *req, unsigned int issue_flags) struct file *file; bool resolve_nonblock, nonblock_set; bool fixed = !!open->file_slot; + CLASS(filename_complete_delayed, name)(&open->filename); int ret; ret = build_open_flags(&open->how, &op); @@ -140,7 +138,7 @@ int io_openat2(struct io_kiocb *req, unsigned int issue_flags) goto err; } - file = do_filp_open(open->dfd, open->filename, &op); + file = do_filp_open(open->dfd, name, &op); if (IS_ERR(file)) { /* * We could hang on to this 'fd' on retrying, but seems like @@ -152,9 +150,13 @@ int io_openat2(struct io_kiocb *req, unsigned int issue_flags) ret = PTR_ERR(file); /* only retry if RESOLVE_CACHED wasn't already set by application */ - if (ret == -EAGAIN && - (!resolve_nonblock && (issue_flags & IO_URING_F_NONBLOCK))) - return -EAGAIN; + if (ret == -EAGAIN && !resolve_nonblock && + (issue_flags & IO_URING_F_NONBLOCK)) { + ret = putname_to_delayed(&open->filename, + no_free_ptr(name)); + if (likely(!ret)) + return -EAGAIN; + } goto err; } @@ -167,7 +169,6 @@ int io_openat2(struct io_kiocb *req, unsigned int issue_flags) ret = io_fixed_fd_install(req, issue_flags, file, open->file_slot); err: - putname(open->filename); req->flags &= ~REQ_F_NEED_CLEANUP; if (ret < 0) req_set_fail(req); @@ -184,8 +185,7 @@ void io_open_cleanup(struct io_kiocb *req) { struct io_open *open = io_kiocb_to_cmd(req, struct io_open); - if (open->filename) - putname(open->filename); + dismiss_delayed_filename(&open->filename); } int __io_close_fixed(struct io_ring_ctx *ctx, unsigned int issue_flags, diff --git a/io_uring/statx.c b/io_uring/statx.c index 5111e9befbfe..7bcae4a6c4a3 100644 --- a/io_uring/statx.c +++ b/io_uring/statx.c @@ -16,7 +16,7 @@ struct io_statx { int dfd; unsigned int mask; unsigned int flags; - struct filename *filename; + struct delayed_filename filename; struct statx __user *buffer; }; @@ -24,6 +24,7 @@ int io_statx_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { struct io_statx *sx = io_kiocb_to_cmd(req, struct io_statx); const char __user *path; + int ret; if (sqe->buf_index || sqe->splice_fd_in) return -EINVAL; @@ -36,14 +37,10 @@ int io_statx_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) sx->buffer = u64_to_user_ptr(READ_ONCE(sqe->addr2)); sx->flags = READ_ONCE(sqe->statx_flags); - sx->filename = getname_uflags(path, sx->flags); - - if (IS_ERR(sx->filename)) { - int ret = PTR_ERR(sx->filename); + ret = delayed_getname_uflags(&sx->filename, path, sx->flags); - sx->filename = NULL; + if (unlikely(ret)) return ret; - } req->flags |= REQ_F_NEED_CLEANUP; req->flags |= REQ_F_FORCE_ASYNC; @@ -53,11 +50,12 @@ int io_statx_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_statx(struct io_kiocb *req, unsigned int issue_flags) { struct io_statx *sx = io_kiocb_to_cmd(req, struct io_statx); + CLASS(filename_complete_delayed, name)(&sx->filename); int ret; WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); - ret = do_statx(sx->dfd, sx->filename, sx->flags, sx->mask, sx->buffer); + ret = do_statx(sx->dfd, name, sx->flags, sx->mask, sx->buffer); io_req_set_res(req, ret, 0); return IOU_COMPLETE; } @@ -66,6 +64,5 @@ void io_statx_cleanup(struct io_kiocb *req) { struct io_statx *sx = io_kiocb_to_cmd(req, struct io_statx); - if (sx->filename) - putname(sx->filename); + dismiss_delayed_filename(&sx->filename); } diff --git a/io_uring/xattr.c b/io_uring/xattr.c index 322b94ff9e4b..0fb4e5303500 100644 --- a/io_uring/xattr.c +++ b/io_uring/xattr.c @@ -19,16 +19,14 @@ struct io_xattr { struct file *file; struct kernel_xattr_ctx ctx; - struct filename *filename; + struct delayed_filename filename; }; void io_xattr_cleanup(struct io_kiocb *req) { struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr); - if (ix->filename) - putname(ix->filename); - + dismiss_delayed_filename(&ix->filename); kfree(ix->ctx.kname); kvfree(ix->ctx.kvalue); } @@ -48,7 +46,7 @@ static int __io_getxattr_prep(struct io_kiocb *req, const char __user *name; int ret; - ix->filename = NULL; + INIT_DELAYED_FILENAME(&ix->filename); ix->ctx.kvalue = NULL; name = u64_to_user_ptr(READ_ONCE(sqe->addr)); ix->ctx.value = u64_to_user_ptr(READ_ONCE(sqe->addr2)); @@ -93,11 +91,7 @@ int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) path = u64_to_user_ptr(READ_ONCE(sqe->addr3)); - ix->filename = getname(path); - if (IS_ERR(ix->filename)) - return PTR_ERR(ix->filename); - - return 0; + return delayed_getname(&ix->filename, path); } int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags) @@ -119,8 +113,8 @@ int io_getxattr(struct io_kiocb *req, unsigned int issue_flags) WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); - ret = filename_getxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx); - ix->filename = NULL; + ret = filename_getxattr(AT_FDCWD, complete_getname(&ix->filename), + LOOKUP_FOLLOW, &ix->ctx); io_xattr_finish(req, ret); return IOU_COMPLETE; } @@ -132,7 +126,7 @@ static int __io_setxattr_prep(struct io_kiocb *req, const char __user *name; int ret; - ix->filename = NULL; + INIT_DELAYED_FILENAME(&ix->filename); name = u64_to_user_ptr(READ_ONCE(sqe->addr)); ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2)); ix->ctx.kvalue = NULL; @@ -169,11 +163,7 @@ int io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) path = u64_to_user_ptr(READ_ONCE(sqe->addr3)); - ix->filename = getname(path); - if (IS_ERR(ix->filename)) - return PTR_ERR(ix->filename); - - return 0; + return delayed_getname(&ix->filename, path); } int io_fsetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) @@ -200,8 +190,8 @@ int io_setxattr(struct io_kiocb *req, unsigned int issue_flags) WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); - ret = filename_setxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx); - ix->filename = NULL; + ret = filename_setxattr(AT_FDCWD, complete_getname(&ix->filename), + LOOKUP_FOLLOW, &ix->ctx); io_xattr_finish(req, ret); return IOU_COMPLETE; } -- cgit v1.2.3 From 741c97fecb6a4160014a76759e9b8c0880fc44f1 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 2 Nov 2025 01:01:47 -0400 Subject: struct filename ->refcnt doesn't need to be atomic ... or visible outside of audit, really. Note that references held in delayed_filename always have refcount 1, and from the moment of complete_getname() or equivalent point in getname...() there won't be any references to struct filename instance left in places visible to other threads. Acked-by: Paul Moore Signed-off-by: Al Viro --- fs/namei.c | 12 ++++++------ include/linux/fs.h | 8 +------- kernel/auditsc.c | 6 +++--- 3 files changed, 10 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/fs/namei.c b/fs/namei.c index b76cc43fe89d..f4359825ba48 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -150,7 +150,7 @@ static inline void free_filename(struct filename *p) static inline void initname(struct filename *name) { name->aname = NULL; - atomic_set(&name->refcnt, 1); + name->refcnt = 1; } static int getname_long(struct filename *name, const char __user *filename) @@ -292,13 +292,13 @@ void putname(struct filename *name) if (IS_ERR_OR_NULL(name)) return; - refcnt = atomic_read(&name->refcnt); + refcnt = name->refcnt; if (unlikely(refcnt != 1)) { if (WARN_ON_ONCE(!refcnt)) return; - if (!atomic_dec_and_test(&name->refcnt)) - return; + name->refcnt--; + return; } if (unlikely(name->name != name->iname)) @@ -328,12 +328,12 @@ int delayed_getname_uflags(struct delayed_filename *v, const char __user *string int putname_to_delayed(struct delayed_filename *v, struct filename *name) { - if (likely(atomic_read(&name->refcnt) == 1)) { + if (likely(name->refcnt == 1)) { v->__incomplete_filename = name; return 0; } + name->refcnt--; v->__incomplete_filename = do_getname_kernel(name->name, true); - putname(name); return PTR_ERR_OR_ZERO(v->__incomplete_filename); } diff --git a/include/linux/fs.h b/include/linux/fs.h index f1612a7dffd0..6a26ee347517 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2412,7 +2412,7 @@ struct audit_names; struct __filename_head { const char *name; /* pointer to actual string */ - atomic_t refcnt; + int refcnt; struct audit_names *aname; }; #define EMBEDDED_NAME_MAX (192 - sizeof(struct __filename_head)) @@ -2527,12 +2527,6 @@ void dismiss_delayed_filename(struct delayed_filename *); int putname_to_delayed(struct delayed_filename *, struct filename *); struct filename *complete_getname(struct delayed_filename *); -static inline struct filename *refname(struct filename *name) -{ - atomic_inc(&name->refcnt); - return name; -} - DEFINE_CLASS(filename, struct filename *, putname(_T), getname(p), const char __user *p) EXTEND_CLASS(filename, _kernel, getname_kernel(p), const char *p) EXTEND_CLASS(filename, _flags, getname_flags(p, f), const char __user *p, unsigned int f) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 67d8da927381..86a44b162a87 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2191,7 +2191,7 @@ void __audit_getname(struct filename *name) n->name = name; n->name_len = AUDIT_NAME_FULL; name->aname = n; - refname(name); + name->refcnt++; } static inline int audit_copy_fcaps(struct audit_names *name, @@ -2323,7 +2323,7 @@ out_alloc: return; if (name) { n->name = name; - refname(name); + name->refcnt++; } out: @@ -2445,7 +2445,7 @@ void __audit_inode_child(struct inode *parent, if (found_parent) { found_child->name = found_parent->name; found_child->name_len = AUDIT_NAME_FULL; - refname(found_child->name); + found_child->name->refcnt++; } } -- cgit v1.2.3