xref: /linux/lib/slub_kunit.c (revision 2da68a77)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <kunit/test.h>
3 #include <linux/mm.h>
4 #include <linux/slab.h>
5 #include <linux/module.h>
6 #include <linux/kernel.h>
7 #include "../mm/slab.h"
8 
9 static struct kunit_resource resource;
10 static int slab_errors;
11 
12 /*
13  * Wrapper function for kmem_cache_create(), which reduces 2 parameters:
14  * 'align' and 'ctor', and sets SLAB_SKIP_KFENCE flag to avoid getting an
15  * object from kfence pool, where the operation could be caught by both
16  * our test and kfence sanity check.
17  */
18 static struct kmem_cache *test_kmem_cache_create(const char *name,
19 				unsigned int size, slab_flags_t flags)
20 {
21 	struct kmem_cache *s = kmem_cache_create(name, size, 0,
22 					(flags | SLAB_NO_USER_FLAGS), NULL);
23 	s->flags |= SLAB_SKIP_KFENCE;
24 	return s;
25 }
26 
27 static void test_clobber_zone(struct kunit *test)
28 {
29 	struct kmem_cache *s = test_kmem_cache_create("TestSlub_RZ_alloc", 64,
30 							SLAB_RED_ZONE);
31 	u8 *p = kmem_cache_alloc(s, GFP_KERNEL);
32 
33 	kasan_disable_current();
34 	p[64] = 0x12;
35 
36 	validate_slab_cache(s);
37 	KUNIT_EXPECT_EQ(test, 2, slab_errors);
38 
39 	kasan_enable_current();
40 	kmem_cache_free(s, p);
41 	kmem_cache_destroy(s);
42 }
43 
44 #ifndef CONFIG_KASAN
45 static void test_next_pointer(struct kunit *test)
46 {
47 	struct kmem_cache *s = test_kmem_cache_create("TestSlub_next_ptr_free",
48 							64, SLAB_POISON);
49 	u8 *p = kmem_cache_alloc(s, GFP_KERNEL);
50 	unsigned long tmp;
51 	unsigned long *ptr_addr;
52 
53 	kmem_cache_free(s, p);
54 
55 	ptr_addr = (unsigned long *)(p + s->offset);
56 	tmp = *ptr_addr;
57 	p[s->offset] = 0x12;
58 
59 	/*
60 	 * Expecting three errors.
61 	 * One for the corrupted freechain and the other one for the wrong
62 	 * count of objects in use. The third error is fixing broken cache.
63 	 */
64 	validate_slab_cache(s);
65 	KUNIT_EXPECT_EQ(test, 3, slab_errors);
66 
67 	/*
68 	 * Try to repair corrupted freepointer.
69 	 * Still expecting two errors. The first for the wrong count
70 	 * of objects in use.
71 	 * The second error is for fixing broken cache.
72 	 */
73 	*ptr_addr = tmp;
74 	slab_errors = 0;
75 
76 	validate_slab_cache(s);
77 	KUNIT_EXPECT_EQ(test, 2, slab_errors);
78 
79 	/*
80 	 * Previous validation repaired the count of objects in use.
81 	 * Now expecting no error.
82 	 */
83 	slab_errors = 0;
84 	validate_slab_cache(s);
85 	KUNIT_EXPECT_EQ(test, 0, slab_errors);
86 
87 	kmem_cache_destroy(s);
88 }
89 
90 static void test_first_word(struct kunit *test)
91 {
92 	struct kmem_cache *s = test_kmem_cache_create("TestSlub_1th_word_free",
93 							64, SLAB_POISON);
94 	u8 *p = kmem_cache_alloc(s, GFP_KERNEL);
95 
96 	kmem_cache_free(s, p);
97 	*p = 0x78;
98 
99 	validate_slab_cache(s);
100 	KUNIT_EXPECT_EQ(test, 2, slab_errors);
101 
102 	kmem_cache_destroy(s);
103 }
104 
105 static void test_clobber_50th_byte(struct kunit *test)
106 {
107 	struct kmem_cache *s = test_kmem_cache_create("TestSlub_50th_word_free",
108 							64, SLAB_POISON);
109 	u8 *p = kmem_cache_alloc(s, GFP_KERNEL);
110 
111 	kmem_cache_free(s, p);
112 	p[50] = 0x9a;
113 
114 	validate_slab_cache(s);
115 	KUNIT_EXPECT_EQ(test, 2, slab_errors);
116 
117 	kmem_cache_destroy(s);
118 }
119 #endif
120 
121 static void test_clobber_redzone_free(struct kunit *test)
122 {
123 	struct kmem_cache *s = test_kmem_cache_create("TestSlub_RZ_free", 64,
124 							SLAB_RED_ZONE);
125 	u8 *p = kmem_cache_alloc(s, GFP_KERNEL);
126 
127 	kasan_disable_current();
128 	kmem_cache_free(s, p);
129 	p[64] = 0xab;
130 
131 	validate_slab_cache(s);
132 	KUNIT_EXPECT_EQ(test, 2, slab_errors);
133 
134 	kasan_enable_current();
135 	kmem_cache_destroy(s);
136 }
137 
138 static void test_kmalloc_redzone_access(struct kunit *test)
139 {
140 	struct kmem_cache *s = test_kmem_cache_create("TestSlub_RZ_kmalloc", 32,
141 				SLAB_KMALLOC|SLAB_STORE_USER|SLAB_RED_ZONE);
142 	u8 *p = kmalloc_trace(s, GFP_KERNEL, 18);
143 
144 	kasan_disable_current();
145 
146 	/* Suppress the -Warray-bounds warning */
147 	OPTIMIZER_HIDE_VAR(p);
148 	p[18] = 0xab;
149 	p[19] = 0xab;
150 
151 	validate_slab_cache(s);
152 	KUNIT_EXPECT_EQ(test, 2, slab_errors);
153 
154 	kasan_enable_current();
155 	kmem_cache_free(s, p);
156 	kmem_cache_destroy(s);
157 }
158 
159 static int test_init(struct kunit *test)
160 {
161 	slab_errors = 0;
162 
163 	kunit_add_named_resource(test, NULL, NULL, &resource,
164 					"slab_errors", &slab_errors);
165 	return 0;
166 }
167 
168 static struct kunit_case test_cases[] = {
169 	KUNIT_CASE(test_clobber_zone),
170 
171 #ifndef CONFIG_KASAN
172 	KUNIT_CASE(test_next_pointer),
173 	KUNIT_CASE(test_first_word),
174 	KUNIT_CASE(test_clobber_50th_byte),
175 #endif
176 
177 	KUNIT_CASE(test_clobber_redzone_free),
178 	KUNIT_CASE(test_kmalloc_redzone_access),
179 	{}
180 };
181 
182 static struct kunit_suite test_suite = {
183 	.name = "slub_test",
184 	.init = test_init,
185 	.test_cases = test_cases,
186 };
187 kunit_test_suite(test_suite);
188 
189 MODULE_LICENSE("GPL");
190