Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/char/misc_minor_kunit.c
29509 views
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) */
11
static struct miscdevice dev_static_minor = {
12
.minor = LCD_MINOR,
13
.name = "dev_static_minor",
14
};
15
16
/* misc dynamic minor */
17
static struct miscdevice dev_misc_dynamic_minor = {
18
.minor = MISC_DYNAMIC_MINOR,
19
.name = "dev_misc_dynamic_minor",
20
};
21
22
static 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
32
static 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
41
struct miscdev_test_case {
42
const char *str;
43
int minor;
44
};
45
46
static 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
65
KUNIT_ARRAY_PARAM_DESC(miscdev, miscdev_test_ranges, str);
66
67
static 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
140
static bool is_valid_dynamic_minor(int minor)
141
{
142
if (minor < 0)
143
return false;
144
return minor > MISC_DYNAMIC_MINOR;
145
}
146
147
static int miscdev_test_open(struct inode *inode, struct file *file)
148
{
149
return 0;
150
}
151
152
static const struct file_operations miscdev_test_fops = {
153
.open = miscdev_test_open,
154
};
155
156
static 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_OR_NULL(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
178
static 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
199
static 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
218
static 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
242
static 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
270
static 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
*/
300
static 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
*/
355
static 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
394
static 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
433
static 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
474
static 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
515
static 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
547
static 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 */
590
static 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
*/
610
static 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
648
static 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
661
static struct kunit_suite test_suite = {
662
.name = "miscdev",
663
.suite_init = miscdev_find_minors,
664
.test_cases = test_cases,
665
};
666
kunit_test_suite(test_suite);
667
668
static 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
679
static struct kunit_suite test_init_suite = {
680
.name = "miscdev_init",
681
.suite_init = miscdev_find_minors,
682
.test_cases = test_init_cases,
683
};
684
kunit_test_init_section_suite(test_init_suite);
685
686
MODULE_LICENSE("GPL");
687
MODULE_AUTHOR("Vimal Agrawal");
688
MODULE_AUTHOR("Thadeu Lima de Souza Cascardo <[email protected]>");
689
MODULE_DESCRIPTION("Test module for misc character devices");
690
691