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