File Index
Files and directories
Sign in
FILE

misc_minor_kunit.c

File preview with syntax highlighting.

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