Merge tag 'nfsd-6.1-3' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux
[platform/kernel/linux-starfive.git] / mm / slub.c
index 2a6b3f3..157527d 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -22,6 +22,7 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/kasan.h>
+#include <linux/kmsan.h>
 #include <linux/cpu.h>
 #include <linux/cpuset.h>
 #include <linux/mempolicy.h>
@@ -385,6 +386,17 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)
        prefetchw(object + s->offset);
 }
 
+/*
+ * When running under KMSAN, get_freepointer_safe() may return an uninitialized
+ * pointer value in the case the current thread loses the race for the next
+ * memory chunk in the freelist. In that case this_cpu_cmpxchg_double() in
+ * slab_alloc_node() will fail, so the uninitialized value won't be used, but
+ * KMSAN will still check all arguments of cmpxchg because of imperfect
+ * handling of inline assembly.
+ * To work around this problem, we apply __no_kmsan_checks to ensure that
+ * get_freepointer_safe() returns initialized memory.
+ */
+__no_kmsan_checks
 static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
 {
        unsigned long freepointer_addr;
@@ -1679,6 +1691,7 @@ static __always_inline bool slab_free_hook(struct kmem_cache *s,
                                                void *x, bool init)
 {
        kmemleak_free_recursive(x, s->flags);
+       kmsan_slab_free(s, x);
 
        debug_check_no_locks_freed(x, s->object_size);
 
@@ -1868,7 +1881,7 @@ static bool shuffle_freelist(struct kmem_cache *s, struct slab *slab)
                return false;
 
        freelist_count = oo_objects(s->oo);
-       pos = get_random_int() % freelist_count;
+       pos = prandom_u32_max(freelist_count);
 
        page_limit = slab->objects * s->size;
        start = fixup_red_left(s, slab_address(slab));
@@ -5701,6 +5714,29 @@ STAT_ATTR(CPU_PARTIAL_NODE, cpu_partial_node);
 STAT_ATTR(CPU_PARTIAL_DRAIN, cpu_partial_drain);
 #endif /* CONFIG_SLUB_STATS */
 
+#ifdef CONFIG_KFENCE
+static ssize_t skip_kfence_show(struct kmem_cache *s, char *buf)
+{
+       return sysfs_emit(buf, "%d\n", !!(s->flags & SLAB_SKIP_KFENCE));
+}
+
+static ssize_t skip_kfence_store(struct kmem_cache *s,
+                       const char *buf, size_t length)
+{
+       int ret = length;
+
+       if (buf[0] == '0')
+               s->flags &= ~SLAB_SKIP_KFENCE;
+       else if (buf[0] == '1')
+               s->flags |= SLAB_SKIP_KFENCE;
+       else
+               ret = -EINVAL;
+
+       return ret;
+}
+SLAB_ATTR(skip_kfence);
+#endif
+
 static struct attribute *slab_attrs[] = {
        &slab_size_attr.attr,
        &object_size_attr.attr,
@@ -5768,6 +5804,9 @@ static struct attribute *slab_attrs[] = {
        &failslab_attr.attr,
 #endif
        &usersize_attr.attr,
+#ifdef CONFIG_KFENCE
+       &skip_kfence_attr.attr,
+#endif
 
        NULL
 };
@@ -5870,6 +5909,7 @@ static char *create_unique_id(struct kmem_cache *s)
                kfree(name);
                return ERR_PTR(-EINVAL);
        }
+       kmsan_unpoison_memory(name, p - name);
        return name;
 }
 
@@ -5973,6 +6013,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name)
        al->name = name;
        al->next = alias_list;
        alias_list = al;
+       kmsan_unpoison_memory(al, sizeof(*al));
        return 0;
 }