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 static int i_zero; 13 static 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 */ 19 static 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, (size_t)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, (size_t)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 */ 69 static 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, (size_t)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, (size_t)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 */ 115 static 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, (size_t)0, len); 139 140 KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE, buffer, 141 &len, &pos)); 142 KUNIT_EXPECT_EQ(test, (size_t)0, len); 143 } 144 145 /* 146 * Test that proc_dointvec refuses to read when the file position is non-zero. 147 */ 148 static 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, (size_t)0, len); 178 } 179 180 /* 181 * Test that we can read a two digit number in a sufficiently size buffer. 182 * Nothing fancy. 183 */ 184 static 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, (size_t)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 */ 215 static 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, (size_t)4, len); 237 buffer[len] = '\0'; 238 KUNIT_EXPECT_STREQ(test, "-16\n", (char *)buffer); 239 } 240 241 /* 242 * Test that a simple positive write works. 243 */ 244 static 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, (size_t)pos); 269 KUNIT_EXPECT_EQ(test, 9, *((int *)table.data)); 270 } 271 272 /* 273 * Same as previous test, but now with negative numbers. 274 */ 275 static 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, (size_t)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 */ 306 static 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 */ 344 static 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 373 static 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 387 static struct kunit_suite sysctl_test_suite = { 388 .name = "sysctl_test", 389 .test_cases = sysctl_test_cases, 390 }; 391 392 kunit_test_suites(&sysctl_test_suite); 393 394 MODULE_LICENSE("GPL v2"); 395