1 // SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1 2 /* 3 * proc sysctl test driver 4 * 5 * Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org> 6 */ 7 8 /* 9 * This module provides an interface to the proc sysctl interfaces. This 10 * driver requires CONFIG_SYSCTL. It will not normally be loaded by the 11 * system unless explicitly requested by name. You can also build this driver 12 * into your kernel. 13 */ 14 15 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 16 17 #include <linux/init.h> 18 #include <linux/jiffies.h> 19 #include <linux/list.h> 20 #include <linux/module.h> 21 #include <linux/printk.h> 22 #include <linux/fs.h> 23 #include <linux/miscdevice.h> 24 #include <linux/slab.h> 25 #include <linux/uaccess.h> 26 #include <linux/async.h> 27 #include <linux/delay.h> 28 #include <linux/vmalloc.h> 29 30 static int i_zero; 31 static int i_one_hundred = 100; 32 static int match_int_ok = 1; 33 34 enum { 35 TEST_H_SETUP_NODE, 36 TEST_H_MNT, 37 TEST_H_MNTERROR, 38 TEST_H_EMPTY_ADD, 39 TEST_H_EMPTY, 40 TEST_H_U8, 41 TEST_H_SIZE /* Always at the end */ 42 }; 43 44 static struct ctl_table_header *ctl_headers[TEST_H_SIZE] = {}; 45 struct test_sysctl_data { 46 int int_0001; 47 int int_0002; 48 int int_0003[4]; 49 50 int boot_int; 51 52 int int_jiffies; 53 int int_ms_jiffies; 54 int int_userhz_jiffies; 55 56 unsigned int uint_0001; 57 58 char string_0001[65]; 59 60 #define SYSCTL_TEST_BITMAP_SIZE 65536 61 unsigned long *bitmap_0001; 62 }; 63 64 static struct test_sysctl_data test_data = { 65 .int_0001 = 60, 66 .int_0002 = 1, 67 68 .int_0003[0] = 0, 69 .int_0003[1] = 1, 70 .int_0003[2] = 2, 71 .int_0003[3] = 3, 72 73 .boot_int = 0, 74 75 /* One second in each converter's unit; all three store HZ jiffies. */ 76 .int_jiffies = HZ, 77 .int_ms_jiffies = HZ, 78 .int_userhz_jiffies = HZ, 79 80 .uint_0001 = 314, 81 82 .string_0001 = "(none)", 83 }; 84 85 /* These are all under /proc/sys/debug/test_sysctl/ */ 86 static const struct ctl_table test_table[] = { 87 { 88 .procname = "int_0001", 89 .data = &test_data.int_0001, 90 .maxlen = sizeof(int), 91 .mode = 0644, 92 .proc_handler = proc_dointvec_minmax, 93 .extra1 = &i_zero, 94 .extra2 = &i_one_hundred, 95 }, 96 { 97 .procname = "int_0002", 98 .data = &test_data.int_0002, 99 .maxlen = sizeof(int), 100 .mode = 0644, 101 .proc_handler = proc_dointvec, 102 }, 103 { 104 .procname = "int_0003", 105 .data = &test_data.int_0003, 106 .maxlen = sizeof(test_data.int_0003), 107 .mode = 0644, 108 .proc_handler = proc_dointvec, 109 }, 110 { 111 .procname = "match_int", 112 .data = &match_int_ok, 113 .maxlen = sizeof(match_int_ok), 114 .mode = 0444, 115 .proc_handler = proc_dointvec, 116 }, 117 { 118 .procname = "boot_int", 119 .data = &test_data.boot_int, 120 .maxlen = sizeof(test_data.boot_int), 121 .mode = 0644, 122 .proc_handler = proc_dointvec, 123 .extra1 = SYSCTL_ZERO, 124 .extra2 = SYSCTL_ONE, 125 }, 126 { 127 .procname = "int_jiffies", 128 .data = &test_data.int_jiffies, 129 .maxlen = sizeof(int), 130 .mode = 0644, 131 .proc_handler = proc_dointvec_jiffies, 132 }, 133 { 134 .procname = "int_ms_jiffies", 135 .data = &test_data.int_ms_jiffies, 136 .maxlen = sizeof(int), 137 .mode = 0644, 138 .proc_handler = proc_dointvec_ms_jiffies, 139 }, 140 { 141 .procname = "int_userhz_jiffies", 142 .data = &test_data.int_userhz_jiffies, 143 .maxlen = sizeof(int), 144 .mode = 0644, 145 .proc_handler = proc_dointvec_userhz_jiffies, 146 }, 147 { 148 .procname = "uint_0001", 149 .data = &test_data.uint_0001, 150 .maxlen = sizeof(unsigned int), 151 .mode = 0644, 152 .proc_handler = proc_douintvec, 153 }, 154 { 155 .procname = "string_0001", 156 .data = &test_data.string_0001, 157 .maxlen = sizeof(test_data.string_0001), 158 .mode = 0644, 159 .proc_handler = proc_dostring, 160 }, 161 { 162 .procname = "bitmap_0001", 163 .data = &test_data.bitmap_0001, 164 .maxlen = SYSCTL_TEST_BITMAP_SIZE, 165 .mode = 0644, 166 .proc_handler = proc_do_large_bitmap, 167 }, 168 }; 169 170 static void test_sysctl_calc_match_int_ok(void) 171 { 172 int i; 173 174 struct { 175 int defined; 176 int wanted; 177 } match_int[] = { 178 {.defined = *(int *)SYSCTL_ZERO, .wanted = 0}, 179 {.defined = *(int *)SYSCTL_ONE, .wanted = 1}, 180 {.defined = *(int *)SYSCTL_TWO, .wanted = 2}, 181 {.defined = *(int *)SYSCTL_THREE, .wanted = 3}, 182 {.defined = *(int *)SYSCTL_FOUR, .wanted = 4}, 183 {.defined = *(int *)SYSCTL_ONE_HUNDRED, .wanted = 100}, 184 {.defined = *(int *)SYSCTL_TWO_HUNDRED, .wanted = 200}, 185 {.defined = *(int *)SYSCTL_ONE_THOUSAND, .wanted = 1000}, 186 {.defined = *(int *)SYSCTL_THREE_THOUSAND, .wanted = 3000}, 187 {.defined = *(int *)SYSCTL_INT_MAX, .wanted = INT_MAX}, 188 {.defined = *(int *)SYSCTL_MAXOLDUID, .wanted = 65535}, 189 {.defined = *(int *)SYSCTL_NEG_ONE, .wanted = -1}, 190 }; 191 192 for (i = 0; i < ARRAY_SIZE(match_int); i++) 193 if (match_int[i].defined != match_int[i].wanted) 194 match_int_ok = 0; 195 } 196 197 static int test_sysctl_setup_node_tests(void) 198 { 199 test_sysctl_calc_match_int_ok(); 200 test_data.bitmap_0001 = kzalloc(SYSCTL_TEST_BITMAP_SIZE/8, GFP_KERNEL); 201 if (!test_data.bitmap_0001) 202 return -ENOMEM; 203 ctl_headers[TEST_H_SETUP_NODE] = register_sysctl("debug/test_sysctl", test_table); 204 if (!ctl_headers[TEST_H_SETUP_NODE]) { 205 kfree(test_data.bitmap_0001); 206 return -ENOMEM; 207 } 208 209 return 0; 210 } 211 212 /* Used to test that unregister actually removes the directory */ 213 static const struct ctl_table test_table_unregister[] = { 214 { 215 .procname = "unregister_error", 216 .data = &test_data.int_0001, 217 .maxlen = sizeof(int), 218 .mode = 0644, 219 .proc_handler = proc_dointvec_minmax, 220 }, 221 }; 222 223 static int test_sysctl_run_unregister_nested(void) 224 { 225 struct ctl_table_header *unregister; 226 227 unregister = register_sysctl("debug/test_sysctl/unregister_error", 228 test_table_unregister); 229 if (!unregister) 230 return -ENOMEM; 231 232 unregister_sysctl_table(unregister); 233 return 0; 234 } 235 236 static int test_sysctl_run_register_mount_point(void) 237 { 238 ctl_headers[TEST_H_MNT] 239 = register_sysctl_mount_point("debug/test_sysctl/mnt"); 240 if (!ctl_headers[TEST_H_MNT]) 241 return -ENOMEM; 242 243 ctl_headers[TEST_H_MNTERROR] 244 = register_sysctl("debug/test_sysctl/mnt/mnt_error", 245 test_table_unregister); 246 /* 247 * Don't check the result.: 248 * If it fails (expected behavior), return 0. 249 * If successful (missbehavior of register mount point), we want to see 250 * mnt_error when we run the sysctl test script 251 */ 252 253 return 0; 254 } 255 256 static const struct ctl_table test_table_empty[] = { }; 257 258 static int test_sysctl_run_register_empty(void) 259 { 260 /* Tets that an empty dir can be created */ 261 ctl_headers[TEST_H_EMPTY_ADD] 262 = register_sysctl("debug/test_sysctl/empty_add", test_table_empty); 263 if (!ctl_headers[TEST_H_EMPTY_ADD]) 264 return -ENOMEM; 265 266 /* Test that register on top of an empty dir works */ 267 ctl_headers[TEST_H_EMPTY] 268 = register_sysctl("debug/test_sysctl/empty_add/empty", test_table_empty); 269 if (!ctl_headers[TEST_H_EMPTY]) 270 return -ENOMEM; 271 272 return 0; 273 } 274 275 static const struct ctl_table table_u8_over[] = { 276 { 277 .procname = "u8_over", 278 .data = &test_data.uint_0001, 279 .maxlen = sizeof(u8), 280 .mode = 0644, 281 .proc_handler = proc_dou8vec_minmax, 282 .extra1 = SYSCTL_FOUR, 283 .extra2 = SYSCTL_ONE_THOUSAND, 284 }, 285 }; 286 287 static const struct ctl_table table_u8_under[] = { 288 { 289 .procname = "u8_under", 290 .data = &test_data.uint_0001, 291 .maxlen = sizeof(u8), 292 .mode = 0644, 293 .proc_handler = proc_dou8vec_minmax, 294 .extra1 = SYSCTL_NEG_ONE, 295 .extra2 = SYSCTL_ONE_HUNDRED, 296 }, 297 }; 298 299 static const struct ctl_table table_u8_valid[] = { 300 { 301 .procname = "u8_valid", 302 .data = &test_data.uint_0001, 303 .maxlen = sizeof(u8), 304 .mode = 0644, 305 .proc_handler = proc_dou8vec_minmax, 306 .extra1 = SYSCTL_ZERO, 307 .extra2 = SYSCTL_TWO_HUNDRED, 308 }, 309 }; 310 311 static int test_sysctl_register_u8_extra(void) 312 { 313 /* should fail because it's over */ 314 ctl_headers[TEST_H_U8] 315 = register_sysctl("debug/test_sysctl", table_u8_over); 316 if (ctl_headers[TEST_H_U8]) 317 return -ENOMEM; 318 319 /* should fail because it's under */ 320 ctl_headers[TEST_H_U8] 321 = register_sysctl("debug/test_sysctl", table_u8_under); 322 if (ctl_headers[TEST_H_U8]) 323 return -ENOMEM; 324 325 /* should not fail because it's valid */ 326 ctl_headers[TEST_H_U8] 327 = register_sysctl("debug/test_sysctl", table_u8_valid); 328 if (!ctl_headers[TEST_H_U8]) 329 return -ENOMEM; 330 331 return 0; 332 } 333 334 static int __init test_sysctl_init(void) 335 { 336 int err = 0; 337 338 int (*func_array[])(void) = { 339 test_sysctl_setup_node_tests, 340 test_sysctl_run_unregister_nested, 341 test_sysctl_run_register_mount_point, 342 test_sysctl_run_register_empty, 343 test_sysctl_register_u8_extra 344 }; 345 346 for (int i = 0; !err && i < ARRAY_SIZE(func_array); i++) 347 err = func_array[i](); 348 349 return err; 350 } 351 module_init(test_sysctl_init); 352 353 static void __exit test_sysctl_exit(void) 354 { 355 for (int i = 0; i < TEST_H_SIZE; i++) { 356 if (ctl_headers[i]) 357 unregister_sysctl_table(ctl_headers[i]); 358 } 359 kfree(test_data.bitmap_0001); 360 } 361 362 module_exit(test_sysctl_exit); 363 364 MODULE_AUTHOR("Luis R. Rodriguez <mcgrof@kernel.org>"); 365 MODULE_DESCRIPTION("proc sysctl test driver"); 366 MODULE_LICENSE("GPL"); 367