|
@@ -34,6 +34,7 @@
|
|
|
#include <linux/stacktrace.h>
|
|
|
#include <linux/prefetch.h>
|
|
|
#include <linux/memcontrol.h>
|
|
|
+#include <linux/random.h>
|
|
|
|
|
|
#include <trace/events/kmem.h>
|
|
|
|
|
@@ -238,30 +239,58 @@ static inline void stat(const struct kmem_cache *s, enum stat_item si)
|
|
|
* Core slab cache functions
|
|
|
*******************************************************************/
|
|
|
|
|
|
+/*
|
|
|
+ * Returns freelist pointer (ptr). With hardening, this is obfuscated
|
|
|
+ * 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)
|
|
|
+{
|
|
|
+#ifdef CONFIG_SLAB_FREELIST_HARDENED
|
|
|
+ return (void *)((unsigned long)ptr ^ s->random ^ ptr_addr);
|
|
|
+#else
|
|
|
+ return ptr;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+/* 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),
|
|
|
+ (unsigned long)ptr_addr);
|
|
|
+}
|
|
|
+
|
|
|
static inline void *get_freepointer(struct kmem_cache *s, void *object)
|
|
|
{
|
|
|
- return *(void **)(object + s->offset);
|
|
|
+ return freelist_dereference(s, object + s->offset);
|
|
|
}
|
|
|
|
|
|
static void prefetch_freepointer(const struct kmem_cache *s, void *object)
|
|
|
{
|
|
|
- prefetch(object + s->offset);
|
|
|
+ if (object)
|
|
|
+ prefetch(freelist_dereference(s, object + s->offset));
|
|
|
}
|
|
|
|
|
|
static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
|
|
|
{
|
|
|
+ unsigned long freepointer_addr;
|
|
|
void *p;
|
|
|
|
|
|
if (!debug_pagealloc_enabled())
|
|
|
return get_freepointer(s, object);
|
|
|
|
|
|
- probe_kernel_read(&p, (void **)(object + s->offset), sizeof(p));
|
|
|
- return p;
|
|
|
+ freepointer_addr = (unsigned long)object + s->offset;
|
|
|
+ probe_kernel_read(&p, (void **)freepointer_addr, sizeof(p));
|
|
|
+ return freelist_ptr(s, p, freepointer_addr);
|
|
|
}
|
|
|
|
|
|
static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
|
|
|
{
|
|
|
- *(void **)(object + s->offset) = fp;
|
|
|
+ unsigned long freeptr_addr = (unsigned long)object + s->offset;
|
|
|
+
|
|
|
+ *(void **)freeptr_addr = freelist_ptr(s, fp, freeptr_addr);
|
|
|
}
|
|
|
|
|
|
/* Loop over all objects in a slab */
|
|
@@ -3563,6 +3592,9 @@ static int kmem_cache_open(struct kmem_cache *s, unsigned long flags)
|
|
|
{
|
|
|
s->flags = kmem_cache_flags(s->size, flags, s->name, s->ctor);
|
|
|
s->reserved = 0;
|
|
|
+#ifdef CONFIG_SLAB_FREELIST_HARDENED
|
|
|
+ s->random = get_random_long();
|
|
|
+#endif
|
|
|
|
|
|
if (need_reserve_slab_rcu && (s->flags & SLAB_TYPESAFE_BY_RCU))
|
|
|
s->reserved = sizeof(struct rcu_head);
|