Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * KUnit test of proc sysctl.
0004  */
0005 
0006 #include <kunit/test.h>
0007 #include <linux/sysctl.h>
0008 
0009 #define KUNIT_PROC_READ 0
0010 #define KUNIT_PROC_WRITE 1
0011 
0012 static int i_zero;
0013 static int i_one_hundred = 100;
0014 
0015 /*
0016  * Test that proc_dointvec will not try to use a NULL .data field even when the
0017  * length is non-zero.
0018  */
0019 static void sysctl_test_api_dointvec_null_tbl_data(struct kunit *test)
0020 {
0021     struct ctl_table null_data_table = {
0022         .procname = "foo",
0023         /*
0024          * Here we are testing that proc_dointvec behaves correctly when
0025          * we give it a NULL .data field. Normally this would point to a
0026          * piece of memory where the value would be stored.
0027          */
0028         .data       = NULL,
0029         .maxlen     = sizeof(int),
0030         .mode       = 0644,
0031         .proc_handler   = proc_dointvec,
0032         .extra1     = &i_zero,
0033         .extra2         = &i_one_hundred,
0034     };
0035     /*
0036      * proc_dointvec expects a buffer in user space, so we allocate one. We
0037      * also need to cast it to __user so sparse doesn't get mad.
0038      */
0039     void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
0040                                GFP_USER);
0041     size_t len;
0042     loff_t pos;
0043 
0044     /*
0045      * We don't care what the starting length is since proc_dointvec should
0046      * not try to read because .data is NULL.
0047      */
0048     len = 1234;
0049     KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&null_data_table,
0050                            KUNIT_PROC_READ, buffer, &len,
0051                            &pos));
0052     KUNIT_EXPECT_EQ(test, 0, len);
0053 
0054     /*
0055      * See above.
0056      */
0057     len = 1234;
0058     KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&null_data_table,
0059                            KUNIT_PROC_WRITE, buffer, &len,
0060                            &pos));
0061     KUNIT_EXPECT_EQ(test, 0, len);
0062 }
0063 
0064 /*
0065  * Similar to the previous test, we create a struct ctrl_table that has a .data
0066  * field that proc_dointvec cannot do anything with; however, this time it is
0067  * because we tell proc_dointvec that the size is 0.
0068  */
0069 static void sysctl_test_api_dointvec_table_maxlen_unset(struct kunit *test)
0070 {
0071     int data = 0;
0072     struct ctl_table data_maxlen_unset_table = {
0073         .procname = "foo",
0074         .data       = &data,
0075         /*
0076          * So .data is no longer NULL, but we tell proc_dointvec its
0077          * length is 0, so it still shouldn't try to use it.
0078          */
0079         .maxlen     = 0,
0080         .mode       = 0644,
0081         .proc_handler   = proc_dointvec,
0082         .extra1     = &i_zero,
0083         .extra2         = &i_one_hundred,
0084     };
0085     void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
0086                                GFP_USER);
0087     size_t len;
0088     loff_t pos;
0089 
0090     /*
0091      * As before, we don't care what buffer length is because proc_dointvec
0092      * cannot do anything because its internal .data buffer has zero length.
0093      */
0094     len = 1234;
0095     KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&data_maxlen_unset_table,
0096                            KUNIT_PROC_READ, buffer, &len,
0097                            &pos));
0098     KUNIT_EXPECT_EQ(test, 0, len);
0099 
0100     /*
0101      * See previous comment.
0102      */
0103     len = 1234;
0104     KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&data_maxlen_unset_table,
0105                            KUNIT_PROC_WRITE, buffer, &len,
0106                            &pos));
0107     KUNIT_EXPECT_EQ(test, 0, len);
0108 }
0109 
0110 /*
0111  * Here we provide a valid struct ctl_table, but we try to read and write from
0112  * it using a buffer of zero length, so it should still fail in a similar way as
0113  * before.
0114  */
0115 static void sysctl_test_api_dointvec_table_len_is_zero(struct kunit *test)
0116 {
0117     int data = 0;
0118     /* Good table. */
0119     struct ctl_table table = {
0120         .procname = "foo",
0121         .data       = &data,
0122         .maxlen     = sizeof(int),
0123         .mode       = 0644,
0124         .proc_handler   = proc_dointvec,
0125         .extra1     = &i_zero,
0126         .extra2         = &i_one_hundred,
0127     };
0128     void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
0129                                GFP_USER);
0130     /*
0131      * However, now our read/write buffer has zero length.
0132      */
0133     size_t len = 0;
0134     loff_t pos;
0135 
0136     KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ, buffer,
0137                            &len, &pos));
0138     KUNIT_EXPECT_EQ(test, 0, len);
0139 
0140     KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE, buffer,
0141                            &len, &pos));
0142     KUNIT_EXPECT_EQ(test, 0, len);
0143 }
0144 
0145 /*
0146  * Test that proc_dointvec refuses to read when the file position is non-zero.
0147  */
0148 static void sysctl_test_api_dointvec_table_read_but_position_set(
0149         struct kunit *test)
0150 {
0151     int data = 0;
0152     /* Good table. */
0153     struct ctl_table table = {
0154         .procname = "foo",
0155         .data       = &data,
0156         .maxlen     = sizeof(int),
0157         .mode       = 0644,
0158         .proc_handler   = proc_dointvec,
0159         .extra1     = &i_zero,
0160         .extra2         = &i_one_hundred,
0161     };
0162     void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
0163                                GFP_USER);
0164     /*
0165      * We don't care about our buffer length because we start off with a
0166      * non-zero file position.
0167      */
0168     size_t len = 1234;
0169     /*
0170      * proc_dointvec should refuse to read into the buffer since the file
0171      * pos is non-zero.
0172      */
0173     loff_t pos = 1;
0174 
0175     KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ, buffer,
0176                            &len, &pos));
0177     KUNIT_EXPECT_EQ(test, 0, len);
0178 }
0179 
0180 /*
0181  * Test that we can read a two digit number in a sufficiently size buffer.
0182  * Nothing fancy.
0183  */
0184 static void sysctl_test_dointvec_read_happy_single_positive(struct kunit *test)
0185 {
0186     int data = 0;
0187     /* Good table. */
0188     struct ctl_table table = {
0189         .procname = "foo",
0190         .data       = &data,
0191         .maxlen     = sizeof(int),
0192         .mode       = 0644,
0193         .proc_handler   = proc_dointvec,
0194         .extra1     = &i_zero,
0195         .extra2         = &i_one_hundred,
0196     };
0197     size_t len = 4;
0198     loff_t pos = 0;
0199     char *buffer = kunit_kzalloc(test, len, GFP_USER);
0200     char __user *user_buffer = (char __user *)buffer;
0201     /* Store 13 in the data field. */
0202     *((int *)table.data) = 13;
0203 
0204     KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ,
0205                            user_buffer, &len, &pos));
0206     KUNIT_ASSERT_EQ(test, 3, len);
0207     buffer[len] = '\0';
0208     /* And we read 13 back out. */
0209     KUNIT_EXPECT_STREQ(test, "13\n", buffer);
0210 }
0211 
0212 /*
0213  * Same as previous test, just now with negative numbers.
0214  */
0215 static void sysctl_test_dointvec_read_happy_single_negative(struct kunit *test)
0216 {
0217     int data = 0;
0218     /* Good table. */
0219     struct ctl_table table = {
0220         .procname = "foo",
0221         .data       = &data,
0222         .maxlen     = sizeof(int),
0223         .mode       = 0644,
0224         .proc_handler   = proc_dointvec,
0225         .extra1     = &i_zero,
0226         .extra2         = &i_one_hundred,
0227     };
0228     size_t len = 5;
0229     loff_t pos = 0;
0230     char *buffer = kunit_kzalloc(test, len, GFP_USER);
0231     char __user *user_buffer = (char __user *)buffer;
0232     *((int *)table.data) = -16;
0233 
0234     KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ,
0235                            user_buffer, &len, &pos));
0236     KUNIT_ASSERT_EQ(test, 4, len);
0237     buffer[len] = '\0';
0238     KUNIT_EXPECT_STREQ(test, "-16\n", buffer);
0239 }
0240 
0241 /*
0242  * Test that a simple positive write works.
0243  */
0244 static void sysctl_test_dointvec_write_happy_single_positive(struct kunit *test)
0245 {
0246     int data = 0;
0247     /* Good table. */
0248     struct ctl_table table = {
0249         .procname = "foo",
0250         .data       = &data,
0251         .maxlen     = sizeof(int),
0252         .mode       = 0644,
0253         .proc_handler   = proc_dointvec,
0254         .extra1     = &i_zero,
0255         .extra2         = &i_one_hundred,
0256     };
0257     char input[] = "9";
0258     size_t len = sizeof(input) - 1;
0259     loff_t pos = 0;
0260     char *buffer = kunit_kzalloc(test, len, GFP_USER);
0261     char __user *user_buffer = (char __user *)buffer;
0262 
0263     memcpy(buffer, input, len);
0264 
0265     KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE,
0266                            user_buffer, &len, &pos));
0267     KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len);
0268     KUNIT_EXPECT_EQ(test, sizeof(input) - 1, pos);
0269     KUNIT_EXPECT_EQ(test, 9, *((int *)table.data));
0270 }
0271 
0272 /*
0273  * Same as previous test, but now with negative numbers.
0274  */
0275 static void sysctl_test_dointvec_write_happy_single_negative(struct kunit *test)
0276 {
0277     int data = 0;
0278     struct ctl_table table = {
0279         .procname = "foo",
0280         .data       = &data,
0281         .maxlen     = sizeof(int),
0282         .mode       = 0644,
0283         .proc_handler   = proc_dointvec,
0284         .extra1     = &i_zero,
0285         .extra2         = &i_one_hundred,
0286     };
0287     char input[] = "-9";
0288     size_t len = sizeof(input) - 1;
0289     loff_t pos = 0;
0290     char *buffer = kunit_kzalloc(test, len, GFP_USER);
0291     char __user *user_buffer = (char __user *)buffer;
0292 
0293     memcpy(buffer, input, len);
0294 
0295     KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE,
0296                            user_buffer, &len, &pos));
0297     KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len);
0298     KUNIT_EXPECT_EQ(test, sizeof(input) - 1, pos);
0299     KUNIT_EXPECT_EQ(test, -9, *((int *)table.data));
0300 }
0301 
0302 /*
0303  * Test that writing a value smaller than the minimum possible value is not
0304  * allowed.
0305  */
0306 static void sysctl_test_api_dointvec_write_single_less_int_min(
0307         struct kunit *test)
0308 {
0309     int data = 0;
0310     struct ctl_table table = {
0311         .procname = "foo",
0312         .data       = &data,
0313         .maxlen     = sizeof(int),
0314         .mode       = 0644,
0315         .proc_handler   = proc_dointvec,
0316         .extra1     = &i_zero,
0317         .extra2         = &i_one_hundred,
0318     };
0319     size_t max_len = 32, len = max_len;
0320     loff_t pos = 0;
0321     char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
0322     char __user *user_buffer = (char __user *)buffer;
0323     unsigned long abs_of_less_than_min = (unsigned long)INT_MAX
0324                          - (INT_MAX + INT_MIN) + 1;
0325 
0326     /*
0327      * We use this rigmarole to create a string that contains a value one
0328      * less than the minimum accepted value.
0329      */
0330     KUNIT_ASSERT_LT(test,
0331             (size_t)snprintf(buffer, max_len, "-%lu",
0332                      abs_of_less_than_min),
0333             max_len);
0334 
0335     KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec(&table, KUNIT_PROC_WRITE,
0336                              user_buffer, &len, &pos));
0337     KUNIT_EXPECT_EQ(test, max_len, len);
0338     KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
0339 }
0340 
0341 /*
0342  * Test that writing the maximum possible value works.
0343  */
0344 static void sysctl_test_api_dointvec_write_single_greater_int_max(
0345         struct kunit *test)
0346 {
0347     int data = 0;
0348     struct ctl_table table = {
0349         .procname = "foo",
0350         .data       = &data,
0351         .maxlen     = sizeof(int),
0352         .mode       = 0644,
0353         .proc_handler   = proc_dointvec,
0354         .extra1     = &i_zero,
0355         .extra2         = &i_one_hundred,
0356     };
0357     size_t max_len = 32, len = max_len;
0358     loff_t pos = 0;
0359     char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
0360     char __user *user_buffer = (char __user *)buffer;
0361     unsigned long greater_than_max = (unsigned long)INT_MAX + 1;
0362 
0363     KUNIT_ASSERT_GT(test, greater_than_max, (unsigned long)INT_MAX);
0364     KUNIT_ASSERT_LT(test, (size_t)snprintf(buffer, max_len, "%lu",
0365                            greater_than_max),
0366             max_len);
0367     KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec(&table, KUNIT_PROC_WRITE,
0368                              user_buffer, &len, &pos));
0369     KUNIT_ASSERT_EQ(test, max_len, len);
0370     KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
0371 }
0372 
0373 static struct kunit_case sysctl_test_cases[] = {
0374     KUNIT_CASE(sysctl_test_api_dointvec_null_tbl_data),
0375     KUNIT_CASE(sysctl_test_api_dointvec_table_maxlen_unset),
0376     KUNIT_CASE(sysctl_test_api_dointvec_table_len_is_zero),
0377     KUNIT_CASE(sysctl_test_api_dointvec_table_read_but_position_set),
0378     KUNIT_CASE(sysctl_test_dointvec_read_happy_single_positive),
0379     KUNIT_CASE(sysctl_test_dointvec_read_happy_single_negative),
0380     KUNIT_CASE(sysctl_test_dointvec_write_happy_single_positive),
0381     KUNIT_CASE(sysctl_test_dointvec_write_happy_single_negative),
0382     KUNIT_CASE(sysctl_test_api_dointvec_write_single_less_int_min),
0383     KUNIT_CASE(sysctl_test_api_dointvec_write_single_greater_int_max),
0384     {}
0385 };
0386 
0387 static struct kunit_suite sysctl_test_suite = {
0388     .name = "sysctl_test",
0389     .test_cases = sysctl_test_cases,
0390 };
0391 
0392 kunit_test_suites(&sysctl_test_suite);
0393 
0394 MODULE_LICENSE("GPL v2");