xref: /linux/lib/test_sysctl.c (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce)
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