xref: /openbmc/linux/lib/memcpy_kunit.c (revision 3c3d394b)
1bb95ebbeSKees Cook // SPDX-License-Identifier: GPL-2.0
2bb95ebbeSKees Cook /*
3bb95ebbeSKees Cook  * Test cases for memcpy(), memmove(), and memset().
4bb95ebbeSKees Cook  */
5bb95ebbeSKees Cook #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
6bb95ebbeSKees Cook 
7bb95ebbeSKees Cook #include <kunit/test.h>
8bb95ebbeSKees Cook #include <linux/device.h>
9bb95ebbeSKees Cook #include <linux/init.h>
10bb95ebbeSKees Cook #include <linux/kernel.h>
11bb95ebbeSKees Cook #include <linux/mm.h>
12bb95ebbeSKees Cook #include <linux/module.h>
13bb95ebbeSKees Cook #include <linux/overflow.h>
14bb95ebbeSKees Cook #include <linux/slab.h>
15bb95ebbeSKees Cook #include <linux/types.h>
16bb95ebbeSKees Cook #include <linux/vmalloc.h>
17bb95ebbeSKees Cook 
18bb95ebbeSKees Cook struct some_bytes {
19bb95ebbeSKees Cook 	union {
20bb95ebbeSKees Cook 		u8 data[32];
21bb95ebbeSKees Cook 		struct {
22bb95ebbeSKees Cook 			u32 one;
23bb95ebbeSKees Cook 			u16 two;
24bb95ebbeSKees Cook 			u8  three;
25bb95ebbeSKees Cook 			/* 1 byte hole */
26bb95ebbeSKees Cook 			u32 four[4];
27bb95ebbeSKees Cook 		};
28bb95ebbeSKees Cook 	};
29bb95ebbeSKees Cook };
30bb95ebbeSKees Cook 
31bb95ebbeSKees Cook #define check(instance, v) do {	\
32bb95ebbeSKees Cook 	BUILD_BUG_ON(sizeof(instance.data) != 32);	\
33dfbafa70SKees Cook 	for (size_t i = 0; i < sizeof(instance.data); i++) {	\
34bb95ebbeSKees Cook 		KUNIT_ASSERT_EQ_MSG(test, instance.data[i], v, \
35*3c3d394bSDavid Gow 			"line %d: '%s' not initialized to 0x%02x @ %zu (saw 0x%02x)\n", \
36bb95ebbeSKees Cook 			__LINE__, #instance, v, i, instance.data[i]);	\
37bb95ebbeSKees Cook 	}	\
38bb95ebbeSKees Cook } while (0)
39bb95ebbeSKees Cook 
40bb95ebbeSKees Cook #define compare(name, one, two) do { \
41bb95ebbeSKees Cook 	BUILD_BUG_ON(sizeof(one) != sizeof(two)); \
42dfbafa70SKees Cook 	for (size_t i = 0; i < sizeof(one); i++) {	\
43bb95ebbeSKees Cook 		KUNIT_EXPECT_EQ_MSG(test, one.data[i], two.data[i], \
44*3c3d394bSDavid Gow 			"line %d: %s.data[%zu] (0x%02x) != %s.data[%zu] (0x%02x)\n", \
45bb95ebbeSKees Cook 			__LINE__, #one, i, one.data[i], #two, i, two.data[i]); \
46bb95ebbeSKees Cook 	}	\
47bb95ebbeSKees Cook 	kunit_info(test, "ok: " TEST_OP "() " name "\n");	\
48bb95ebbeSKees Cook } while (0)
49bb95ebbeSKees Cook 
memcpy_test(struct kunit * test)50bb95ebbeSKees Cook static void memcpy_test(struct kunit *test)
51bb95ebbeSKees Cook {
52bb95ebbeSKees Cook #define TEST_OP "memcpy"
53bb95ebbeSKees Cook 	struct some_bytes control = {
54bb95ebbeSKees Cook 		.data = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
55bb95ebbeSKees Cook 			  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
56bb95ebbeSKees Cook 			  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
57bb95ebbeSKees Cook 			  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
58bb95ebbeSKees Cook 			},
59bb95ebbeSKees Cook 	};
60bb95ebbeSKees Cook 	struct some_bytes zero = { };
61bb95ebbeSKees Cook 	struct some_bytes middle = {
62bb95ebbeSKees Cook 		.data = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
63bb95ebbeSKees Cook 			  0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00,
64bb95ebbeSKees Cook 			  0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20,
65bb95ebbeSKees Cook 			  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
66bb95ebbeSKees Cook 			},
67bb95ebbeSKees Cook 	};
68bb95ebbeSKees Cook 	struct some_bytes three = {
69bb95ebbeSKees Cook 		.data = { 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
70bb95ebbeSKees Cook 			  0x20, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20,
71bb95ebbeSKees Cook 			  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
72bb95ebbeSKees Cook 			  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
73bb95ebbeSKees Cook 			},
74bb95ebbeSKees Cook 	};
75bb95ebbeSKees Cook 	struct some_bytes dest = { };
76bb95ebbeSKees Cook 	int count;
77bb95ebbeSKees Cook 	u8 *ptr;
78bb95ebbeSKees Cook 
79bb95ebbeSKees Cook 	/* Verify static initializers. */
80bb95ebbeSKees Cook 	check(control, 0x20);
81bb95ebbeSKees Cook 	check(zero, 0);
82bb95ebbeSKees Cook 	compare("static initializers", dest, zero);
83bb95ebbeSKees Cook 
84bb95ebbeSKees Cook 	/* Verify assignment. */
85bb95ebbeSKees Cook 	dest = control;
86bb95ebbeSKees Cook 	compare("direct assignment", dest, control);
87bb95ebbeSKees Cook 
88bb95ebbeSKees Cook 	/* Verify complete overwrite. */
89bb95ebbeSKees Cook 	memcpy(dest.data, zero.data, sizeof(dest.data));
90bb95ebbeSKees Cook 	compare("complete overwrite", dest, zero);
91bb95ebbeSKees Cook 
92bb95ebbeSKees Cook 	/* Verify middle overwrite. */
93bb95ebbeSKees Cook 	dest = control;
94bb95ebbeSKees Cook 	memcpy(dest.data + 12, zero.data, 7);
95bb95ebbeSKees Cook 	compare("middle overwrite", dest, middle);
96bb95ebbeSKees Cook 
97bb95ebbeSKees Cook 	/* Verify argument side-effects aren't repeated. */
98bb95ebbeSKees Cook 	dest = control;
99bb95ebbeSKees Cook 	ptr = dest.data;
100bb95ebbeSKees Cook 	count = 1;
101bb95ebbeSKees Cook 	memcpy(ptr++, zero.data, count++);
102bb95ebbeSKees Cook 	ptr += 8;
103bb95ebbeSKees Cook 	memcpy(ptr++, zero.data, count++);
104bb95ebbeSKees Cook 	compare("argument side-effects", dest, three);
105bb95ebbeSKees Cook #undef TEST_OP
106bb95ebbeSKees Cook }
107bb95ebbeSKees Cook 
108bce5a1e8SNick Desaulniers static unsigned char larger_array [2048];
109bce5a1e8SNick Desaulniers 
memmove_test(struct kunit * test)110bb95ebbeSKees Cook static void memmove_test(struct kunit *test)
111bb95ebbeSKees Cook {
112bb95ebbeSKees Cook #define TEST_OP "memmove"
113bb95ebbeSKees Cook 	struct some_bytes control = {
114bb95ebbeSKees Cook 		.data = { 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
115bb95ebbeSKees Cook 			  0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
116bb95ebbeSKees Cook 			  0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
117bb95ebbeSKees Cook 			  0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
118bb95ebbeSKees Cook 			},
119bb95ebbeSKees Cook 	};
120bb95ebbeSKees Cook 	struct some_bytes zero = { };
121bb95ebbeSKees Cook 	struct some_bytes middle = {
122bb95ebbeSKees Cook 		.data = { 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
123bb95ebbeSKees Cook 			  0x99, 0x99, 0x99, 0x99, 0x00, 0x00, 0x00, 0x00,
124bb95ebbeSKees Cook 			  0x00, 0x00, 0x00, 0x99, 0x99, 0x99, 0x99, 0x99,
125bb95ebbeSKees Cook 			  0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
126bb95ebbeSKees Cook 			},
127bb95ebbeSKees Cook 	};
128bb95ebbeSKees Cook 	struct some_bytes five = {
129bb95ebbeSKees Cook 		.data = { 0x00, 0x00, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
130bb95ebbeSKees Cook 			  0x99, 0x99, 0x00, 0x00, 0x00, 0x99, 0x99, 0x99,
131bb95ebbeSKees Cook 			  0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
132bb95ebbeSKees Cook 			  0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
133bb95ebbeSKees Cook 			},
134bb95ebbeSKees Cook 	};
135bb95ebbeSKees Cook 	struct some_bytes overlap = {
136bb95ebbeSKees Cook 		.data = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
137bb95ebbeSKees Cook 			  0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
138bb95ebbeSKees Cook 			  0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
139bb95ebbeSKees Cook 			  0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
140bb95ebbeSKees Cook 			},
141bb95ebbeSKees Cook 	};
142bb95ebbeSKees Cook 	struct some_bytes overlap_expected = {
143bb95ebbeSKees Cook 		.data = { 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x07,
144bb95ebbeSKees Cook 			  0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
145bb95ebbeSKees Cook 			  0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
146bb95ebbeSKees Cook 			  0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
147bb95ebbeSKees Cook 			},
148bb95ebbeSKees Cook 	};
149bb95ebbeSKees Cook 	struct some_bytes dest = { };
150bb95ebbeSKees Cook 	int count;
151bb95ebbeSKees Cook 	u8 *ptr;
152bb95ebbeSKees Cook 
153bb95ebbeSKees Cook 	/* Verify static initializers. */
154bb95ebbeSKees Cook 	check(control, 0x99);
155bb95ebbeSKees Cook 	check(zero, 0);
156bb95ebbeSKees Cook 	compare("static initializers", zero, dest);
157bb95ebbeSKees Cook 
158bb95ebbeSKees Cook 	/* Verify assignment. */
159bb95ebbeSKees Cook 	dest = control;
160bb95ebbeSKees Cook 	compare("direct assignment", dest, control);
161bb95ebbeSKees Cook 
162bb95ebbeSKees Cook 	/* Verify complete overwrite. */
163bb95ebbeSKees Cook 	memmove(dest.data, zero.data, sizeof(dest.data));
164bb95ebbeSKees Cook 	compare("complete overwrite", dest, zero);
165bb95ebbeSKees Cook 
166bb95ebbeSKees Cook 	/* Verify middle overwrite. */
167bb95ebbeSKees Cook 	dest = control;
168bb95ebbeSKees Cook 	memmove(dest.data + 12, zero.data, 7);
169bb95ebbeSKees Cook 	compare("middle overwrite", dest, middle);
170bb95ebbeSKees Cook 
171bb95ebbeSKees Cook 	/* Verify argument side-effects aren't repeated. */
172bb95ebbeSKees Cook 	dest = control;
173bb95ebbeSKees Cook 	ptr = dest.data;
174bb95ebbeSKees Cook 	count = 2;
175bb95ebbeSKees Cook 	memmove(ptr++, zero.data, count++);
176bb95ebbeSKees Cook 	ptr += 9;
177bb95ebbeSKees Cook 	memmove(ptr++, zero.data, count++);
178bb95ebbeSKees Cook 	compare("argument side-effects", dest, five);
179bb95ebbeSKees Cook 
180bb95ebbeSKees Cook 	/* Verify overlapping overwrite is correct. */
181bb95ebbeSKees Cook 	ptr = &overlap.data[2];
182bb95ebbeSKees Cook 	memmove(ptr, overlap.data, 5);
183bb95ebbeSKees Cook 	compare("overlapping write", overlap, overlap_expected);
184bce5a1e8SNick Desaulniers 
185bce5a1e8SNick Desaulniers 	/* Verify larger overlapping moves. */
186bce5a1e8SNick Desaulniers 	larger_array[256] = 0xAAu;
187bce5a1e8SNick Desaulniers 	/*
188bce5a1e8SNick Desaulniers 	 * Test a backwards overlapping memmove first. 256 and 1024 are
189bce5a1e8SNick Desaulniers 	 * important for i386 to use rep movsl.
190bce5a1e8SNick Desaulniers 	 */
191bce5a1e8SNick Desaulniers 	memmove(larger_array, larger_array + 256, 1024);
192bce5a1e8SNick Desaulniers 	KUNIT_ASSERT_EQ(test, larger_array[0], 0xAAu);
193bce5a1e8SNick Desaulniers 	KUNIT_ASSERT_EQ(test, larger_array[256], 0x00);
194bce5a1e8SNick Desaulniers 	KUNIT_ASSERT_NULL(test,
195bce5a1e8SNick Desaulniers 		memchr(larger_array + 1, 0xaa, ARRAY_SIZE(larger_array) - 1));
196bce5a1e8SNick Desaulniers 	/* Test a forwards overlapping memmove. */
197bce5a1e8SNick Desaulniers 	larger_array[0] = 0xBBu;
198bce5a1e8SNick Desaulniers 	memmove(larger_array + 256, larger_array, 1024);
199bce5a1e8SNick Desaulniers 	KUNIT_ASSERT_EQ(test, larger_array[0], 0xBBu);
200bce5a1e8SNick Desaulniers 	KUNIT_ASSERT_EQ(test, larger_array[256], 0xBBu);
201bce5a1e8SNick Desaulniers 	KUNIT_ASSERT_NULL(test, memchr(larger_array + 1, 0xBBu, 256 - 1));
202bce5a1e8SNick Desaulniers 	KUNIT_ASSERT_NULL(test,
203bce5a1e8SNick Desaulniers 		memchr(larger_array + 257, 0xBBu, ARRAY_SIZE(larger_array) - 257));
204bb95ebbeSKees Cook #undef TEST_OP
205bb95ebbeSKees Cook }
206bb95ebbeSKees Cook 
memset_test(struct kunit * test)207bb95ebbeSKees Cook static void memset_test(struct kunit *test)
208bb95ebbeSKees Cook {
209bb95ebbeSKees Cook #define TEST_OP "memset"
210bb95ebbeSKees Cook 	struct some_bytes control = {
211bb95ebbeSKees Cook 		.data = { 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
212bb95ebbeSKees Cook 			  0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
213bb95ebbeSKees Cook 			  0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
214bb95ebbeSKees Cook 			  0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
215bb95ebbeSKees Cook 			},
216bb95ebbeSKees Cook 	};
217bb95ebbeSKees Cook 	struct some_bytes complete = {
218bb95ebbeSKees Cook 		.data = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
219bb95ebbeSKees Cook 			  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
220bb95ebbeSKees Cook 			  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
221bb95ebbeSKees Cook 			  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
222bb95ebbeSKees Cook 			},
223bb95ebbeSKees Cook 	};
224bb95ebbeSKees Cook 	struct some_bytes middle = {
225bb95ebbeSKees Cook 		.data = { 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31,
226bb95ebbeSKees Cook 			  0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
227bb95ebbeSKees Cook 			  0x31, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, 0x30,
228bb95ebbeSKees Cook 			  0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
229bb95ebbeSKees Cook 			},
230bb95ebbeSKees Cook 	};
231bb95ebbeSKees Cook 	struct some_bytes three = {
232bb95ebbeSKees Cook 		.data = { 0x60, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
233bb95ebbeSKees Cook 			  0x30, 0x61, 0x61, 0x30, 0x30, 0x30, 0x30, 0x30,
234bb95ebbeSKees Cook 			  0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
235bb95ebbeSKees Cook 			  0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
236bb95ebbeSKees Cook 			},
237bb95ebbeSKees Cook 	};
2384797632fSKees Cook 	struct some_bytes after = {
2394797632fSKees Cook 		.data = { 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x72,
2404797632fSKees Cook 			  0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
2414797632fSKees Cook 			  0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
2424797632fSKees Cook 			  0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
2434797632fSKees Cook 			},
2444797632fSKees Cook 	};
2456dbefad4SKees Cook 	struct some_bytes startat = {
2466dbefad4SKees Cook 		.data = { 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
2476dbefad4SKees Cook 			  0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
2486dbefad4SKees Cook 			  0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
2496dbefad4SKees Cook 			  0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
2506dbefad4SKees Cook 			},
2516dbefad4SKees Cook 	};
252bb95ebbeSKees Cook 	struct some_bytes dest = { };
253bb95ebbeSKees Cook 	int count, value;
254bb95ebbeSKees Cook 	u8 *ptr;
255bb95ebbeSKees Cook 
256bb95ebbeSKees Cook 	/* Verify static initializers. */
257bb95ebbeSKees Cook 	check(control, 0x30);
258bb95ebbeSKees Cook 	check(dest, 0);
259bb95ebbeSKees Cook 
260bb95ebbeSKees Cook 	/* Verify assignment. */
261bb95ebbeSKees Cook 	dest = control;
262bb95ebbeSKees Cook 	compare("direct assignment", dest, control);
263bb95ebbeSKees Cook 
264bb95ebbeSKees Cook 	/* Verify complete overwrite. */
265bb95ebbeSKees Cook 	memset(dest.data, 0xff, sizeof(dest.data));
266bb95ebbeSKees Cook 	compare("complete overwrite", dest, complete);
267bb95ebbeSKees Cook 
268bb95ebbeSKees Cook 	/* Verify middle overwrite. */
269bb95ebbeSKees Cook 	dest = control;
270bb95ebbeSKees Cook 	memset(dest.data + 4, 0x31, 16);
271bb95ebbeSKees Cook 	compare("middle overwrite", dest, middle);
272bb95ebbeSKees Cook 
273bb95ebbeSKees Cook 	/* Verify argument side-effects aren't repeated. */
274bb95ebbeSKees Cook 	dest = control;
275bb95ebbeSKees Cook 	ptr = dest.data;
276bb95ebbeSKees Cook 	value = 0x60;
277bb95ebbeSKees Cook 	count = 1;
278bb95ebbeSKees Cook 	memset(ptr++, value++, count++);
279bb95ebbeSKees Cook 	ptr += 8;
280bb95ebbeSKees Cook 	memset(ptr++, value++, count++);
281bb95ebbeSKees Cook 	compare("argument side-effects", dest, three);
2824797632fSKees Cook 
2834797632fSKees Cook 	/* Verify memset_after() */
2844797632fSKees Cook 	dest = control;
2854797632fSKees Cook 	memset_after(&dest, 0x72, three);
2864797632fSKees Cook 	compare("memset_after()", dest, after);
2874797632fSKees Cook 
2886dbefad4SKees Cook 	/* Verify memset_startat() */
2896dbefad4SKees Cook 	dest = control;
2906dbefad4SKees Cook 	memset_startat(&dest, 0x79, four);
2916dbefad4SKees Cook 	compare("memset_startat()", dest, startat);
292bb95ebbeSKees Cook #undef TEST_OP
293bb95ebbeSKees Cook }
294bb95ebbeSKees Cook 
29596fce387SKees Cook static u8 large_src[1024];
29696fce387SKees Cook static u8 large_dst[2048];
29796fce387SKees Cook static const u8 large_zero[2048];
29896fce387SKees Cook 
set_random_nonzero(struct kunit * test,u8 * byte)29996fce387SKees Cook static void set_random_nonzero(struct kunit *test, u8 *byte)
30096fce387SKees Cook {
30196fce387SKees Cook 	int failed_rng = 0;
30296fce387SKees Cook 
30396fce387SKees Cook 	while (*byte == 0) {
30496fce387SKees Cook 		get_random_bytes(byte, 1);
30596fce387SKees Cook 		KUNIT_ASSERT_LT_MSG(test, failed_rng++, 100,
30696fce387SKees Cook 				    "Is the RNG broken?");
30796fce387SKees Cook 	}
30896fce387SKees Cook }
30996fce387SKees Cook 
init_large(struct kunit * test)31096fce387SKees Cook static void init_large(struct kunit *test)
31196fce387SKees Cook {
3124acf1de3SKees Cook 	if (!IS_ENABLED(CONFIG_MEMCPY_SLOW_KUNIT_TEST))
3134acf1de3SKees Cook 		kunit_skip(test, "Slow test skipped. Enable with CONFIG_MEMCPY_SLOW_KUNIT_TEST=y");
31496fce387SKees Cook 
31596fce387SKees Cook 	/* Get many bit patterns. */
31696fce387SKees Cook 	get_random_bytes(large_src, ARRAY_SIZE(large_src));
31796fce387SKees Cook 
31896fce387SKees Cook 	/* Make sure we have non-zero edges. */
31996fce387SKees Cook 	set_random_nonzero(test, &large_src[0]);
32096fce387SKees Cook 	set_random_nonzero(test, &large_src[ARRAY_SIZE(large_src) - 1]);
32196fce387SKees Cook 
32296fce387SKees Cook 	/* Explicitly zero the entire destination. */
32396fce387SKees Cook 	memset(large_dst, 0, ARRAY_SIZE(large_dst));
32496fce387SKees Cook }
32596fce387SKees Cook 
32696fce387SKees Cook /*
32796fce387SKees Cook  * Instead of an indirect function call for "copy" or a giant macro,
32896fce387SKees Cook  * use a bool to pick memcpy or memmove.
32996fce387SKees Cook  */
copy_large_test(struct kunit * test,bool use_memmove)33096fce387SKees Cook static void copy_large_test(struct kunit *test, bool use_memmove)
33196fce387SKees Cook {
33296fce387SKees Cook 	init_large(test);
33396fce387SKees Cook 
33496fce387SKees Cook 	/* Copy a growing number of non-overlapping bytes ... */
33596fce387SKees Cook 	for (int bytes = 1; bytes <= ARRAY_SIZE(large_src); bytes++) {
33696fce387SKees Cook 		/* Over a shifting destination window ... */
33796fce387SKees Cook 		for (int offset = 0; offset < ARRAY_SIZE(large_src); offset++) {
33896fce387SKees Cook 			int right_zero_pos = offset + bytes;
33996fce387SKees Cook 			int right_zero_size = ARRAY_SIZE(large_dst) - right_zero_pos;
34096fce387SKees Cook 
34196fce387SKees Cook 			/* Copy! */
34296fce387SKees Cook 			if (use_memmove)
34396fce387SKees Cook 				memmove(large_dst + offset, large_src, bytes);
34496fce387SKees Cook 			else
34596fce387SKees Cook 				memcpy(large_dst + offset, large_src, bytes);
34696fce387SKees Cook 
34796fce387SKees Cook 			/* Did we touch anything before the copy area? */
34896fce387SKees Cook 			KUNIT_ASSERT_EQ_MSG(test,
34996fce387SKees Cook 				memcmp(large_dst, large_zero, offset), 0,
35096fce387SKees Cook 				"with size %d at offset %d", bytes, offset);
35196fce387SKees Cook 			/* Did we touch anything after the copy area? */
35296fce387SKees Cook 			KUNIT_ASSERT_EQ_MSG(test,
35396fce387SKees Cook 				memcmp(&large_dst[right_zero_pos], large_zero, right_zero_size), 0,
35496fce387SKees Cook 				"with size %d at offset %d", bytes, offset);
35596fce387SKees Cook 
35696fce387SKees Cook 			/* Are we byte-for-byte exact across the copy? */
35796fce387SKees Cook 			KUNIT_ASSERT_EQ_MSG(test,
35896fce387SKees Cook 				memcmp(large_dst + offset, large_src, bytes), 0,
35996fce387SKees Cook 				"with size %d at offset %d", bytes, offset);
36096fce387SKees Cook 
36196fce387SKees Cook 			/* Zero out what we copied for the next cycle. */
36296fce387SKees Cook 			memset(large_dst + offset, 0, bytes);
36396fce387SKees Cook 		}
36496fce387SKees Cook 		/* Avoid stall warnings if this loop gets slow. */
36596fce387SKees Cook 		cond_resched();
36696fce387SKees Cook 	}
36796fce387SKees Cook }
36896fce387SKees Cook 
memcpy_large_test(struct kunit * test)36996fce387SKees Cook static void memcpy_large_test(struct kunit *test)
37096fce387SKees Cook {
37196fce387SKees Cook 	copy_large_test(test, false);
37296fce387SKees Cook }
37396fce387SKees Cook 
memmove_large_test(struct kunit * test)37496fce387SKees Cook static void memmove_large_test(struct kunit *test)
37596fce387SKees Cook {
37696fce387SKees Cook 	copy_large_test(test, true);
37796fce387SKees Cook }
37896fce387SKees Cook 
37996fce387SKees Cook /*
38096fce387SKees Cook  * On the assumption that boundary conditions are going to be the most
38196fce387SKees Cook  * sensitive, instead of taking a full step (inc) each iteration,
38296fce387SKees Cook  * take single index steps for at least the first "inc"-many indexes
38396fce387SKees Cook  * from the "start" and at least the last "inc"-many indexes before
38496fce387SKees Cook  * the "end". When in the middle, take full "inc"-wide steps. For
38596fce387SKees Cook  * example, calling next_step(idx, 1, 15, 3) with idx starting at 0
38696fce387SKees Cook  * would see the following pattern: 1 2 3 4 7 10 11 12 13 14 15.
38796fce387SKees Cook  */
next_step(int idx,int start,int end,int inc)38896fce387SKees Cook static int next_step(int idx, int start, int end, int inc)
38996fce387SKees Cook {
39096fce387SKees Cook 	start += inc;
39196fce387SKees Cook 	end -= inc;
39296fce387SKees Cook 
39396fce387SKees Cook 	if (idx < start || idx + inc > end)
39496fce387SKees Cook 		inc = 1;
39596fce387SKees Cook 	return idx + inc;
39696fce387SKees Cook }
39796fce387SKees Cook 
inner_loop(struct kunit * test,int bytes,int d_off,int s_off)39896fce387SKees Cook static void inner_loop(struct kunit *test, int bytes, int d_off, int s_off)
39996fce387SKees Cook {
40096fce387SKees Cook 	int left_zero_pos, left_zero_size;
40196fce387SKees Cook 	int right_zero_pos, right_zero_size;
40296fce387SKees Cook 	int src_pos, src_orig_pos, src_size;
40396fce387SKees Cook 	int pos;
40496fce387SKees Cook 
40596fce387SKees Cook 	/* Place the source in the destination buffer. */
40696fce387SKees Cook 	memcpy(&large_dst[s_off], large_src, bytes);
40796fce387SKees Cook 
40896fce387SKees Cook 	/* Copy to destination offset. */
40996fce387SKees Cook 	memmove(&large_dst[d_off], &large_dst[s_off], bytes);
41096fce387SKees Cook 
41196fce387SKees Cook 	/* Make sure destination entirely matches. */
41296fce387SKees Cook 	KUNIT_ASSERT_EQ_MSG(test, memcmp(&large_dst[d_off], large_src, bytes), 0,
41396fce387SKees Cook 		"with size %d at src offset %d and dest offset %d",
41496fce387SKees Cook 		bytes, s_off, d_off);
41596fce387SKees Cook 
41696fce387SKees Cook 	/* Calculate the expected zero spans. */
41796fce387SKees Cook 	if (s_off < d_off) {
41896fce387SKees Cook 		left_zero_pos = 0;
41996fce387SKees Cook 		left_zero_size = s_off;
42096fce387SKees Cook 
42196fce387SKees Cook 		right_zero_pos = d_off + bytes;
42296fce387SKees Cook 		right_zero_size = ARRAY_SIZE(large_dst) - right_zero_pos;
42396fce387SKees Cook 
42496fce387SKees Cook 		src_pos = s_off;
42596fce387SKees Cook 		src_orig_pos = 0;
42696fce387SKees Cook 		src_size = d_off - s_off;
42796fce387SKees Cook 	} else {
42896fce387SKees Cook 		left_zero_pos = 0;
42996fce387SKees Cook 		left_zero_size = d_off;
43096fce387SKees Cook 
43196fce387SKees Cook 		right_zero_pos = s_off + bytes;
43296fce387SKees Cook 		right_zero_size = ARRAY_SIZE(large_dst) - right_zero_pos;
43396fce387SKees Cook 
43496fce387SKees Cook 		src_pos = d_off + bytes;
43596fce387SKees Cook 		src_orig_pos = src_pos - s_off;
43696fce387SKees Cook 		src_size = right_zero_pos - src_pos;
43796fce387SKees Cook 	}
43896fce387SKees Cook 
43996fce387SKees Cook 	/* Check non-overlapping source is unchanged.*/
44096fce387SKees Cook 	KUNIT_ASSERT_EQ_MSG(test,
44196fce387SKees Cook 		memcmp(&large_dst[src_pos], &large_src[src_orig_pos], src_size), 0,
44296fce387SKees Cook 		"with size %d at src offset %d and dest offset %d",
44396fce387SKees Cook 		bytes, s_off, d_off);
44496fce387SKees Cook 
44596fce387SKees Cook 	/* Check leading buffer contents are zero. */
44696fce387SKees Cook 	KUNIT_ASSERT_EQ_MSG(test,
44796fce387SKees Cook 		memcmp(&large_dst[left_zero_pos], large_zero, left_zero_size), 0,
44896fce387SKees Cook 		"with size %d at src offset %d and dest offset %d",
44996fce387SKees Cook 		bytes, s_off, d_off);
45096fce387SKees Cook 	/* Check trailing buffer contents are zero. */
45196fce387SKees Cook 	KUNIT_ASSERT_EQ_MSG(test,
45296fce387SKees Cook 		memcmp(&large_dst[right_zero_pos], large_zero, right_zero_size), 0,
45396fce387SKees Cook 		"with size %d at src offset %d and dest offset %d",
45496fce387SKees Cook 		bytes, s_off, d_off);
45596fce387SKees Cook 
45696fce387SKees Cook 	/* Zero out everything not already zeroed.*/
45796fce387SKees Cook 	pos = left_zero_pos + left_zero_size;
45896fce387SKees Cook 	memset(&large_dst[pos], 0, right_zero_pos - pos);
45996fce387SKees Cook }
46096fce387SKees Cook 
memmove_overlap_test(struct kunit * test)46196fce387SKees Cook static void memmove_overlap_test(struct kunit *test)
46296fce387SKees Cook {
46396fce387SKees Cook 	/*
46496fce387SKees Cook 	 * Running all possible offset and overlap combinations takes a
46596fce387SKees Cook 	 * very long time. Instead, only check up to 128 bytes offset
46696fce387SKees Cook 	 * into the destination buffer (which should result in crossing
46796fce387SKees Cook 	 * cachelines), with a step size of 1 through 7 to try to skip some
46896fce387SKees Cook 	 * redundancy.
46996fce387SKees Cook 	 */
47096fce387SKees Cook 	static const int offset_max = 128; /* less than ARRAY_SIZE(large_src); */
47196fce387SKees Cook 	static const int bytes_step = 7;
47296fce387SKees Cook 	static const int window_step = 7;
47396fce387SKees Cook 
47496fce387SKees Cook 	static const int bytes_start = 1;
47596fce387SKees Cook 	static const int bytes_end = ARRAY_SIZE(large_src) + 1;
47696fce387SKees Cook 
47796fce387SKees Cook 	init_large(test);
47896fce387SKees Cook 
47996fce387SKees Cook 	/* Copy a growing number of overlapping bytes ... */
48096fce387SKees Cook 	for (int bytes = bytes_start; bytes < bytes_end;
48196fce387SKees Cook 	     bytes = next_step(bytes, bytes_start, bytes_end, bytes_step)) {
48296fce387SKees Cook 
48396fce387SKees Cook 		/* Over a shifting destination window ... */
48496fce387SKees Cook 		for (int d_off = 0; d_off < offset_max; d_off++) {
48596fce387SKees Cook 			int s_start = max(d_off - bytes, 0);
48696fce387SKees Cook 			int s_end = min_t(int, d_off + bytes, ARRAY_SIZE(large_src));
48796fce387SKees Cook 
48896fce387SKees Cook 			/* Over a shifting source window ... */
48996fce387SKees Cook 			for (int s_off = s_start; s_off < s_end;
49096fce387SKees Cook 			     s_off = next_step(s_off, s_start, s_end, window_step))
49196fce387SKees Cook 				inner_loop(test, bytes, d_off, s_off);
49296fce387SKees Cook 
49396fce387SKees Cook 			/* Avoid stall warnings. */
49496fce387SKees Cook 			cond_resched();
49596fce387SKees Cook 		}
49696fce387SKees Cook 	}
49796fce387SKees Cook }
49896fce387SKees Cook 
strtomem_test(struct kunit * test)499dfbafa70SKees Cook static void strtomem_test(struct kunit *test)
500dfbafa70SKees Cook {
50166cb2a36SKees Cook 	static const char input[sizeof(unsigned long)] = "hi";
502dfbafa70SKees Cook 	static const char truncate[] = "this is too long";
503dfbafa70SKees Cook 	struct {
504dfbafa70SKees Cook 		unsigned long canary1;
505dfbafa70SKees Cook 		unsigned char output[sizeof(unsigned long)] __nonstring;
506dfbafa70SKees Cook 		unsigned long canary2;
507dfbafa70SKees Cook 	} wrap;
508dfbafa70SKees Cook 
509dfbafa70SKees Cook 	memset(&wrap, 0xFF, sizeof(wrap));
510dfbafa70SKees Cook 	KUNIT_EXPECT_EQ_MSG(test, wrap.canary1, ULONG_MAX,
511dfbafa70SKees Cook 			    "bad initial canary value");
512dfbafa70SKees Cook 	KUNIT_EXPECT_EQ_MSG(test, wrap.canary2, ULONG_MAX,
513dfbafa70SKees Cook 			    "bad initial canary value");
514dfbafa70SKees Cook 
515dfbafa70SKees Cook 	/* Check unpadded copy leaves surroundings untouched. */
516dfbafa70SKees Cook 	strtomem(wrap.output, input);
517dfbafa70SKees Cook 	KUNIT_EXPECT_EQ(test, wrap.canary1, ULONG_MAX);
518dfbafa70SKees Cook 	KUNIT_EXPECT_EQ(test, wrap.output[0], input[0]);
519dfbafa70SKees Cook 	KUNIT_EXPECT_EQ(test, wrap.output[1], input[1]);
520dfbafa70SKees Cook 	for (size_t i = 2; i < sizeof(wrap.output); i++)
521dfbafa70SKees Cook 		KUNIT_EXPECT_EQ(test, wrap.output[i], 0xFF);
522dfbafa70SKees Cook 	KUNIT_EXPECT_EQ(test, wrap.canary2, ULONG_MAX);
523dfbafa70SKees Cook 
524dfbafa70SKees Cook 	/* Check truncated copy leaves surroundings untouched. */
525dfbafa70SKees Cook 	memset(&wrap, 0xFF, sizeof(wrap));
526dfbafa70SKees Cook 	strtomem(wrap.output, truncate);
527dfbafa70SKees Cook 	KUNIT_EXPECT_EQ(test, wrap.canary1, ULONG_MAX);
528dfbafa70SKees Cook 	for (size_t i = 0; i < sizeof(wrap.output); i++)
529dfbafa70SKees Cook 		KUNIT_EXPECT_EQ(test, wrap.output[i], truncate[i]);
530dfbafa70SKees Cook 	KUNIT_EXPECT_EQ(test, wrap.canary2, ULONG_MAX);
531dfbafa70SKees Cook 
532dfbafa70SKees Cook 	/* Check padded copy leaves only string padded. */
533dfbafa70SKees Cook 	memset(&wrap, 0xFF, sizeof(wrap));
534dfbafa70SKees Cook 	strtomem_pad(wrap.output, input, 0xAA);
535dfbafa70SKees Cook 	KUNIT_EXPECT_EQ(test, wrap.canary1, ULONG_MAX);
536dfbafa70SKees Cook 	KUNIT_EXPECT_EQ(test, wrap.output[0], input[0]);
537dfbafa70SKees Cook 	KUNIT_EXPECT_EQ(test, wrap.output[1], input[1]);
538dfbafa70SKees Cook 	for (size_t i = 2; i < sizeof(wrap.output); i++)
539dfbafa70SKees Cook 		KUNIT_EXPECT_EQ(test, wrap.output[i], 0xAA);
540dfbafa70SKees Cook 	KUNIT_EXPECT_EQ(test, wrap.canary2, ULONG_MAX);
541dfbafa70SKees Cook 
542dfbafa70SKees Cook 	/* Check truncated padded copy has no padding. */
543dfbafa70SKees Cook 	memset(&wrap, 0xFF, sizeof(wrap));
544dfbafa70SKees Cook 	strtomem(wrap.output, truncate);
545dfbafa70SKees Cook 	KUNIT_EXPECT_EQ(test, wrap.canary1, ULONG_MAX);
546dfbafa70SKees Cook 	for (size_t i = 0; i < sizeof(wrap.output); i++)
547dfbafa70SKees Cook 		KUNIT_EXPECT_EQ(test, wrap.output[i], truncate[i]);
548dfbafa70SKees Cook 	KUNIT_EXPECT_EQ(test, wrap.canary2, ULONG_MAX);
549dfbafa70SKees Cook }
550dfbafa70SKees Cook 
551bb95ebbeSKees Cook static struct kunit_case memcpy_test_cases[] = {
552bb95ebbeSKees Cook 	KUNIT_CASE(memset_test),
553bb95ebbeSKees Cook 	KUNIT_CASE(memcpy_test),
554d055c6a2SRae Moar 	KUNIT_CASE_SLOW(memcpy_large_test),
555d055c6a2SRae Moar 	KUNIT_CASE_SLOW(memmove_test),
556d055c6a2SRae Moar 	KUNIT_CASE_SLOW(memmove_large_test),
557d055c6a2SRae Moar 	KUNIT_CASE_SLOW(memmove_overlap_test),
558dfbafa70SKees Cook 	KUNIT_CASE(strtomem_test),
559bb95ebbeSKees Cook 	{}
560bb95ebbeSKees Cook };
561bb95ebbeSKees Cook 
562bb95ebbeSKees Cook static struct kunit_suite memcpy_test_suite = {
563bb95ebbeSKees Cook 	.name = "memcpy",
564bb95ebbeSKees Cook 	.test_cases = memcpy_test_cases,
565bb95ebbeSKees Cook };
566bb95ebbeSKees Cook 
567bb95ebbeSKees Cook kunit_test_suite(memcpy_test_suite);
568bb95ebbeSKees Cook 
569bb95ebbeSKees Cook MODULE_LICENSE("GPL");
570