A bucket set is created with kmem_cache_create_usercopy(..., align = 0), so calculate_alignment() falls back to arch_slab_minalign(), typically 8 bytes. The general kmalloc caches it stands in for are created through create_boot_cache(), which starts from ARCH_KMALLOC_MINALIGN and raises it to the largest power-of-two divisor of the size: if (flags & SLAB_KMALLOC) align = max(align, 1U << (ffs(size) - 1)); This is only a problem when slab metadata is enabled with CONFIG_KASAN=y, CONFIG_SLUB_DEBUG_ON=y, or "slab_debug=...", because metadata changes the stride size off a power of two, for example: size 128: bucket align=8 size=224 | kmalloc align=128 size=384 size 512: bucket align=8 size=608 | kmalloc align=512 size=1536 size 2048: bucket align=8 size=2144 | kmalloc align=2048 size=6144 So bucket allocations will fail the IS_ALIGNED(p, ARCH_DMA_MINALIGN) check, potentially creating problems for non-coherent DMA situation. As discussed in review, add an alignment argument to kmem_buckets_create(), where kmem_cache_create() has it. A caller that needs a particular alignment passes it. With 0, each cache takes the alignment of the kmalloc cache being mirrored, which is where the size and the name suffix already come from, so a caller moving from kmalloc() keeps the alignment it had. Both existing callers pass 0. Fixes: b32801d1255be ("mm/slab: Introduce kmem_buckets_create() and family") Assisted-by: LLM Signed-off-by: Kees Cook --- include/linux/slab.h | 3 ++- ipc/msgutil.c | 2 +- mm/slab_common.c | 9 +++++++-- mm/util.c | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index cda126def67a..94709e4e14f3 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -890,7 +890,8 @@ void *kmem_cache_alloc_lru_noprof(struct kmem_cache *s, struct list_lru *lru, bool kmem_cache_charge(void *objp, gfp_t gfpflags); void kmem_cache_free(struct kmem_cache *s, void *objp); -kmem_buckets *kmem_buckets_create(const char *name, slab_flags_t flags, +kmem_buckets *kmem_buckets_create(const char *name, unsigned int align, + slab_flags_t flags, unsigned int useroffset, unsigned int usersize, void (*ctor)(void *)); diff --git a/ipc/msgutil.c b/ipc/msgutil.c index e28f0cecb2ec..b49b8f65a582 100644 --- a/ipc/msgutil.c +++ b/ipc/msgutil.c @@ -43,7 +43,7 @@ static kmem_buckets *msg_buckets __ro_after_init; static int __init init_msg_buckets(void) { - msg_buckets = kmem_buckets_create("msg_msg", SLAB_ACCOUNT, + msg_buckets = kmem_buckets_create("msg_msg", 0, SLAB_ACCOUNT, sizeof(struct msg_msg), DATALEN_MSG, NULL); diff --git a/mm/slab_common.c b/mm/slab_common.c index 270408ce5a9d..301f3d4f4ca2 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -415,6 +415,9 @@ static struct kmem_cache *kmem_buckets_cache __ro_after_init; * allocations via kmem_buckets_alloc() * @name: A prefix string which is used in /proc/slabinfo to identify this * cache. The individual caches with have their sizes as the suffix. + * @align: The required alignment for the objects, or 0 to give each cache + * the alignment of the kmalloc cache of the same size, as a caller + * moving from kmalloc() may depend on. * @flags: SLAB flags (see kmem_cache_create() for details). * @useroffset: Starting offset within an allocation that may be copied * to/from userspace. @@ -429,7 +432,8 @@ static struct kmem_cache *kmem_buckets_cache __ro_after_init; * subsequent calls to kmem_buckets_alloc() will fall back to kmalloc(). * (i.e. callers only need to check for NULL on failure.) */ -kmem_buckets *kmem_buckets_create(const char *name, slab_flags_t flags, +kmem_buckets *kmem_buckets_create(const char *name, unsigned int align, + slab_flags_t flags, unsigned int useroffset, unsigned int usersize, void (*ctor)(void *)) @@ -487,7 +491,8 @@ kmem_buckets *kmem_buckets_create(const char *name, slab_flags_t flags, if (WARN_ON(!cache_name)) goto fail; (*b)[aligned_idx] = kmem_cache_create_usercopy(cache_name, size, - 0, flags, cache_useroffset, + align ?: kmalloc_caches[KMALLOC_NORMAL][idx]->align, + flags, cache_useroffset, cache_usersize, ctor); kfree(cache_name); if (WARN_ON(!(*b)[aligned_idx])) diff --git a/mm/util.c b/mm/util.c index bf0513d1d3d0..3f04d77ef4b8 100644 --- a/mm/util.c +++ b/mm/util.c @@ -199,7 +199,7 @@ static kmem_buckets *user_buckets __ro_after_init; static int __init init_user_buckets(void) { - user_buckets = kmem_buckets_create("memdup_user", 0, 0, INT_MAX, NULL); + user_buckets = kmem_buckets_create("memdup_user", 0, 0, 0, INT_MAX, NULL); return 0; } -- 2.34.1