[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20230703143820.152479-1-matteorizzo@google.com>
Date: Mon, 3 Jul 2023 14:38:20 +0000
From: Matteo Rizzo <matteorizzo@...gle.com>
To: linux-mm@...ck.org, linux-kernel@...r.kernel.org, cl@...ux.com,
penberg@...nel.org, rientjes@...gle.com, iamjoonsoo.kim@....com,
akpm@...ux-foundation.org, vbabka@...e.cz
Cc: roman.gushchin@...ux.dev, 42.hyeyoo@...il.com, jannh@...gle.com,
matteorizzo@...gle.com
Subject: [PATCH] mm/slub: refactor freelist to use custom type
From: Jann Horn <jannh@...gle.com>
Currently the SLUB code represents encoded freelist entries as "void*".
That's misleading, those things are encoded under
CONFIG_SLAB_FREELIST_HARDENED so that they're not actually dereferencable.
Give them their own type, and split freelist_ptr() into one function per
direction (one for encoding, one for decoding).
Signed-off-by: Jann Horn <jannh@...gle.com>
Co-developed-by: Matteo Rizzo <matteorizzo@...gle.com>
Signed-off-by: Matteo Rizzo <matteorizzo@...gle.com>
---
include/linux/slub_def.h | 6 ++++++
mm/slub.c | 37 ++++++++++++++++++++++++++-----------
2 files changed, 32 insertions(+), 11 deletions(-)
diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h
index deb90cf4bffb..c747820a55b4 100644
--- a/include/linux/slub_def.h
+++ b/include/linux/slub_def.h
@@ -43,6 +43,12 @@ enum stat_item {
};
#ifndef CONFIG_SLUB_TINY
+/*
+ * freeptr_t represents a SLUB freelist pointer, which might be encoded
+ * and not dereferenceable if CONFIG_SLAB_FREELIST_HARDENED is enabled.
+ */
+typedef struct { unsigned long v; } freeptr_t;
+
/*
* When changing the layout, make sure freelist and tid are still compatible
* with this_cpu_cmpxchg_double() alignment requirements.
diff --git a/mm/slub.c b/mm/slub.c
index e3b5d5c0eb3a..26d0ca02b61d 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -365,8 +365,8 @@ static struct workqueue_struct *flushwq;
* with an XOR of the address where the pointer is held and a per-cache
* random number.
*/
-static inline void *freelist_ptr(const struct kmem_cache *s, void *ptr,
- unsigned long ptr_addr)
+static inline freeptr_t freelist_ptr_encode(const struct kmem_cache *s,
+ void *ptr, unsigned long ptr_addr)
{
#ifdef CONFIG_SLAB_FREELIST_HARDENED
/*
@@ -379,25 +379,40 @@ static inline void *freelist_ptr(const struct kmem_cache *s, void *ptr,
* calls get_freepointer() with an untagged pointer, which causes the
* freepointer to be restored incorrectly.
*/
- return (void *)((unsigned long)ptr ^ s->random ^
- swab((unsigned long)kasan_reset_tag((void *)ptr_addr)));
+ return (freeptr_t){.v = (unsigned long)ptr ^ s->random ^
+ swab((unsigned long)kasan_reset_tag((void *)ptr_addr))};
#else
- return ptr;
+ return (freeptr_t){.v = (unsigned long)ptr};
#endif
}
+static inline void *freelist_ptr_decode(const struct kmem_cache *s,
+ freeptr_t ptr, unsigned long ptr_addr)
+{
+ void *decoded;
+
+#ifdef CONFIG_SLAB_FREELIST_HARDENED
+ /* See the comment in freelist_ptr_encode */
+ decoded = (void *)(ptr.v ^ s->random ^
+ swab((unsigned long)kasan_reset_tag((void *)ptr_addr)));
+#else
+ decoded = (void *)ptr.v;
+#endif
+ return decoded;
+}
+
/* Returns the freelist pointer recorded at location ptr_addr. */
static inline void *freelist_dereference(const struct kmem_cache *s,
void *ptr_addr)
{
- return freelist_ptr(s, (void *)*(unsigned long *)(ptr_addr),
+ return freelist_ptr_decode(s, *(freeptr_t *)(ptr_addr),
(unsigned long)ptr_addr);
}
static inline void *get_freepointer(struct kmem_cache *s, void *object)
{
object = kasan_reset_tag(object);
- return freelist_dereference(s, object + s->offset);
+ return freelist_dereference(s, (freeptr_t *)(object + s->offset));
}
#ifndef CONFIG_SLUB_TINY
@@ -421,15 +436,15 @@ __no_kmsan_checks
static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
{
unsigned long freepointer_addr;
- void *p;
+ freeptr_t p;
if (!debug_pagealloc_enabled_static())
return get_freepointer(s, object);
object = kasan_reset_tag(object);
freepointer_addr = (unsigned long)object + s->offset;
- copy_from_kernel_nofault(&p, (void **)freepointer_addr, sizeof(p));
- return freelist_ptr(s, p, freepointer_addr);
+ copy_from_kernel_nofault(&p, (freeptr_t *)freepointer_addr, sizeof(p));
+ return freelist_ptr_decode(s, p, freepointer_addr);
}
static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
@@ -441,7 +456,7 @@ static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
#endif
freeptr_addr = (unsigned long)kasan_reset_tag((void *)freeptr_addr);
- *(void **)freeptr_addr = freelist_ptr(s, fp, freeptr_addr);
+ *(freeptr_t *)freeptr_addr = freelist_ptr_encode(s, fp, freeptr_addr);
}
/* Loop over all objects in a slab */
base-commit: a901a3568fd26ca9c4a82d8bc5ed5b3ed844d451
--
2.41.0.255.g8b1d071c50-goog
Powered by blists - more mailing lists