[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAG48ez1GYR+6kZHDmy4CTZvEfdyUySCxhZaXRo1S=YyN=Fsp8Q@mail.gmail.com>
Date: Wed, 11 Feb 2026 02:28:53 +0100
From: Jann Horn <jannh@...gle.com>
To: Kees Cook <kees@...nel.org>
Cc: Al Viro <viro@...iv.linux.org.uk>, Christian Brauner <brauner@...nel.org>, Jan Kara <jack@...e.cz>,
linux-fsdevel@...r.kernel.org, linux-kernel@...r.kernel.org,
linux-hardening@...r.kernel.org
Subject: Re: [PATCH] fs: Keep long filenames in isolated slab buckets
On Wed, Feb 11, 2026 at 1:48 AM Kees Cook <kees@...nel.org> wrote:
> A building block of Use-After-Free heap memory corruption attacks is
> using userspace controllable kernel allocations to fill specifically sized
> kmalloc regions with specific contents. The most powerful of these kinds
> of primitives is arbitrarily controllable contents with arbitrary size.
> Keeping these kinds of allocations out of the general kmalloc buckets is
> needed to harden the kernel against such manipulations, so this is why
> these sorts of "copy data from userspace into kernel heap" situations are
> expected to use things like memdup_user(), which keeps the allocations
> in their own set of slab buckets. However, using memdup_user() is not
> always appropriate, so in those cases, kmem_buckets can used directly.
>
> Filenames used to be isolated in their own (fixed size) slab cache so
> they would not end up in the general kmalloc buckets (which made them
> unusable for the heap grooming method described above). After commit
> 8c888b31903c ("struct filename: saner handling of long names"), filenames
> were being copied into arbitrarily sized kmalloc regions in the general
> kmalloc buckets. Instead, like memdup_user(), use a dedicated set of
> kmem buckets for long filenames so we do not introduce a new way for
> attackers to place arbitrary contents into the general kmalloc buckets.
>
> Fixes: 8c888b31903c ("struct filename: saner handling of long names")
> Signed-off-by: Kees Cook <kees@...nel.org>
> ---
> Also, from the same commit, is the loss of SLAB_HWCACHE_ALIGN|SLAB_PANIC
> for filename allocations relavant at all? It could be added back for
> these buckets if desired, but I left it default in this patch.
>
> Cc: Al Viro <viro@...iv.linux.org.uk>
> Cc: Christian Brauner <brauner@...nel.org>
> Cc: Jan Kara <jack@...e.cz>
> Cc: <linux-fsdevel@...r.kernel.org>
> ---
> fs/namei.c | 10 +++++++---
> 1 file changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index 8e7792de0000..a901733380cd 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -128,6 +128,8 @@
> /* SLAB cache for struct filename instances */
> static struct kmem_cache *__names_cache __ro_after_init;
> #define names_cache runtime_const_ptr(__names_cache)
> +/* SLAB buckets for long names */
> +static kmem_buckets *names_buckets __ro_after_init;
>
> void __init filename_init(void)
> {
> @@ -135,6 +137,8 @@ void __init filename_init(void)
> SLAB_HWCACHE_ALIGN|SLAB_PANIC, offsetof(struct filename, iname),
> EMBEDDED_NAME_MAX, NULL);
> runtime_const_init(ptr, __names_cache);
> +
> + names_buckets = kmem_buckets_create("names_bucket", 0, 0, PATH_MAX, NULL);
> }
>
> static inline struct filename *alloc_filename(void)
> @@ -156,7 +160,7 @@ static inline void initname(struct filename *name)
> static int getname_long(struct filename *name, const char __user *filename)
> {
> int len;
> - char *p __free(kfree) = kmalloc(PATH_MAX, GFP_KERNEL);
> + char *p __free(kfree) = kmem_buckets_alloc(names_buckets, PATH_MAX, GFP_KERNEL);
> if (unlikely(!p))
> return -ENOMEM;
I think this path, where we always do maximally-sized allocations, is
the normal case where we're handling paths coming from userspace...
> @@ -264,14 +268,14 @@ static struct filename *do_getname_kernel(const char *filename, bool incomplete)
>
> if (len <= EMBEDDED_NAME_MAX) {
> p = (char *)result->iname;
> - memcpy(p, filename, len);
> } else {
> - p = kmemdup(filename, len, GFP_KERNEL);
> + p = kmem_buckets_alloc(names_buckets, len, GFP_KERNEL);
... while this is kind of the exceptional case, where paths are coming
from kernelspace.
So you might want to get rid of the bucketing and instead just create
a single kmem_cache for long paths.
By the way, did you know that "struct filename" is only used for
storing fairly-temporary stuff like paths supplied to open(), but not
for storing the names of directory entries in the dentry cache (which
are more long-lived)? My understanding is that this is also why the
kernel doesn't really try to optimize the size of "struct filename" -
almost all instances of it only exist for the duration of a syscall or
something like that.
The dentry cache allocates long names as "struct external_name" in
reclaimable kmalloc slabs, see __d_alloc().
Powered by blists - more mailing lists