|
34 | 34 | #include <linux/stacktrace.h> |
35 | 35 | #include <linux/prefetch.h> |
36 | 36 | #include <linux/memcontrol.h> |
| 37 | +#include <linux/random.h> |
37 | 38 |
|
38 | 39 | #include <trace/events/kmem.h> |
39 | 40 |
|
@@ -238,30 +239,58 @@ static inline void stat(const struct kmem_cache *s, enum stat_item si) |
238 | 239 | * Core slab cache functions |
239 | 240 | *******************************************************************/ |
240 | 241 |
|
| 242 | +/* |
| 243 | + * Returns freelist pointer (ptr). With hardening, this is obfuscated |
| 244 | + * with an XOR of the address where the pointer is held and a per-cache |
| 245 | + * random number. |
| 246 | + */ |
| 247 | +static inline void *freelist_ptr(const struct kmem_cache *s, void *ptr, |
| 248 | + unsigned long ptr_addr) |
| 249 | +{ |
| 250 | +#ifdef CONFIG_SLAB_FREELIST_HARDENED |
| 251 | + return (void *)((unsigned long)ptr ^ s->random ^ ptr_addr); |
| 252 | +#else |
| 253 | + return ptr; |
| 254 | +#endif |
| 255 | +} |
| 256 | + |
| 257 | +/* Returns the freelist pointer recorded at location ptr_addr. */ |
| 258 | +static inline void *freelist_dereference(const struct kmem_cache *s, |
| 259 | + void *ptr_addr) |
| 260 | +{ |
| 261 | + return freelist_ptr(s, (void *)*(unsigned long *)(ptr_addr), |
| 262 | + (unsigned long)ptr_addr); |
| 263 | +} |
| 264 | + |
241 | 265 | static inline void *get_freepointer(struct kmem_cache *s, void *object) |
242 | 266 | { |
243 | | - return *(void **)(object + s->offset); |
| 267 | + return freelist_dereference(s, object + s->offset); |
244 | 268 | } |
245 | 269 |
|
246 | 270 | static void prefetch_freepointer(const struct kmem_cache *s, void *object) |
247 | 271 | { |
248 | | - prefetch(object + s->offset); |
| 272 | + if (object) |
| 273 | + prefetch(freelist_dereference(s, object + s->offset)); |
249 | 274 | } |
250 | 275 |
|
251 | 276 | static inline void *get_freepointer_safe(struct kmem_cache *s, void *object) |
252 | 277 | { |
| 278 | + unsigned long freepointer_addr; |
253 | 279 | void *p; |
254 | 280 |
|
255 | 281 | if (!debug_pagealloc_enabled()) |
256 | 282 | return get_freepointer(s, object); |
257 | 283 |
|
258 | | - probe_kernel_read(&p, (void **)(object + s->offset), sizeof(p)); |
259 | | - return p; |
| 284 | + freepointer_addr = (unsigned long)object + s->offset; |
| 285 | + probe_kernel_read(&p, (void **)freepointer_addr, sizeof(p)); |
| 286 | + return freelist_ptr(s, p, freepointer_addr); |
260 | 287 | } |
261 | 288 |
|
262 | 289 | static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp) |
263 | 290 | { |
264 | | - *(void **)(object + s->offset) = fp; |
| 291 | + unsigned long freeptr_addr = (unsigned long)object + s->offset; |
| 292 | + |
| 293 | + *(void **)freeptr_addr = freelist_ptr(s, fp, freeptr_addr); |
265 | 294 | } |
266 | 295 |
|
267 | 296 | /* Loop over all objects in a slab */ |
@@ -3563,6 +3592,9 @@ static int kmem_cache_open(struct kmem_cache *s, unsigned long flags) |
3563 | 3592 | { |
3564 | 3593 | s->flags = kmem_cache_flags(s->size, flags, s->name, s->ctor); |
3565 | 3594 | s->reserved = 0; |
| 3595 | +#ifdef CONFIG_SLAB_FREELIST_HARDENED |
| 3596 | + s->random = get_random_long(); |
| 3597 | +#endif |
3566 | 3598 |
|
3567 | 3599 | if (need_reserve_slab_rcu && (s->flags & SLAB_TYPESAFE_BY_RCU)) |
3568 | 3600 | s->reserved = sizeof(struct rcu_head); |
|
0 commit comments