@@ -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. */
390411static 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
397418static 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
421442static 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
435456static 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