xref: /linux/drivers/misc/misc_minor_kunit.c (revision 22c55fb9eb92395d999b8404d73e58540d11bdd8)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <kunit/test.h>
3 #include <kunit/test-bug.h>
4 #include <linux/module.h>
5 #include <linux/miscdevice.h>
6 #include <linux/fs.h>
7 #include <linux/file.h>
8 #include <linux/init_syscalls.h>
9 
10 /* dynamic minor (2) */
11 static struct miscdevice dev_dynamic_minor = {
12 	.minor  = 2,
13 	.name   = "dev_dynamic_minor",
14 };
15 
16 /* static minor (LCD_MINOR) */
17 static struct miscdevice dev_static_minor = {
18 	.minor  = LCD_MINOR,
19 	.name   = "dev_static_minor",
20 };
21 
22 /* misc dynamic minor */
23 static struct miscdevice dev_misc_dynamic_minor = {
24 	.minor  = MISC_DYNAMIC_MINOR,
25 	.name   = "dev_misc_dynamic_minor",
26 };
27 
28 static void kunit_dynamic_minor(struct kunit *test)
29 {
30 	int ret;
31 
32 	ret = misc_register(&dev_dynamic_minor);
33 	KUNIT_EXPECT_EQ(test, 0, ret);
34 	KUNIT_EXPECT_EQ(test, 2, dev_dynamic_minor.minor);
35 	misc_deregister(&dev_dynamic_minor);
36 }
37 
38 static void kunit_static_minor(struct kunit *test)
39 {
40 	int ret;
41 
42 	ret = misc_register(&dev_static_minor);
43 	KUNIT_EXPECT_EQ(test, 0, ret);
44 	KUNIT_EXPECT_EQ(test, LCD_MINOR, dev_static_minor.minor);
45 	misc_deregister(&dev_static_minor);
46 }
47 
48 static void kunit_misc_dynamic_minor(struct kunit *test)
49 {
50 	int ret;
51 
52 	ret = misc_register(&dev_misc_dynamic_minor);
53 	KUNIT_EXPECT_EQ(test, 0, ret);
54 	misc_deregister(&dev_misc_dynamic_minor);
55 }
56 
57 struct miscdev_test_case {
58 	const char *str;
59 	int minor;
60 };
61 
62 static struct miscdev_test_case miscdev_test_ranges[] = {
63 	{
64 		.str = "lower static range, top",
65 		.minor = 15,
66 	},
67 	{
68 		.str = "upper static range, bottom",
69 		.minor = 130,
70 	},
71 	{
72 		.str = "lower static range, bottom",
73 		.minor = 0,
74 	},
75 	{
76 		.str = "upper static range, top",
77 		.minor = MISC_DYNAMIC_MINOR - 1,
78 	},
79 };
80 
81 KUNIT_ARRAY_PARAM_DESC(miscdev, miscdev_test_ranges, str);
82 
83 static int miscdev_find_minors(struct kunit_suite *suite)
84 {
85 	int ret;
86 	struct miscdevice miscstat = {
87 		.name = "miscstat",
88 	};
89 	int i;
90 
91 	for (i = 15; i >= 0; i--) {
92 		miscstat.minor = i;
93 		ret = misc_register(&miscstat);
94 		if (ret == 0)
95 			break;
96 	}
97 
98 	if (ret == 0) {
99 		kunit_info(suite, "found misc device minor %d available\n",
100 				miscstat.minor);
101 		miscdev_test_ranges[0].minor = miscstat.minor;
102 		misc_deregister(&miscstat);
103 	} else {
104 		return ret;
105 	}
106 
107 	for (i = 128; i < MISC_DYNAMIC_MINOR; i++) {
108 		miscstat.minor = i;
109 		ret = misc_register(&miscstat);
110 		if (ret == 0)
111 			break;
112 	}
113 
114 	if (ret == 0) {
115 		kunit_info(suite, "found misc device minor %d available\n",
116 				miscstat.minor);
117 		miscdev_test_ranges[1].minor = miscstat.minor;
118 		misc_deregister(&miscstat);
119 	} else {
120 		return ret;
121 	}
122 
123 	for (i = 0; i < miscdev_test_ranges[0].minor; i++) {
124 		miscstat.minor = i;
125 		ret = misc_register(&miscstat);
126 		if (ret == 0)
127 			break;
128 	}
129 
130 	if (ret == 0) {
131 		kunit_info(suite, "found misc device minor %d available\n",
132 			miscstat.minor);
133 		miscdev_test_ranges[2].minor = miscstat.minor;
134 		misc_deregister(&miscstat);
135 	} else {
136 		return ret;
137 	}
138 
139 	for (i = MISC_DYNAMIC_MINOR - 1; i > miscdev_test_ranges[1].minor; i--) {
140 		miscstat.minor = i;
141 		ret = misc_register(&miscstat);
142 		if (ret == 0)
143 			break;
144 	}
145 
146 	if (ret == 0) {
147 		kunit_info(suite, "found misc device minor %d available\n",
148 			miscstat.minor);
149 		miscdev_test_ranges[3].minor = miscstat.minor;
150 		misc_deregister(&miscstat);
151 	}
152 
153 	return ret;
154 }
155 
156 static bool is_valid_dynamic_minor(int minor)
157 {
158 	if (minor < 0)
159 		return false;
160 	if (minor == MISC_DYNAMIC_MINOR)
161 		return false;
162 	if (minor >= 0 && minor <= 15)
163 		return false;
164 	if (minor >= 128 && minor < MISC_DYNAMIC_MINOR)
165 		return false;
166 	return true;
167 }
168 
169 static int miscdev_test_open(struct inode *inode, struct file *file)
170 {
171 	return 0;
172 }
173 
174 static const struct file_operations miscdev_test_fops = {
175 	.open	= miscdev_test_open,
176 };
177 
178 static void __init miscdev_test_can_open(struct kunit *test, struct miscdevice *misc)
179 {
180 	int ret;
181 	struct file *filp;
182 	char *devname;
183 
184 	devname = kasprintf(GFP_KERNEL, "/dev/%s", misc->name);
185 	ret = init_mknod(devname, S_IFCHR | 0600,
186 			 new_encode_dev(MKDEV(MISC_MAJOR, misc->minor)));
187 	if (ret != 0)
188 		KUNIT_FAIL(test, "failed to create node\n");
189 
190 	filp = filp_open(devname, O_RDONLY, 0);
191 	if (IS_ERR_OR_NULL(filp))
192 		KUNIT_FAIL(test, "failed to open misc device: %ld\n", PTR_ERR(filp));
193 	else
194 		fput(filp);
195 
196 	init_unlink(devname);
197 	kfree(devname);
198 }
199 
200 static void __init miscdev_test_static_basic(struct kunit *test)
201 {
202 	struct miscdevice misc_test = {
203 		.name = "misc_test",
204 		.fops = &miscdev_test_fops,
205 	};
206 	int ret;
207 	const struct miscdev_test_case *params = test->param_value;
208 
209 	misc_test.minor = params->minor;
210 
211 	ret = misc_register(&misc_test);
212 	KUNIT_EXPECT_EQ(test, ret, 0);
213 	KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor);
214 
215 	if (ret == 0) {
216 		miscdev_test_can_open(test, &misc_test);
217 		misc_deregister(&misc_test);
218 	}
219 }
220 
221 static void __init miscdev_test_dynamic_basic(struct kunit *test)
222 {
223 	struct miscdevice misc_test = {
224 		.minor = MISC_DYNAMIC_MINOR,
225 		.name = "misc_test",
226 		.fops = &miscdev_test_fops,
227 	};
228 	int ret;
229 
230 	ret = misc_register(&misc_test);
231 	KUNIT_EXPECT_EQ(test, ret, 0);
232 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc_test.minor));
233 
234 	if (ret == 0) {
235 		miscdev_test_can_open(test, &misc_test);
236 		misc_deregister(&misc_test);
237 	}
238 }
239 
240 static void miscdev_test_twice(struct kunit *test)
241 {
242 	struct miscdevice misc_test = {
243 		.name = "misc_test",
244 		.fops = &miscdev_test_fops,
245 	};
246 	int ret;
247 	const struct miscdev_test_case *params = test->param_value;
248 
249 	misc_test.minor = params->minor;
250 
251 	ret = misc_register(&misc_test);
252 	KUNIT_EXPECT_EQ(test, ret, 0);
253 	KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor);
254 	if (ret == 0)
255 		misc_deregister(&misc_test);
256 
257 	ret = misc_register(&misc_test);
258 	KUNIT_EXPECT_EQ(test, ret, 0);
259 	KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor);
260 	if (ret == 0)
261 		misc_deregister(&misc_test);
262 }
263 
264 static void miscdev_test_duplicate_minor(struct kunit *test)
265 {
266 	struct miscdevice misc1 = {
267 		.name = "misc1",
268 		.fops = &miscdev_test_fops,
269 	};
270 	struct miscdevice misc2 = {
271 		.name = "misc2",
272 		.fops = &miscdev_test_fops,
273 	};
274 	int ret;
275 	const struct miscdev_test_case *params = test->param_value;
276 
277 	misc1.minor = params->minor;
278 	misc2.minor = params->minor;
279 
280 	ret = misc_register(&misc1);
281 	KUNIT_EXPECT_EQ(test, ret, 0);
282 	KUNIT_EXPECT_EQ(test, misc1.minor, params->minor);
283 
284 	ret = misc_register(&misc2);
285 	KUNIT_EXPECT_EQ(test, ret, -EBUSY);
286 	if (ret == 0)
287 		misc_deregister(&misc2);
288 
289 	misc_deregister(&misc1);
290 }
291 
292 static void miscdev_test_duplicate_name(struct kunit *test)
293 {
294 	struct miscdevice misc1 = {
295 		.minor = MISC_DYNAMIC_MINOR,
296 		.name = "misc1",
297 		.fops = &miscdev_test_fops,
298 	};
299 	struct miscdevice misc2 = {
300 		.minor = MISC_DYNAMIC_MINOR,
301 		.name = "misc1",
302 		.fops = &miscdev_test_fops,
303 	};
304 	int ret;
305 
306 	ret = misc_register(&misc1);
307 	KUNIT_EXPECT_EQ(test, ret, 0);
308 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor));
309 
310 	ret = misc_register(&misc2);
311 	KUNIT_EXPECT_EQ(test, ret, -EEXIST);
312 	if (ret == 0)
313 		misc_deregister(&misc2);
314 
315 	misc_deregister(&misc1);
316 }
317 
318 /*
319  * Test that after a duplicate name failure, the reserved minor number is
320  * freed to be allocated next.
321  */
322 static void miscdev_test_duplicate_name_leak(struct kunit *test)
323 {
324 	struct miscdevice misc1 = {
325 		.minor = MISC_DYNAMIC_MINOR,
326 		.name = "misc1",
327 		.fops = &miscdev_test_fops,
328 	};
329 	struct miscdevice misc2 = {
330 		.minor = MISC_DYNAMIC_MINOR,
331 		.name = "misc1",
332 		.fops = &miscdev_test_fops,
333 	};
334 	struct miscdevice misc3 = {
335 		.minor = MISC_DYNAMIC_MINOR,
336 		.name = "misc3",
337 		.fops = &miscdev_test_fops,
338 	};
339 	int ret;
340 	int dyn_minor;
341 
342 	ret = misc_register(&misc1);
343 	KUNIT_EXPECT_EQ(test, ret, 0);
344 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor));
345 
346 	/*
347 	 * Find out what is the next minor number available.
348 	 */
349 	ret = misc_register(&misc3);
350 	KUNIT_EXPECT_EQ(test, ret, 0);
351 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor));
352 	dyn_minor = misc3.minor;
353 	misc_deregister(&misc3);
354 	misc3.minor = MISC_DYNAMIC_MINOR;
355 
356 	ret = misc_register(&misc2);
357 	KUNIT_EXPECT_EQ(test, ret, -EEXIST);
358 	if (ret == 0)
359 		misc_deregister(&misc2);
360 
361 	/*
362 	 * Now check that we can still get the same minor we found before.
363 	 */
364 	ret = misc_register(&misc3);
365 	KUNIT_EXPECT_EQ(test, ret, 0);
366 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor));
367 	KUNIT_EXPECT_EQ(test, misc3.minor, dyn_minor);
368 	misc_deregister(&misc3);
369 
370 	misc_deregister(&misc1);
371 }
372 
373 /*
374  * Try to register a static minor with a duplicate name. That might not
375  * deallocate the minor, preventing it from being used again.
376  */
377 static void miscdev_test_duplicate_error(struct kunit *test)
378 {
379 	struct miscdevice miscdyn = {
380 		.minor = MISC_DYNAMIC_MINOR,
381 		.name = "name1",
382 		.fops = &miscdev_test_fops,
383 	};
384 	struct miscdevice miscstat = {
385 		.name = "name1",
386 		.fops = &miscdev_test_fops,
387 	};
388 	struct miscdevice miscnew = {
389 		.name = "name2",
390 		.fops = &miscdev_test_fops,
391 	};
392 	int ret;
393 	const struct miscdev_test_case *params = test->param_value;
394 
395 	miscstat.minor = params->minor;
396 	miscnew.minor = params->minor;
397 
398 	ret = misc_register(&miscdyn);
399 	KUNIT_EXPECT_EQ(test, ret, 0);
400 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
401 
402 	ret = misc_register(&miscstat);
403 	KUNIT_EXPECT_EQ(test, ret, -EEXIST);
404 	if (ret == 0)
405 		misc_deregister(&miscstat);
406 
407 	ret = misc_register(&miscnew);
408 	KUNIT_EXPECT_EQ(test, ret, 0);
409 	KUNIT_EXPECT_EQ(test, miscnew.minor, params->minor);
410 	if (ret == 0)
411 		misc_deregister(&miscnew);
412 
413 	misc_deregister(&miscdyn);
414 }
415 
416 static void __init miscdev_test_dynamic_only_range(struct kunit *test)
417 {
418 	int ret;
419 	struct miscdevice *miscdev;
420 	const int dynamic_minors = 256;
421 	int i;
422 
423 	miscdev = kunit_kmalloc_array(test, dynamic_minors,
424 					sizeof(struct miscdevice),
425 					GFP_KERNEL | __GFP_ZERO);
426 
427 	for (i = 0; i < dynamic_minors; i++) {
428 		miscdev[i].minor = MISC_DYNAMIC_MINOR;
429 		miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i);
430 		miscdev[i].fops = &miscdev_test_fops;
431 		ret = misc_register(&miscdev[i]);
432 		if (ret != 0)
433 			break;
434 		/*
435 		 * This is the bug we are looking for!
436 		 * We asked for a dynamic minor and got a minor in the static range space.
437 		 */
438 		if (miscdev[i].minor >= 0 && miscdev[i].minor <= 15) {
439 			KUNIT_FAIL(test, "misc_register allocated minor %d\n", miscdev[i].minor);
440 			i++;
441 			break;
442 		}
443 		KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor));
444 	}
445 
446 	for (i--; i >= 0; i--) {
447 		miscdev_test_can_open(test, &miscdev[i]);
448 		misc_deregister(&miscdev[i]);
449 		kfree_const(miscdev[i].name);
450 	}
451 
452 	KUNIT_EXPECT_EQ(test, ret, 0);
453 }
454 
455 static void __init miscdev_test_collision(struct kunit *test)
456 {
457 	int ret;
458 	struct miscdevice *miscdev;
459 	struct miscdevice miscstat = {
460 		.name = "miscstat",
461 		.fops = &miscdev_test_fops,
462 	};
463 	const int dynamic_minors = 256;
464 	int i;
465 
466 	miscdev = kunit_kmalloc_array(test, dynamic_minors,
467 					sizeof(struct miscdevice),
468 					GFP_KERNEL | __GFP_ZERO);
469 
470 	miscstat.minor = miscdev_test_ranges[0].minor;
471 	ret = misc_register(&miscstat);
472 	KUNIT_ASSERT_EQ(test, ret, 0);
473 	KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor);
474 
475 	for (i = 0; i < dynamic_minors; i++) {
476 		miscdev[i].minor = MISC_DYNAMIC_MINOR;
477 		miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i);
478 		miscdev[i].fops = &miscdev_test_fops;
479 		ret = misc_register(&miscdev[i]);
480 		if (ret != 0)
481 			break;
482 		KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor));
483 	}
484 
485 	for (i--; i >= 0; i--) {
486 		miscdev_test_can_open(test, &miscdev[i]);
487 		misc_deregister(&miscdev[i]);
488 		kfree_const(miscdev[i].name);
489 	}
490 
491 	misc_deregister(&miscstat);
492 
493 	KUNIT_EXPECT_EQ(test, ret, 0);
494 }
495 
496 static void __init miscdev_test_collision_reverse(struct kunit *test)
497 {
498 	int ret;
499 	struct miscdevice *miscdev;
500 	struct miscdevice miscstat = {
501 		.name = "miscstat",
502 		.fops = &miscdev_test_fops,
503 	};
504 	const int dynamic_minors = 256;
505 	int i;
506 
507 	miscdev = kunit_kmalloc_array(test, dynamic_minors,
508 					sizeof(struct miscdevice),
509 					GFP_KERNEL | __GFP_ZERO);
510 
511 	for (i = 0; i < dynamic_minors; i++) {
512 		miscdev[i].minor = MISC_DYNAMIC_MINOR;
513 		miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i);
514 		miscdev[i].fops = &miscdev_test_fops;
515 		ret = misc_register(&miscdev[i]);
516 		if (ret != 0)
517 			break;
518 		KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor));
519 	}
520 
521 	KUNIT_EXPECT_EQ(test, ret, 0);
522 
523 	miscstat.minor = miscdev_test_ranges[0].minor;
524 	ret = misc_register(&miscstat);
525 	KUNIT_EXPECT_EQ(test, ret, 0);
526 	KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor);
527 	if (ret == 0)
528 		misc_deregister(&miscstat);
529 
530 	for (i--; i >= 0; i--) {
531 		miscdev_test_can_open(test, &miscdev[i]);
532 		misc_deregister(&miscdev[i]);
533 		kfree_const(miscdev[i].name);
534 	}
535 }
536 
537 static void __init miscdev_test_conflict(struct kunit *test)
538 {
539 	int ret;
540 	struct miscdevice miscdyn = {
541 		.name = "miscdyn",
542 		.minor = MISC_DYNAMIC_MINOR,
543 		.fops = &miscdev_test_fops,
544 	};
545 	struct miscdevice miscstat = {
546 		.name = "miscstat",
547 		.fops = &miscdev_test_fops,
548 	};
549 
550 	ret = misc_register(&miscdyn);
551 	KUNIT_ASSERT_EQ(test, ret, 0);
552 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
553 
554 	/*
555 	 * Try to register a static minor with the same minor as the
556 	 * dynamic one.
557 	 */
558 	miscstat.minor = miscdyn.minor;
559 	ret = misc_register(&miscstat);
560 	KUNIT_EXPECT_EQ(test, ret, -EBUSY);
561 	if (ret == 0)
562 		misc_deregister(&miscstat);
563 
564 	miscdev_test_can_open(test, &miscdyn);
565 
566 	misc_deregister(&miscdyn);
567 }
568 
569 static void __init miscdev_test_conflict_reverse(struct kunit *test)
570 {
571 	int ret;
572 	struct miscdevice miscdyn = {
573 		.name = "miscdyn",
574 		.minor = MISC_DYNAMIC_MINOR,
575 		.fops = &miscdev_test_fops,
576 	};
577 	struct miscdevice miscstat = {
578 		.name = "miscstat",
579 		.fops = &miscdev_test_fops,
580 	};
581 
582 	/*
583 	 * Find the first available dynamic minor to use it as a static
584 	 * minor later on.
585 	 */
586 	ret = misc_register(&miscdyn);
587 	KUNIT_ASSERT_EQ(test, ret, 0);
588 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
589 	miscstat.minor = miscdyn.minor;
590 	misc_deregister(&miscdyn);
591 
592 	ret = misc_register(&miscstat);
593 	KUNIT_EXPECT_EQ(test, ret, 0);
594 	KUNIT_EXPECT_EQ(test, miscstat.minor, miscdyn.minor);
595 
596 	/*
597 	 * Try to register a dynamic minor after registering a static minor
598 	 * within the dynamic range. It should work but get a different
599 	 * minor.
600 	 */
601 	miscdyn.minor = MISC_DYNAMIC_MINOR;
602 	ret = misc_register(&miscdyn);
603 	KUNIT_EXPECT_EQ(test, ret, 0);
604 	KUNIT_EXPECT_NE(test, miscdyn.minor, miscstat.minor);
605 	KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
606 	if (ret == 0)
607 		misc_deregister(&miscdyn);
608 
609 	miscdev_test_can_open(test, &miscstat);
610 
611 	misc_deregister(&miscstat);
612 }
613 
614 static struct kunit_case test_cases[] = {
615 	KUNIT_CASE(kunit_dynamic_minor),
616 	KUNIT_CASE(kunit_static_minor),
617 	KUNIT_CASE(kunit_misc_dynamic_minor),
618 	KUNIT_CASE_PARAM(miscdev_test_twice, miscdev_gen_params),
619 	KUNIT_CASE_PARAM(miscdev_test_duplicate_minor, miscdev_gen_params),
620 	KUNIT_CASE(miscdev_test_duplicate_name),
621 	KUNIT_CASE(miscdev_test_duplicate_name_leak),
622 	KUNIT_CASE_PARAM(miscdev_test_duplicate_error, miscdev_gen_params),
623 	{}
624 };
625 
626 static struct kunit_suite test_suite = {
627 	.name = "miscdev",
628 	.suite_init = miscdev_find_minors,
629 	.test_cases = test_cases,
630 };
631 kunit_test_suite(test_suite);
632 
633 static struct kunit_case __refdata test_init_cases[] = {
634 	KUNIT_CASE_PARAM(miscdev_test_static_basic, miscdev_gen_params),
635 	KUNIT_CASE(miscdev_test_dynamic_basic),
636 	KUNIT_CASE(miscdev_test_dynamic_only_range),
637 	KUNIT_CASE(miscdev_test_collision),
638 	KUNIT_CASE(miscdev_test_collision_reverse),
639 	KUNIT_CASE(miscdev_test_conflict),
640 	KUNIT_CASE(miscdev_test_conflict_reverse),
641 	{}
642 };
643 
644 static struct kunit_suite test_init_suite = {
645 	.name = "miscdev_init",
646 	.suite_init = miscdev_find_minors,
647 	.test_cases = test_init_cases,
648 };
649 kunit_test_init_section_suite(test_init_suite);
650 
651 MODULE_LICENSE("GPL");
652 MODULE_AUTHOR("Vimal Agrawal");
653 MODULE_AUTHOR("Thadeu Lima de Souza Cascardo <cascardo@igalia.com>");
654 MODULE_DESCRIPTION("Test module for misc character devices");
655