linux/kernel/sysctl-test.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * KUnit test of proc sysctl.
   4 */
   5
   6#include <kunit/test.h>
   7#include <linux/sysctl.h>
   8
   9#define KUNIT_PROC_READ 0
  10#define KUNIT_PROC_WRITE 1
  11
  12static int i_zero;
  13static int i_one_hundred = 100;
  14
  15/*
  16 * Test that proc_dointvec will not try to use a NULL .data field even when the
  17 * length is non-zero.
  18 */
  19static void sysctl_test_api_dointvec_null_tbl_data(struct kunit *test)
  20{
  21        struct ctl_table null_data_table = {
  22                .procname = "foo",
  23                /*
  24                 * Here we are testing that proc_dointvec behaves correctly when
  25                 * we give it a NULL .data field. Normally this would point to a
  26                 * piece of memory where the value would be stored.
  27                 */
  28                .data           = NULL,
  29                .maxlen         = sizeof(int),
  30                .mode           = 0644,
  31                .proc_handler   = proc_dointvec,
  32                .extra1         = &i_zero,
  33                .extra2         = &i_one_hundred,
  34        };
  35        /*
  36         * proc_dointvec expects a buffer in user space, so we allocate one. We
  37         * also need to cast it to __user so sparse doesn't get mad.
  38         */
  39        void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
  40                                                           GFP_USER);
  41        size_t len;
  42        loff_t pos;
  43
  44        /*
  45         * We don't care what the starting length is since proc_dointvec should
  46         * not try to read because .data is NULL.
  47         */
  48        len = 1234;
  49        KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&null_data_table,
  50                                               KUNIT_PROC_READ, buffer, &len,
  51                                               &pos));
  52        KUNIT_EXPECT_EQ(test, 0, len);
  53
  54        /*
  55         * See above.
  56         */
  57        len = 1234;
  58        KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&null_data_table,
  59                                               KUNIT_PROC_WRITE, buffer, &len,
  60                                               &pos));
  61        KUNIT_EXPECT_EQ(test, 0, len);
  62}
  63
  64/*
  65 * Similar to the previous test, we create a struct ctrl_table that has a .data
  66 * field that proc_dointvec cannot do anything with; however, this time it is
  67 * because we tell proc_dointvec that the size is 0.
  68 */
  69static void sysctl_test_api_dointvec_table_maxlen_unset(struct kunit *test)
  70{
  71        int data = 0;
  72        struct ctl_table data_maxlen_unset_table = {
  73                .procname = "foo",
  74                .data           = &data,
  75                /*
  76                 * So .data is no longer NULL, but we tell proc_dointvec its
  77                 * length is 0, so it still shouldn't try to use it.
  78                 */
  79                .maxlen         = 0,
  80                .mode           = 0644,
  81                .proc_handler   = proc_dointvec,
  82                .extra1         = &i_zero,
  83                .extra2         = &i_one_hundred,
  84        };
  85        void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
  86                                                           GFP_USER);
  87        size_t len;
  88        loff_t pos;
  89
  90        /*
  91         * As before, we don't care what buffer length is because proc_dointvec
  92         * cannot do anything because its internal .data buffer has zero length.
  93         */
  94        len = 1234;
  95        KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&data_maxlen_unset_table,
  96                                               KUNIT_PROC_READ, buffer, &len,
  97                                               &pos));
  98        KUNIT_EXPECT_EQ(test, 0, len);
  99
 100        /*
 101         * See previous comment.
 102         */
 103        len = 1234;
 104        KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&data_maxlen_unset_table,
 105                                               KUNIT_PROC_WRITE, buffer, &len,
 106                                               &pos));
 107        KUNIT_EXPECT_EQ(test, 0, len);
 108}
 109
 110/*
 111 * Here we provide a valid struct ctl_table, but we try to read and write from
 112 * it using a buffer of zero length, so it should still fail in a similar way as
 113 * before.
 114 */
 115static void sysctl_test_api_dointvec_table_len_is_zero(struct kunit *test)
 116{
 117        int data = 0;
 118        /* Good table. */
 119        struct ctl_table table = {
 120                .procname = "foo",
 121                .data           = &data,
 122                .maxlen         = sizeof(int),
 123                .mode           = 0644,
 124                .proc_handler   = proc_dointvec,
 125                .extra1         = &i_zero,
 126                .extra2         = &i_one_hundred,
 127        };
 128        void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
 129                                                           GFP_USER);
 130        /*
 131         * However, now our read/write buffer has zero length.
 132         */
 133        size_t len = 0;
 134        loff_t pos;
 135
 136        KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ, buffer,
 137                                               &len, &pos));
 138        KUNIT_EXPECT_EQ(test, 0, len);
 139
 140        KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE, buffer,
 141                                               &len, &pos));
 142        KUNIT_EXPECT_EQ(test, 0, len);
 143}
 144
 145/*
 146 * Test that proc_dointvec refuses to read when the file position is non-zero.
 147 */
 148static void sysctl_test_api_dointvec_table_read_but_position_set(
 149                struct kunit *test)
 150{
 151        int data = 0;
 152        /* Good table. */
 153        struct ctl_table table = {
 154                .procname = "foo",
 155                .data           = &data,
 156                .maxlen         = sizeof(int),
 157                .mode           = 0644,
 158                .proc_handler   = proc_dointvec,
 159                .extra1         = &i_zero,
 160                .extra2         = &i_one_hundred,
 161        };
 162        void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
 163                                                           GFP_USER);
 164        /*
 165         * We don't care about our buffer length because we start off with a
 166         * non-zero file position.
 167         */
 168        size_t len = 1234;
 169        /*
 170         * proc_dointvec should refuse to read into the buffer since the file
 171         * pos is non-zero.
 172         */
 173        loff_t pos = 1;
 174
 175        KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ, buffer,
 176                                               &len, &pos));
 177        KUNIT_EXPECT_EQ(test, 0, len);
 178}
 179
 180/*
 181 * Test that we can read a two digit number in a sufficiently size buffer.
 182 * Nothing fancy.
 183 */
 184static void sysctl_test_dointvec_read_happy_single_positive(struct kunit *test)
 185{
 186        int data = 0;
 187        /* Good table. */
 188        struct ctl_table table = {
 189                .procname = "foo",
 190                .data           = &data,
 191                .maxlen         = sizeof(int),
 192                .mode           = 0644,
 193                .proc_handler   = proc_dointvec,
 194                .extra1         = &i_zero,
 195                .extra2         = &i_one_hundred,
 196        };
 197        size_t len = 4;
 198        loff_t pos = 0;
 199        char *buffer = kunit_kzalloc(test, len, GFP_USER);
 200        char __user *user_buffer = (char __user *)buffer;
 201        /* Store 13 in the data field. */
 202        *((int *)table.data) = 13;
 203
 204        KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ,
 205                                               user_buffer, &len, &pos));
 206        KUNIT_ASSERT_EQ(test, 3, len);
 207        buffer[len] = '\0';
 208        /* And we read 13 back out. */
 209        KUNIT_EXPECT_STREQ(test, "13\n", buffer);
 210}
 211
 212/*
 213 * Same as previous test, just now with negative numbers.
 214 */
 215static void sysctl_test_dointvec_read_happy_single_negative(struct kunit *test)
 216{
 217        int data = 0;
 218        /* Good table. */
 219        struct ctl_table table = {
 220                .procname = "foo",
 221                .data           = &data,
 222                .maxlen         = sizeof(int),
 223                .mode           = 0644,
 224                .proc_handler   = proc_dointvec,
 225                .extra1         = &i_zero,
 226                .extra2         = &i_one_hundred,
 227        };
 228        size_t len = 5;
 229        loff_t pos = 0;
 230        char *buffer = kunit_kzalloc(test, len, GFP_USER);
 231        char __user *user_buffer = (char __user *)buffer;
 232        *((int *)table.data) = -16;
 233
 234        KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ,
 235                                               user_buffer, &len, &pos));
 236        KUNIT_ASSERT_EQ(test, 4, len);
 237        buffer[len] = '\0';
 238        KUNIT_EXPECT_STREQ(test, "-16\n", buffer);
 239}
 240
 241/*
 242 * Test that a simple positive write works.
 243 */
 244static void sysctl_test_dointvec_write_happy_single_positive(struct kunit *test)
 245{
 246        int data = 0;
 247        /* Good table. */
 248        struct ctl_table table = {
 249                .procname = "foo",
 250                .data           = &data,
 251                .maxlen         = sizeof(int),
 252                .mode           = 0644,
 253                .proc_handler   = proc_dointvec,
 254                .extra1         = &i_zero,
 255                .extra2         = &i_one_hundred,
 256        };
 257        char input[] = "9";
 258        size_t len = sizeof(input) - 1;
 259        loff_t pos = 0;
 260        char *buffer = kunit_kzalloc(test, len, GFP_USER);
 261        char __user *user_buffer = (char __user *)buffer;
 262
 263        memcpy(buffer, input, len);
 264
 265        KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE,
 266                                               user_buffer, &len, &pos));
 267        KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len);
 268        KUNIT_EXPECT_EQ(test, sizeof(input) - 1, pos);
 269        KUNIT_EXPECT_EQ(test, 9, *((int *)table.data));
 270}
 271
 272/*
 273 * Same as previous test, but now with negative numbers.
 274 */
 275static void sysctl_test_dointvec_write_happy_single_negative(struct kunit *test)
 276{
 277        int data = 0;
 278        struct ctl_table table = {
 279                .procname = "foo",
 280                .data           = &data,
 281                .maxlen         = sizeof(int),
 282                .mode           = 0644,
 283                .proc_handler   = proc_dointvec,
 284                .extra1         = &i_zero,
 285                .extra2         = &i_one_hundred,
 286        };
 287        char input[] = "-9";
 288        size_t len = sizeof(input) - 1;
 289        loff_t pos = 0;
 290        char *buffer = kunit_kzalloc(test, len, GFP_USER);
 291        char __user *user_buffer = (char __user *)buffer;
 292
 293        memcpy(buffer, input, len);
 294
 295        KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE,
 296                                               user_buffer, &len, &pos));
 297        KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len);
 298        KUNIT_EXPECT_EQ(test, sizeof(input) - 1, pos);
 299        KUNIT_EXPECT_EQ(test, -9, *((int *)table.data));
 300}
 301
 302/*
 303 * Test that writing a value smaller than the minimum possible value is not
 304 * allowed.
 305 */
 306static void sysctl_test_api_dointvec_write_single_less_int_min(
 307                struct kunit *test)
 308{
 309        int data = 0;
 310        struct ctl_table table = {
 311                .procname = "foo",
 312                .data           = &data,
 313                .maxlen         = sizeof(int),
 314                .mode           = 0644,
 315                .proc_handler   = proc_dointvec,
 316                .extra1         = &i_zero,
 317                .extra2         = &i_one_hundred,
 318        };
 319        size_t max_len = 32, len = max_len;
 320        loff_t pos = 0;
 321        char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
 322        char __user *user_buffer = (char __user *)buffer;
 323        unsigned long abs_of_less_than_min = (unsigned long)INT_MAX
 324                                             - (INT_MAX + INT_MIN) + 1;
 325
 326        /*
 327         * We use this rigmarole to create a string that contains a value one
 328         * less than the minimum accepted value.
 329         */
 330        KUNIT_ASSERT_LT(test,
 331                        (size_t)snprintf(buffer, max_len, "-%lu",
 332                                         abs_of_less_than_min),
 333                        max_len);
 334
 335        KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec(&table, KUNIT_PROC_WRITE,
 336                                                     user_buffer, &len, &pos));
 337        KUNIT_EXPECT_EQ(test, max_len, len);
 338        KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
 339}
 340
 341/*
 342 * Test that writing the maximum possible value works.
 343 */
 344static void sysctl_test_api_dointvec_write_single_greater_int_max(
 345                struct kunit *test)
 346{
 347        int data = 0;
 348        struct ctl_table table = {
 349                .procname = "foo",
 350                .data           = &data,
 351                .maxlen         = sizeof(int),
 352                .mode           = 0644,
 353                .proc_handler   = proc_dointvec,
 354                .extra1         = &i_zero,
 355                .extra2         = &i_one_hundred,
 356        };
 357        size_t max_len = 32, len = max_len;
 358        loff_t pos = 0;
 359        char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
 360        char __user *user_buffer = (char __user *)buffer;
 361        unsigned long greater_than_max = (unsigned long)INT_MAX + 1;
 362
 363        KUNIT_ASSERT_GT(test, greater_than_max, (unsigned long)INT_MAX);
 364        KUNIT_ASSERT_LT(test, (size_t)snprintf(buffer, max_len, "%lu",
 365                                               greater_than_max),
 366                        max_len);
 367        KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec(&table, KUNIT_PROC_WRITE,
 368                                                     user_buffer, &len, &pos));
 369        KUNIT_ASSERT_EQ(test, max_len, len);
 370        KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
 371}
 372
 373static struct kunit_case sysctl_test_cases[] = {
 374        KUNIT_CASE(sysctl_test_api_dointvec_null_tbl_data),
 375        KUNIT_CASE(sysctl_test_api_dointvec_table_maxlen_unset),
 376        KUNIT_CASE(sysctl_test_api_dointvec_table_len_is_zero),
 377        KUNIT_CASE(sysctl_test_api_dointvec_table_read_but_position_set),
 378        KUNIT_CASE(sysctl_test_dointvec_read_happy_single_positive),
 379        KUNIT_CASE(sysctl_test_dointvec_read_happy_single_negative),
 380        KUNIT_CASE(sysctl_test_dointvec_write_happy_single_positive),
 381        KUNIT_CASE(sysctl_test_dointvec_write_happy_single_negative),
 382        KUNIT_CASE(sysctl_test_api_dointvec_write_single_less_int_min),
 383        KUNIT_CASE(sysctl_test_api_dointvec_write_single_greater_int_max),
 384        {}
 385};
 386
 387static struct kunit_suite sysctl_test_suite = {
 388        .name = "sysctl_test",
 389        .test_cases = sysctl_test_cases,
 390};
 391
 392kunit_test_suites(&sysctl_test_suite);
 393
 394MODULE_LICENSE("GPL v2");
 395