| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689 |
- // SPDX-License-Identifier: GPL-2.0
- #include <kunit/test.h>
- #include <kunit/test-bug.h>
- #include <linux/module.h>
- #include <linux/miscdevice.h>
- #include <linux/fs.h>
- #include <linux/file.h>
- #include <linux/init_syscalls.h>
- /* static minor (LCD_MINOR) */
- static struct miscdevice dev_static_minor = {
- .minor = LCD_MINOR,
- .name = "dev_static_minor",
- };
- /* misc dynamic minor */
- static struct miscdevice dev_misc_dynamic_minor = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "dev_misc_dynamic_minor",
- };
- static void kunit_static_minor(struct kunit *test)
- {
- int ret;
- ret = misc_register(&dev_static_minor);
- KUNIT_EXPECT_EQ(test, 0, ret);
- KUNIT_EXPECT_EQ(test, LCD_MINOR, dev_static_minor.minor);
- misc_deregister(&dev_static_minor);
- }
- static void kunit_misc_dynamic_minor(struct kunit *test)
- {
- int ret;
- ret = misc_register(&dev_misc_dynamic_minor);
- KUNIT_EXPECT_EQ(test, 0, ret);
- misc_deregister(&dev_misc_dynamic_minor);
- }
- struct miscdev_test_case {
- const char *str;
- int minor;
- };
- static struct miscdev_test_case miscdev_test_ranges[] = {
- {
- .str = "lower static range, top",
- .minor = 15,
- },
- {
- .str = "upper static range, bottom",
- .minor = 130,
- },
- {
- .str = "lower static range, bottom",
- .minor = 0,
- },
- {
- .str = "upper static range, top",
- .minor = MISC_DYNAMIC_MINOR - 1,
- },
- };
- KUNIT_ARRAY_PARAM_DESC(miscdev, miscdev_test_ranges, str);
- static int miscdev_find_minors(struct kunit_suite *suite)
- {
- int ret;
- struct miscdevice miscstat = {
- .name = "miscstat",
- };
- int i;
- for (i = 15; i >= 0; i--) {
- miscstat.minor = i;
- ret = misc_register(&miscstat);
- if (ret == 0)
- break;
- }
- if (ret == 0) {
- kunit_info(suite, "found misc device minor %d available\n",
- miscstat.minor);
- miscdev_test_ranges[0].minor = miscstat.minor;
- misc_deregister(&miscstat);
- } else {
- return ret;
- }
- for (i = 128; i < MISC_DYNAMIC_MINOR; i++) {
- miscstat.minor = i;
- ret = misc_register(&miscstat);
- if (ret == 0)
- break;
- }
- if (ret == 0) {
- kunit_info(suite, "found misc device minor %d available\n",
- miscstat.minor);
- miscdev_test_ranges[1].minor = miscstat.minor;
- misc_deregister(&miscstat);
- } else {
- return ret;
- }
- for (i = 0; i < miscdev_test_ranges[0].minor; i++) {
- miscstat.minor = i;
- ret = misc_register(&miscstat);
- if (ret == 0)
- break;
- }
- if (ret == 0) {
- kunit_info(suite, "found misc device minor %d available\n",
- miscstat.minor);
- miscdev_test_ranges[2].minor = miscstat.minor;
- misc_deregister(&miscstat);
- } else {
- return ret;
- }
- for (i = MISC_DYNAMIC_MINOR - 1; i > miscdev_test_ranges[1].minor; i--) {
- miscstat.minor = i;
- ret = misc_register(&miscstat);
- if (ret == 0)
- break;
- }
- if (ret == 0) {
- kunit_info(suite, "found misc device minor %d available\n",
- miscstat.minor);
- miscdev_test_ranges[3].minor = miscstat.minor;
- misc_deregister(&miscstat);
- }
- return ret;
- }
- static bool is_valid_dynamic_minor(int minor)
- {
- if (minor < 0)
- return false;
- return minor > MISC_DYNAMIC_MINOR;
- }
- static int miscdev_test_open(struct inode *inode, struct file *file)
- {
- return 0;
- }
- static const struct file_operations miscdev_test_fops = {
- .open = miscdev_test_open,
- };
- static void __init miscdev_test_can_open(struct kunit *test, struct miscdevice *misc)
- {
- int ret;
- struct file *filp;
- char *devname;
- devname = kasprintf(GFP_KERNEL, "/dev/%s", misc->name);
- ret = init_mknod(devname, S_IFCHR | 0600,
- new_encode_dev(MKDEV(MISC_MAJOR, misc->minor)));
- if (ret != 0)
- KUNIT_FAIL(test, "failed to create node\n");
- filp = filp_open(devname, O_RDONLY, 0);
- if (IS_ERR(filp))
- KUNIT_FAIL(test, "failed to open misc device: %ld\n", PTR_ERR(filp));
- else
- fput(filp);
- init_unlink(devname);
- kfree(devname);
- }
- static void __init miscdev_test_static_basic(struct kunit *test)
- {
- struct miscdevice misc_test = {
- .name = "misc_test",
- .fops = &miscdev_test_fops,
- };
- int ret;
- const struct miscdev_test_case *params = test->param_value;
- misc_test.minor = params->minor;
- ret = misc_register(&misc_test);
- KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor);
- if (ret == 0) {
- miscdev_test_can_open(test, &misc_test);
- misc_deregister(&misc_test);
- }
- }
- static void __init miscdev_test_dynamic_basic(struct kunit *test)
- {
- struct miscdevice misc_test = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "misc_test",
- .fops = &miscdev_test_fops,
- };
- int ret;
- ret = misc_register(&misc_test);
- KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc_test.minor));
- if (ret == 0) {
- miscdev_test_can_open(test, &misc_test);
- misc_deregister(&misc_test);
- }
- }
- static void miscdev_test_twice(struct kunit *test)
- {
- struct miscdevice misc_test = {
- .name = "misc_test",
- .fops = &miscdev_test_fops,
- };
- int ret;
- const struct miscdev_test_case *params = test->param_value;
- misc_test.minor = params->minor;
- ret = misc_register(&misc_test);
- KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor);
- if (ret == 0)
- misc_deregister(&misc_test);
- ret = misc_register(&misc_test);
- KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor);
- if (ret == 0)
- misc_deregister(&misc_test);
- }
- static void miscdev_test_duplicate_minor(struct kunit *test)
- {
- struct miscdevice misc1 = {
- .name = "misc1",
- .fops = &miscdev_test_fops,
- };
- struct miscdevice misc2 = {
- .name = "misc2",
- .fops = &miscdev_test_fops,
- };
- int ret;
- const struct miscdev_test_case *params = test->param_value;
- misc1.minor = params->minor;
- misc2.minor = params->minor;
- ret = misc_register(&misc1);
- KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_EQ(test, misc1.minor, params->minor);
- ret = misc_register(&misc2);
- KUNIT_EXPECT_EQ(test, ret, -EBUSY);
- if (ret == 0)
- misc_deregister(&misc2);
- misc_deregister(&misc1);
- }
- static void miscdev_test_duplicate_name(struct kunit *test)
- {
- struct miscdevice misc1 = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "misc1",
- .fops = &miscdev_test_fops,
- };
- struct miscdevice misc2 = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "misc1",
- .fops = &miscdev_test_fops,
- };
- int ret;
- ret = misc_register(&misc1);
- KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor));
- ret = misc_register(&misc2);
- KUNIT_EXPECT_EQ(test, ret, -EEXIST);
- if (ret == 0)
- misc_deregister(&misc2);
- misc_deregister(&misc1);
- }
- /*
- * Test that after a duplicate name failure, the reserved minor number is
- * freed to be allocated next.
- */
- static void miscdev_test_duplicate_name_leak(struct kunit *test)
- {
- struct miscdevice misc1 = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "misc1",
- .fops = &miscdev_test_fops,
- };
- struct miscdevice misc2 = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "misc1",
- .fops = &miscdev_test_fops,
- };
- struct miscdevice misc3 = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "misc3",
- .fops = &miscdev_test_fops,
- };
- int ret;
- int dyn_minor;
- ret = misc_register(&misc1);
- KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor));
- /*
- * Find out what is the next minor number available.
- */
- ret = misc_register(&misc3);
- KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor));
- dyn_minor = misc3.minor;
- misc_deregister(&misc3);
- misc3.minor = MISC_DYNAMIC_MINOR;
- ret = misc_register(&misc2);
- KUNIT_EXPECT_EQ(test, ret, -EEXIST);
- if (ret == 0)
- misc_deregister(&misc2);
- /*
- * Now check that we can still get the same minor we found before.
- */
- ret = misc_register(&misc3);
- KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor));
- KUNIT_EXPECT_EQ(test, misc3.minor, dyn_minor);
- misc_deregister(&misc3);
- misc_deregister(&misc1);
- }
- /*
- * Try to register a static minor with a duplicate name. That might not
- * deallocate the minor, preventing it from being used again.
- */
- static void miscdev_test_duplicate_error(struct kunit *test)
- {
- struct miscdevice miscdyn = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "name1",
- .fops = &miscdev_test_fops,
- };
- struct miscdevice miscstat = {
- .name = "name1",
- .fops = &miscdev_test_fops,
- };
- struct miscdevice miscnew = {
- .name = "name2",
- .fops = &miscdev_test_fops,
- };
- int ret;
- const struct miscdev_test_case *params = test->param_value;
- miscstat.minor = params->minor;
- miscnew.minor = params->minor;
- ret = misc_register(&miscdyn);
- KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
- ret = misc_register(&miscstat);
- KUNIT_EXPECT_EQ(test, ret, -EEXIST);
- if (ret == 0)
- misc_deregister(&miscstat);
- ret = misc_register(&miscnew);
- KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_EQ(test, miscnew.minor, params->minor);
- if (ret == 0)
- misc_deregister(&miscnew);
- misc_deregister(&miscdyn);
- }
- static void __init miscdev_test_dynamic_only_range(struct kunit *test)
- {
- int ret;
- struct miscdevice *miscdev;
- const int dynamic_minors = 256;
- int i;
- miscdev = kunit_kmalloc_array(test, dynamic_minors,
- sizeof(struct miscdevice),
- GFP_KERNEL | __GFP_ZERO);
- for (i = 0; i < dynamic_minors; i++) {
- miscdev[i].minor = MISC_DYNAMIC_MINOR;
- miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i);
- miscdev[i].fops = &miscdev_test_fops;
- ret = misc_register(&miscdev[i]);
- if (ret != 0)
- break;
- /*
- * This is the bug we are looking for!
- * We asked for a dynamic minor and got a minor in the static range space.
- */
- if (miscdev[i].minor >= 0 && miscdev[i].minor <= 15) {
- KUNIT_FAIL(test, "misc_register allocated minor %d\n", miscdev[i].minor);
- i++;
- break;
- }
- KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor));
- }
- for (i--; i >= 0; i--) {
- miscdev_test_can_open(test, &miscdev[i]);
- misc_deregister(&miscdev[i]);
- kfree_const(miscdev[i].name);
- }
- KUNIT_EXPECT_EQ(test, ret, 0);
- }
- static void __init miscdev_test_collision(struct kunit *test)
- {
- int ret;
- struct miscdevice *miscdev;
- struct miscdevice miscstat = {
- .name = "miscstat",
- .fops = &miscdev_test_fops,
- };
- const int dynamic_minors = 256;
- int i;
- miscdev = kunit_kmalloc_array(test, dynamic_minors,
- sizeof(struct miscdevice),
- GFP_KERNEL | __GFP_ZERO);
- miscstat.minor = miscdev_test_ranges[0].minor;
- ret = misc_register(&miscstat);
- KUNIT_ASSERT_EQ(test, ret, 0);
- KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor);
- for (i = 0; i < dynamic_minors; i++) {
- miscdev[i].minor = MISC_DYNAMIC_MINOR;
- miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i);
- miscdev[i].fops = &miscdev_test_fops;
- ret = misc_register(&miscdev[i]);
- if (ret != 0)
- break;
- KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor));
- }
- for (i--; i >= 0; i--) {
- miscdev_test_can_open(test, &miscdev[i]);
- misc_deregister(&miscdev[i]);
- kfree_const(miscdev[i].name);
- }
- misc_deregister(&miscstat);
- KUNIT_EXPECT_EQ(test, ret, 0);
- }
- static void __init miscdev_test_collision_reverse(struct kunit *test)
- {
- int ret;
- struct miscdevice *miscdev;
- struct miscdevice miscstat = {
- .name = "miscstat",
- .fops = &miscdev_test_fops,
- };
- const int dynamic_minors = 256;
- int i;
- miscdev = kunit_kmalloc_array(test, dynamic_minors,
- sizeof(struct miscdevice),
- GFP_KERNEL | __GFP_ZERO);
- for (i = 0; i < dynamic_minors; i++) {
- miscdev[i].minor = MISC_DYNAMIC_MINOR;
- miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i);
- miscdev[i].fops = &miscdev_test_fops;
- ret = misc_register(&miscdev[i]);
- if (ret != 0)
- break;
- KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor));
- }
- KUNIT_EXPECT_EQ(test, ret, 0);
- miscstat.minor = miscdev_test_ranges[0].minor;
- ret = misc_register(&miscstat);
- KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor);
- if (ret == 0)
- misc_deregister(&miscstat);
- for (i--; i >= 0; i--) {
- miscdev_test_can_open(test, &miscdev[i]);
- misc_deregister(&miscdev[i]);
- kfree_const(miscdev[i].name);
- }
- }
- static void __init miscdev_test_conflict(struct kunit *test)
- {
- int ret;
- struct miscdevice miscdyn = {
- .name = "miscdyn",
- .minor = MISC_DYNAMIC_MINOR,
- .fops = &miscdev_test_fops,
- };
- struct miscdevice miscstat = {
- .name = "miscstat",
- .fops = &miscdev_test_fops,
- };
- ret = misc_register(&miscdyn);
- KUNIT_ASSERT_EQ(test, ret, 0);
- KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
- /*
- * Try to register a static minor with the same minor as the
- * dynamic one.
- */
- miscstat.minor = miscdyn.minor;
- ret = misc_register(&miscstat);
- KUNIT_EXPECT_EQ(test, ret, -EINVAL);
- if (ret == 0)
- misc_deregister(&miscstat);
- miscdev_test_can_open(test, &miscdyn);
- misc_deregister(&miscdyn);
- }
- static void __init miscdev_test_conflict_reverse(struct kunit *test)
- {
- int ret;
- struct miscdevice miscdyn = {
- .name = "miscdyn",
- .minor = MISC_DYNAMIC_MINOR,
- .fops = &miscdev_test_fops,
- };
- struct miscdevice miscstat = {
- .name = "miscstat",
- .fops = &miscdev_test_fops,
- };
- /*
- * Find the first available dynamic minor to use it as a static
- * minor later on.
- */
- ret = misc_register(&miscdyn);
- KUNIT_ASSERT_EQ(test, ret, 0);
- KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
- miscstat.minor = miscdyn.minor;
- misc_deregister(&miscdyn);
- ret = misc_register(&miscstat);
- KUNIT_EXPECT_EQ(test, ret, -EINVAL);
- if (ret == 0)
- misc_deregister(&miscstat);
- /*
- * Try to register a dynamic minor after registering a static minor
- * within the dynamic range. It should work but get a different
- * minor.
- */
- miscdyn.minor = MISC_DYNAMIC_MINOR;
- ret = misc_register(&miscdyn);
- KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_EQ(test, miscdyn.minor, miscstat.minor);
- KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
- if (ret == 0)
- misc_deregister(&miscdyn);
- }
- /* Take minor(> MISC_DYNAMIC_MINOR) as invalid when register miscdevice */
- static void miscdev_test_invalid_input(struct kunit *test)
- {
- struct miscdevice misc_test = {
- .minor = MISC_DYNAMIC_MINOR + 1,
- .name = "misc_test",
- .fops = &miscdev_test_fops,
- };
- int ret;
- ret = misc_register(&misc_test);
- KUNIT_EXPECT_EQ(test, ret, -EINVAL);
- if (ret == 0)
- misc_deregister(&misc_test);
- }
- /*
- * Verify if @miscdyn_a can still be registered successfully without
- * reinitialization even if its minor ever owned was requested by
- * another miscdevice such as @miscdyn_b.
- */
- static void miscdev_test_dynamic_reentry(struct kunit *test)
- {
- struct miscdevice miscdyn_a = {
- .name = "miscdyn_a",
- .minor = MISC_DYNAMIC_MINOR,
- .fops = &miscdev_test_fops,
- };
- struct miscdevice miscdyn_b = {
- .name = "miscdyn_b",
- .minor = MISC_DYNAMIC_MINOR,
- .fops = &miscdev_test_fops,
- };
- int ret, minor_a;
- ret = misc_register(&miscdyn_a);
- KUNIT_ASSERT_EQ(test, ret, 0);
- KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn_a.minor));
- minor_a = miscdyn_a.minor;
- if (ret != 0)
- return;
- misc_deregister(&miscdyn_a);
- ret = misc_register(&miscdyn_b);
- KUNIT_ASSERT_EQ(test, ret, 0);
- KUNIT_EXPECT_EQ(test, miscdyn_b.minor, minor_a);
- if (ret != 0)
- return;
- ret = misc_register(&miscdyn_a);
- KUNIT_ASSERT_EQ(test, ret, 0);
- KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn_a.minor));
- KUNIT_EXPECT_NE(test, miscdyn_a.minor, miscdyn_b.minor);
- if (ret == 0)
- misc_deregister(&miscdyn_a);
- misc_deregister(&miscdyn_b);
- }
- static struct kunit_case test_cases[] = {
- KUNIT_CASE(kunit_static_minor),
- KUNIT_CASE(kunit_misc_dynamic_minor),
- KUNIT_CASE(miscdev_test_invalid_input),
- KUNIT_CASE_PARAM(miscdev_test_twice, miscdev_gen_params),
- KUNIT_CASE_PARAM(miscdev_test_duplicate_minor, miscdev_gen_params),
- KUNIT_CASE(miscdev_test_duplicate_name),
- KUNIT_CASE(miscdev_test_duplicate_name_leak),
- KUNIT_CASE_PARAM(miscdev_test_duplicate_error, miscdev_gen_params),
- KUNIT_CASE(miscdev_test_dynamic_reentry),
- {}
- };
- static struct kunit_suite test_suite = {
- .name = "miscdev",
- .suite_init = miscdev_find_minors,
- .test_cases = test_cases,
- };
- kunit_test_suite(test_suite);
- static struct kunit_case __refdata test_init_cases[] = {
- KUNIT_CASE_PARAM(miscdev_test_static_basic, miscdev_gen_params),
- KUNIT_CASE(miscdev_test_dynamic_basic),
- KUNIT_CASE(miscdev_test_dynamic_only_range),
- KUNIT_CASE(miscdev_test_collision),
- KUNIT_CASE(miscdev_test_collision_reverse),
- KUNIT_CASE(miscdev_test_conflict),
- KUNIT_CASE(miscdev_test_conflict_reverse),
- {}
- };
- static struct kunit_suite test_init_suite = {
- .name = "miscdev_init",
- .suite_init = miscdev_find_minors,
- .test_cases = test_init_cases,
- };
- kunit_test_init_section_suite(test_init_suite);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Vimal Agrawal");
- MODULE_AUTHOR("Thadeu Lima de Souza Cascardo <cascardo@igalia.com>");
- MODULE_DESCRIPTION("Test module for misc character devices");
|