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