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