Skip to content

Commit 44f6a42

Browse files
thejhtehcaster
authored andcommitted
mm/slub: refactor freelist to use custom type
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@google.com> Co-developed-by: Matteo Rizzo <matteorizzo@google.com> Signed-off-by: Matteo Rizzo <matteorizzo@google.com> Acked-by: David Rientjes <rientjes@google.com> Signed-off-by: Vlastimil Babka <vbabka@suse.cz>
1 parent 06c2afb commit 44f6a42

File tree

1 file changed

+32
-11
lines changed

1 file changed

+32
-11
lines changed

mm/slub.c

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -360,13 +360,19 @@ static struct workqueue_struct *flushwq;
360360
* Core slab cache functions
361361
*******************************************************************/
362362

363+
/*
364+
* freeptr_t represents a SLUB freelist pointer, which might be encoded
365+
* and not dereferenceable if CONFIG_SLAB_FREELIST_HARDENED is enabled.
366+
*/
367+
typedef struct { unsigned long v; } freeptr_t;
368+
363369
/*
364370
* Returns freelist pointer (ptr). With hardening, this is obfuscated
365371
* with an XOR of the address where the pointer is held and a per-cache
366372
* random number.
367373
*/
368-
static inline void *freelist_ptr(const struct kmem_cache *s, void *ptr,
369-
unsigned long ptr_addr)
374+
static inline freeptr_t freelist_ptr_encode(const struct kmem_cache *s,
375+
void *ptr, unsigned long ptr_addr)
370376
{
371377
#ifdef CONFIG_SLAB_FREELIST_HARDENED
372378
/*
@@ -379,25 +385,40 @@ static inline void *freelist_ptr(const struct kmem_cache *s, void *ptr,
379385
* calls get_freepointer() with an untagged pointer, which causes the
380386
* freepointer to be restored incorrectly.
381387
*/
382-
return (void *)((unsigned long)ptr ^ s->random ^
383-
swab((unsigned long)kasan_reset_tag((void *)ptr_addr)));
388+
return (freeptr_t){.v = (unsigned long)ptr ^ s->random ^
389+
swab((unsigned long)kasan_reset_tag((void *)ptr_addr))};
384390
#else
385-
return ptr;
391+
return (freeptr_t){.v = (unsigned long)ptr};
386392
#endif
387393
}
388394

395+
static inline void *freelist_ptr_decode(const struct kmem_cache *s,
396+
freeptr_t ptr, unsigned long ptr_addr)
397+
{
398+
void *decoded;
399+
400+
#ifdef CONFIG_SLAB_FREELIST_HARDENED
401+
/* See the comment in freelist_ptr_encode */
402+
decoded = (void *)(ptr.v ^ s->random ^
403+
swab((unsigned long)kasan_reset_tag((void *)ptr_addr)));
404+
#else
405+
decoded = (void *)ptr.v;
406+
#endif
407+
return decoded;
408+
}
409+
389410
/* Returns the freelist pointer recorded at location ptr_addr. */
390411
static inline void *freelist_dereference(const struct kmem_cache *s,
391412
void *ptr_addr)
392413
{
393-
return freelist_ptr(s, (void *)*(unsigned long *)(ptr_addr),
414+
return freelist_ptr_decode(s, *(freeptr_t *)(ptr_addr),
394415
(unsigned long)ptr_addr);
395416
}
396417

397418
static inline void *get_freepointer(struct kmem_cache *s, void *object)
398419
{
399420
object = kasan_reset_tag(object);
400-
return freelist_dereference(s, object + s->offset);
421+
return freelist_dereference(s, (freeptr_t *)(object + s->offset));
401422
}
402423

403424
#ifndef CONFIG_SLUB_TINY
@@ -421,15 +442,15 @@ __no_kmsan_checks
421442
static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
422443
{
423444
unsigned long freepointer_addr;
424-
void *p;
445+
freeptr_t p;
425446

426447
if (!debug_pagealloc_enabled_static())
427448
return get_freepointer(s, object);
428449

429450
object = kasan_reset_tag(object);
430451
freepointer_addr = (unsigned long)object + s->offset;
431-
copy_from_kernel_nofault(&p, (void **)freepointer_addr, sizeof(p));
432-
return freelist_ptr(s, p, freepointer_addr);
452+
copy_from_kernel_nofault(&p, (freeptr_t *)freepointer_addr, sizeof(p));
453+
return freelist_ptr_decode(s, p, freepointer_addr);
433454
}
434455

435456
static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
@@ -441,7 +462,7 @@ static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
441462
#endif
442463

443464
freeptr_addr = (unsigned long)kasan_reset_tag((void *)freeptr_addr);
444-
*(void **)freeptr_addr = freelist_ptr(s, fp, freeptr_addr);
465+
*(freeptr_t *)freeptr_addr = freelist_ptr_encode(s, fp, freeptr_addr);
445466
}
446467

447468
/* Loop over all objects in a slab */

0 commit comments

Comments
 (0)