| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * KUnit test of proc sysctl. |
| */ |
| |
| #include <kunit/test.h> |
| #include <linux/printk.h> |
| #include <linux/sysctl.h> |
| #include <linux/uaccess.h> |
| |
| static int i_zero; |
| static int i_one_hundred = 100; |
| |
| struct test_sysctl_data { |
| int int_0001; |
| int int_0002; |
| int int_0003[4]; |
| |
| unsigned int uint_0001; |
| |
| char string_0001[65]; |
| }; |
| |
| static struct test_sysctl_data test_data = { |
| .int_0001 = 60, |
| .int_0002 = 1, |
| |
| .int_0003[0] = 0, |
| .int_0003[1] = 1, |
| .int_0003[2] = 2, |
| .int_0003[3] = 3, |
| |
| .uint_0001 = 314, |
| |
| .string_0001 = "(none)", |
| }; |
| |
| static void sysctl_test_dointvec_null_tbl_data(struct kunit *test) |
| { |
| struct ctl_table table = { |
| .procname = "foo", |
| .data = NULL, |
| .maxlen = sizeof(int), |
| .mode = 0644, |
| .proc_handler = proc_dointvec, |
| .extra1 = &i_zero, |
| .extra2 = &i_one_hundred, |
| }; |
| void *buffer = kunit_kzalloc(test, sizeof(int), GFP_USER); |
| size_t len; |
| loff_t pos; |
| |
| len = 1234; |
| KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 0, buffer, &len, &pos)); |
| KUNIT_EXPECT_EQ(test, 0, len); |
| len = 1234; |
| KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 1, buffer, &len, &pos)); |
| KUNIT_EXPECT_EQ(test, 0, len); |
| } |
| |
| static void sysctl_test_dointvec_table_maxlen_unset(struct kunit *test) |
| { |
| struct ctl_table table = { |
| .procname = "foo", |
| .data = &test_data.int_0001, |
| .maxlen = 0, |
| .mode = 0644, |
| .proc_handler = proc_dointvec, |
| .extra1 = &i_zero, |
| .extra2 = &i_one_hundred, |
| }; |
| void *buffer = kunit_kzalloc(test, sizeof(int), GFP_USER); |
| size_t len; |
| loff_t pos; |
| |
| len = 1234; |
| KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 0, buffer, &len, &pos)); |
| KUNIT_EXPECT_EQ(test, 0, len); |
| len = 1234; |
| KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 1, buffer, &len, &pos)); |
| KUNIT_EXPECT_EQ(test, 0, len); |
| } |
| |
| static void sysctl_test_dointvec_table_len_is_zero(struct kunit *test) |
| { |
| struct ctl_table table = { |
| .procname = "foo", |
| .data = &test_data.int_0001, |
| .maxlen = sizeof(int), |
| .mode = 0644, |
| .proc_handler = proc_dointvec, |
| .extra1 = &i_zero, |
| .extra2 = &i_one_hundred, |
| }; |
| void *buffer = kunit_kzalloc(test, sizeof(int), GFP_USER); |
| size_t len; |
| loff_t pos; |
| |
| len = 0; |
| KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 0, buffer, &len, &pos)); |
| KUNIT_EXPECT_EQ(test, 0, len); |
| KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 1, buffer, &len, &pos)); |
| KUNIT_EXPECT_EQ(test, 0, len); |
| } |
| |
| static void sysctl_test_dointvec_table_read_but_position_set(struct kunit *test) |
| { |
| struct ctl_table table = { |
| .procname = "foo", |
| .data = &test_data.int_0001, |
| .maxlen = sizeof(int), |
| .mode = 0644, |
| .proc_handler = proc_dointvec, |
| .extra1 = &i_zero, |
| .extra2 = &i_one_hundred, |
| }; |
| void *buffer = kunit_kzalloc(test, sizeof(int), GFP_USER); |
| size_t len; |
| loff_t pos; |
| |
| len = 1234; |
| pos = 1; |
| KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 0, buffer, &len, &pos)); |
| KUNIT_EXPECT_EQ(test, 0, len); |
| } |
| |
| static void sysctl_test_dointvec_happy_single_positive(struct kunit *test) |
| { |
| struct ctl_table table = { |
| .procname = "foo", |
| .data = &test_data.int_0001, |
| .maxlen = sizeof(int), |
| .mode = 0644, |
| .proc_handler = proc_dointvec, |
| .extra1 = &i_zero, |
| .extra2 = &i_one_hundred, |
| }; |
| char input[] = "9"; |
| size_t len = sizeof(input) - 1; |
| loff_t pos = 0; |
| |
| table.data = kunit_kzalloc(test, sizeof(int), GFP_USER); |
| KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 1, input, &len, &pos)); |
| KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len); |
| KUNIT_EXPECT_EQ(test, sizeof(input) - 1, pos); |
| KUNIT_EXPECT_EQ(test, 9, *(int *)table.data); |
| } |
| |
| static void sysctl_test_dointvec_happy_single_negative(struct kunit *test) |
| { |
| struct ctl_table table = { |
| .procname = "foo", |
| .data = &test_data.int_0001, |
| .maxlen = sizeof(int), |
| .mode = 0644, |
| .proc_handler = proc_dointvec, |
| .extra1 = &i_zero, |
| .extra2 = &i_one_hundred, |
| }; |
| char input[] = "-9"; |
| size_t len = sizeof(input) - 1; |
| loff_t pos = 0; |
| |
| table.data = kunit_kzalloc(test, sizeof(int), GFP_USER); |
| KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 1, input, &len, &pos)); |
| KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len); |
| KUNIT_EXPECT_EQ(test, sizeof(input) - 1, pos); |
| KUNIT_EXPECT_EQ(test, -9, *(int *)table.data); |
| } |
| |
| static void sysctl_test_dointvec_single_less_int_min(struct kunit *test) |
| { |
| struct ctl_table table = { |
| .procname = "foo", |
| .data = &test_data.int_0001, |
| .maxlen = sizeof(int), |
| .mode = 0644, |
| .proc_handler = proc_dointvec, |
| .extra1 = &i_zero, |
| .extra2 = &i_one_hundred, |
| }; |
| char input[32]; |
| size_t len = sizeof(input) - 1; |
| loff_t pos = 0; |
| unsigned long abs_of_less_than_min = (unsigned long)INT_MAX |
| - (INT_MAX + INT_MIN) + 1; |
| |
| KUNIT_EXPECT_LT(test, |
| snprintf(input, sizeof(input), "-%lu", |
| abs_of_less_than_min), |
| sizeof(input)); |
| |
| table.data = kunit_kzalloc(test, sizeof(int), GFP_USER); |
| KUNIT_EXPECT_EQ(test, -EINVAL, |
| proc_dointvec(&table, 1, input, &len, &pos)); |
| KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len); |
| KUNIT_EXPECT_EQ(test, 0, *(int *)table.data); |
| } |
| |
| static void sysctl_test_dointvec_single_greater_int_max(struct kunit *test) |
| { |
| struct ctl_table table = { |
| .procname = "foo", |
| .data = &test_data.int_0001, |
| .maxlen = sizeof(int), |
| .mode = 0644, |
| .proc_handler = proc_dointvec, |
| .extra1 = &i_zero, |
| .extra2 = &i_one_hundred, |
| }; |
| char input[32]; |
| size_t len = sizeof(input) - 1; |
| loff_t pos = 0; |
| unsigned long greater_than_max = (unsigned long)INT_MAX + 1; |
| |
| KUNIT_EXPECT_GT(test, greater_than_max, INT_MAX); |
| KUNIT_EXPECT_LT(test, snprintf(input, sizeof(input), "%lu", |
| greater_than_max), |
| sizeof(input)); |
| table.data = kunit_kzalloc(test, sizeof(int), GFP_USER); |
| KUNIT_EXPECT_EQ(test, -EINVAL, |
| proc_dointvec(&table, 1, input, &len, &pos)); |
| KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len); |
| KUNIT_EXPECT_EQ(test, 0, *(int *)table.data); |
| } |
| |
| static int sysctl_test_init(struct kunit *test) |
| { |
| return 0; |
| } |
| |
| /* |
| * This is run once after each test case, see the comment on example_test_module |
| * for more information. |
| */ |
| static void sysctl_test_exit(struct kunit *test) |
| { |
| } |
| |
| /* |
| * Here we make a list of all the test cases we want to add to the test module |
| * below. |
| */ |
| static struct kunit_case sysctl_test_cases[] = { |
| /* |
| * This is a helper to create a test case object from a test case |
| * function; its exact function is not important to understand how to |
| * use KUnit, just know that this is how you associate test cases with a |
| * test module. |
| */ |
| KUNIT_CASE(sysctl_test_dointvec_null_tbl_data), |
| KUNIT_CASE(sysctl_test_dointvec_table_maxlen_unset), |
| KUNIT_CASE(sysctl_test_dointvec_table_len_is_zero), |
| KUNIT_CASE(sysctl_test_dointvec_table_read_but_position_set), |
| KUNIT_CASE(sysctl_test_dointvec_happy_single_positive), |
| KUNIT_CASE(sysctl_test_dointvec_happy_single_negative), |
| KUNIT_CASE(sysctl_test_dointvec_single_less_int_min), |
| KUNIT_CASE(sysctl_test_dointvec_single_greater_int_max), |
| {}, |
| }; |
| |
| /* |
| * This defines a suite or grouping of tests. |
| * |
| * Test cases are defined as belonging to the suite by adding them to |
| * `test_cases`. |
| * |
| * Often it is desirable to run some function which will set up things which |
| * will be used by every test; this is accomplished with an `init` function |
| * which runs before each test case is invoked. Similarly, an `exit` function |
| * may be specified which runs after every test case and can be used to for |
| * cleanup. For clarity, running tests in a test module would behave as follows: |
| * |
| * module.init(test); |
| * module.test_case[0](test); |
| * module.exit(test); |
| * module.init(test); |
| * module.test_case[1](test); |
| * module.exit(test); |
| * ...; |
| */ |
| static struct kunit_module sysctl_test_module = { |
| .name = "sysctl_test", |
| .init = sysctl_test_init, |
| .exit = sysctl_test_exit, |
| .test_cases = sysctl_test_cases, |
| }; |
| |
| /* |
| * This registers the above test module telling KUnit that this is a suite of |
| * tests that need to be run. |
| */ |
| module_test(sysctl_test_module); |