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