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 */ 16 static 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 */ 66 static 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 */ 112 static 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 */ 145 static 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 */ 181 static 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 */ 212 static 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 */ 241 static 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 */ 272 static 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 */ 303 static 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 */ 341 static 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 370 static 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 384 static struct kunit_suite sysctl_test_suite = { 385 .name = "sysctl_test", 386 .test_cases = sysctl_test_cases, 387 }; 388 389 kunit_test_suites(&sysctl_test_suite); 390 391 MODULE_LICENSE("GPL v2"); 392