xref: /linux/lib/test_sysctl.c (revision 777740779ec5bd05eac85d9bbbb6b11457cfd238)
16cad1ecdSLuis Chamberlain // SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1
29308f2f9SLuis R. Rodriguez /*
39308f2f9SLuis R. Rodriguez  * proc sysctl test driver
49308f2f9SLuis R. Rodriguez  *
59308f2f9SLuis R. Rodriguez  * Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org>
69308f2f9SLuis R. Rodriguez  */
79308f2f9SLuis R. Rodriguez 
89308f2f9SLuis R. Rodriguez /*
92d046981SRandy Dunlap  * This module provides an interface to the proc sysctl interfaces.  This
109308f2f9SLuis R. Rodriguez  * driver requires CONFIG_PROC_SYSCTL. It will not normally be loaded by the
119308f2f9SLuis R. Rodriguez  * system unless explicitly requested by name. You can also build this driver
129308f2f9SLuis R. Rodriguez  * into your kernel.
139308f2f9SLuis R. Rodriguez  */
149308f2f9SLuis R. Rodriguez 
159308f2f9SLuis R. Rodriguez #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
169308f2f9SLuis R. Rodriguez 
179308f2f9SLuis R. Rodriguez #include <linux/init.h>
189308f2f9SLuis R. Rodriguez #include <linux/list.h>
199308f2f9SLuis R. Rodriguez #include <linux/module.h>
209308f2f9SLuis R. Rodriguez #include <linux/printk.h>
219308f2f9SLuis R. Rodriguez #include <linux/fs.h>
229308f2f9SLuis R. Rodriguez #include <linux/miscdevice.h>
239308f2f9SLuis R. Rodriguez #include <linux/slab.h>
249308f2f9SLuis R. Rodriguez #include <linux/uaccess.h>
259308f2f9SLuis R. Rodriguez #include <linux/async.h>
269308f2f9SLuis R. Rodriguez #include <linux/delay.h>
279308f2f9SLuis R. Rodriguez #include <linux/vmalloc.h>
289308f2f9SLuis R. Rodriguez 
299308f2f9SLuis R. Rodriguez static int i_zero;
309308f2f9SLuis R. Rodriguez static int i_one_hundred = 100;
3157b19468STonghao Zhang static int match_int_ok = 1;
329308f2f9SLuis R. Rodriguez 
33f2e7a626SJoel Granados 
34f2e7a626SJoel Granados static struct {
35f2e7a626SJoel Granados 	struct ctl_table_header *test_h_setup_node;
36f2e7a626SJoel Granados 	struct ctl_table_header *test_h_mnt;
37f2e7a626SJoel Granados 	struct ctl_table_header *test_h_mnterror;
38*77774077SJoel Granados 	struct ctl_table_header *empty_add;
39*77774077SJoel Granados 	struct ctl_table_header *empty;
40f2e7a626SJoel Granados } sysctl_test_headers;
41f2e7a626SJoel Granados 
429308f2f9SLuis R. Rodriguez struct test_sysctl_data {
439308f2f9SLuis R. Rodriguez 	int int_0001;
44eb965edaSLuis R. Rodriguez 	int int_0002;
457c43a657SLuis R. Rodriguez 	int int_0003[4];
46eb965edaSLuis R. Rodriguez 
474f2f682dSVlastimil Babka 	int boot_int;
484f2f682dSVlastimil Babka 
492920fad3SLuis R. Rodriguez 	unsigned int uint_0001;
502920fad3SLuis R. Rodriguez 
519308f2f9SLuis R. Rodriguez 	char string_0001[65];
522ea622b8SEric Sandeen 
532ea622b8SEric Sandeen #define SYSCTL_TEST_BITMAP_SIZE	65536
542ea622b8SEric Sandeen 	unsigned long *bitmap_0001;
559308f2f9SLuis R. Rodriguez };
569308f2f9SLuis R. Rodriguez 
579308f2f9SLuis R. Rodriguez static struct test_sysctl_data test_data = {
589308f2f9SLuis R. Rodriguez 	.int_0001 = 60,
59eb965edaSLuis R. Rodriguez 	.int_0002 = 1,
60eb965edaSLuis R. Rodriguez 
617c43a657SLuis R. Rodriguez 	.int_0003[0] = 0,
627c43a657SLuis R. Rodriguez 	.int_0003[1] = 1,
637c43a657SLuis R. Rodriguez 	.int_0003[2] = 2,
647c43a657SLuis R. Rodriguez 	.int_0003[3] = 3,
657c43a657SLuis R. Rodriguez 
664f2f682dSVlastimil Babka 	.boot_int = 0,
674f2f682dSVlastimil Babka 
682920fad3SLuis R. Rodriguez 	.uint_0001 = 314,
692920fad3SLuis R. Rodriguez 
709308f2f9SLuis R. Rodriguez 	.string_0001 = "(none)",
719308f2f9SLuis R. Rodriguez };
729308f2f9SLuis R. Rodriguez 
739308f2f9SLuis R. Rodriguez /* These are all under /proc/sys/debug/test_sysctl/ */
749308f2f9SLuis R. Rodriguez static struct ctl_table test_table[] = {
759308f2f9SLuis R. Rodriguez 	{
769308f2f9SLuis R. Rodriguez 		.procname	= "int_0001",
779308f2f9SLuis R. Rodriguez 		.data		= &test_data.int_0001,
789308f2f9SLuis R. Rodriguez 		.maxlen		= sizeof(int),
799308f2f9SLuis R. Rodriguez 		.mode		= 0644,
809308f2f9SLuis R. Rodriguez 		.proc_handler	= proc_dointvec_minmax,
819308f2f9SLuis R. Rodriguez 		.extra1		= &i_zero,
829308f2f9SLuis R. Rodriguez 		.extra2         = &i_one_hundred,
839308f2f9SLuis R. Rodriguez 	},
849308f2f9SLuis R. Rodriguez 	{
85eb965edaSLuis R. Rodriguez 		.procname	= "int_0002",
86eb965edaSLuis R. Rodriguez 		.data		= &test_data.int_0002,
87eb965edaSLuis R. Rodriguez 		.maxlen		= sizeof(int),
88eb965edaSLuis R. Rodriguez 		.mode		= 0644,
89eb965edaSLuis R. Rodriguez 		.proc_handler	= proc_dointvec,
90eb965edaSLuis R. Rodriguez 	},
91eb965edaSLuis R. Rodriguez 	{
927c43a657SLuis R. Rodriguez 		.procname	= "int_0003",
937c43a657SLuis R. Rodriguez 		.data		= &test_data.int_0003,
947c43a657SLuis R. Rodriguez 		.maxlen		= sizeof(test_data.int_0003),
957c43a657SLuis R. Rodriguez 		.mode		= 0644,
967c43a657SLuis R. Rodriguez 		.proc_handler	= proc_dointvec,
977c43a657SLuis R. Rodriguez 	},
987c43a657SLuis R. Rodriguez 	{
9957b19468STonghao Zhang 		.procname	= "match_int",
10057b19468STonghao Zhang 		.data		= &match_int_ok,
10157b19468STonghao Zhang 		.maxlen		= sizeof(match_int_ok),
10257b19468STonghao Zhang 		.mode		= 0444,
10357b19468STonghao Zhang 		.proc_handler	= proc_dointvec,
10457b19468STonghao Zhang 	},
10557b19468STonghao Zhang 	{
1064f2f682dSVlastimil Babka 		.procname	= "boot_int",
1074f2f682dSVlastimil Babka 		.data		= &test_data.boot_int,
1084f2f682dSVlastimil Babka 		.maxlen		= sizeof(test_data.boot_int),
1094f2f682dSVlastimil Babka 		.mode		= 0644,
1104f2f682dSVlastimil Babka 		.proc_handler	= proc_dointvec,
1114f2f682dSVlastimil Babka 		.extra1		= SYSCTL_ZERO,
1124f2f682dSVlastimil Babka 		.extra2         = SYSCTL_ONE,
1134f2f682dSVlastimil Babka 	},
1144f2f682dSVlastimil Babka 	{
1152920fad3SLuis R. Rodriguez 		.procname	= "uint_0001",
1162920fad3SLuis R. Rodriguez 		.data		= &test_data.uint_0001,
1172920fad3SLuis R. Rodriguez 		.maxlen		= sizeof(unsigned int),
1182920fad3SLuis R. Rodriguez 		.mode		= 0644,
1192920fad3SLuis R. Rodriguez 		.proc_handler	= proc_douintvec,
1202920fad3SLuis R. Rodriguez 	},
1212920fad3SLuis R. Rodriguez 	{
1229308f2f9SLuis R. Rodriguez 		.procname	= "string_0001",
1239308f2f9SLuis R. Rodriguez 		.data		= &test_data.string_0001,
1249308f2f9SLuis R. Rodriguez 		.maxlen		= sizeof(test_data.string_0001),
1259308f2f9SLuis R. Rodriguez 		.mode		= 0644,
1269308f2f9SLuis R. Rodriguez 		.proc_handler	= proc_dostring,
1279308f2f9SLuis R. Rodriguez 	},
1282ea622b8SEric Sandeen 	{
1292ea622b8SEric Sandeen 		.procname	= "bitmap_0001",
1302ea622b8SEric Sandeen 		.data		= &test_data.bitmap_0001,
1312ea622b8SEric Sandeen 		.maxlen		= SYSCTL_TEST_BITMAP_SIZE,
1322ea622b8SEric Sandeen 		.mode		= 0644,
1332ea622b8SEric Sandeen 		.proc_handler	= proc_do_large_bitmap,
1342ea622b8SEric Sandeen 	},
1359308f2f9SLuis R. Rodriguez 	{ }
1369308f2f9SLuis R. Rodriguez };
1379308f2f9SLuis R. Rodriguez 
138e009bd5eSJoel Granados static void test_sysctl_calc_match_int_ok(void)
1399308f2f9SLuis R. Rodriguez {
14057b19468STonghao Zhang 	int i;
14157b19468STonghao Zhang 
14257b19468STonghao Zhang 	struct {
14357b19468STonghao Zhang 		int defined;
14457b19468STonghao Zhang 		int wanted;
14557b19468STonghao Zhang 	} match_int[] = {
14657b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_ZERO,	.wanted = 0},
14757b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_ONE,		.wanted = 1},
14857b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_TWO,		.wanted = 2},
14957b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_THREE,	.wanted = 3},
15057b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_FOUR,	.wanted = 4},
15157b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_ONE_HUNDRED, .wanted = 100},
15257b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_TWO_HUNDRED,	.wanted = 200},
15357b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_ONE_THOUSAND, .wanted = 1000},
15457b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_THREE_THOUSAND, .wanted = 3000},
15557b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_INT_MAX,	.wanted = INT_MAX},
15657b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_MAXOLDUID,	.wanted = 65535},
15757b19468STonghao Zhang 		{.defined = *(int *)SYSCTL_NEG_ONE,	.wanted = -1},
15857b19468STonghao Zhang 	};
15957b19468STonghao Zhang 
16057b19468STonghao Zhang 	for (i = 0; i < ARRAY_SIZE(match_int); i++)
16157b19468STonghao Zhang 		if (match_int[i].defined != match_int[i].wanted)
16257b19468STonghao Zhang 			match_int_ok = 0;
163e009bd5eSJoel Granados }
16457b19468STonghao Zhang 
165e009bd5eSJoel Granados static int test_sysctl_setup_node_tests(void)
166e009bd5eSJoel Granados {
167e009bd5eSJoel Granados 	test_sysctl_calc_match_int_ok();
1682ea622b8SEric Sandeen 	test_data.bitmap_0001 = kzalloc(SYSCTL_TEST_BITMAP_SIZE/8, GFP_KERNEL);
1692ea622b8SEric Sandeen 	if (!test_data.bitmap_0001)
1709308f2f9SLuis R. Rodriguez 		return -ENOMEM;
171f2e7a626SJoel Granados 	sysctl_test_headers.test_h_setup_node = register_sysctl("debug/test_sysctl", test_table);
172f2e7a626SJoel Granados 	if (!sysctl_test_headers.test_h_setup_node) {
1732ea622b8SEric Sandeen 		kfree(test_data.bitmap_0001);
1742ea622b8SEric Sandeen 		return -ENOMEM;
1752ea622b8SEric Sandeen 	}
176e009bd5eSJoel Granados 
1779308f2f9SLuis R. Rodriguez 	return 0;
1789308f2f9SLuis R. Rodriguez }
179e009bd5eSJoel Granados 
18035576438SJoel Granados /* Used to test that unregister actually removes the directory */
18135576438SJoel Granados static struct ctl_table test_table_unregister[] = {
18235576438SJoel Granados 	{
18335576438SJoel Granados 		.procname	= "unregister_error",
18435576438SJoel Granados 		.data		= &test_data.int_0001,
18535576438SJoel Granados 		.maxlen		= sizeof(int),
18635576438SJoel Granados 		.mode		= 0644,
18735576438SJoel Granados 		.proc_handler	= proc_dointvec_minmax,
18835576438SJoel Granados 	},
18935576438SJoel Granados 	{}
19035576438SJoel Granados };
19135576438SJoel Granados 
19235576438SJoel Granados static int test_sysctl_run_unregister_nested(void)
19335576438SJoel Granados {
19435576438SJoel Granados 	struct ctl_table_header *unregister;
19535576438SJoel Granados 
19635576438SJoel Granados 	unregister = register_sysctl("debug/test_sysctl/unregister_error",
19735576438SJoel Granados 				   test_table_unregister);
19835576438SJoel Granados 	if (!unregister)
19935576438SJoel Granados 		return -ENOMEM;
20035576438SJoel Granados 
20135576438SJoel Granados 	unregister_sysctl_table(unregister);
20235576438SJoel Granados 	return 0;
20335576438SJoel Granados }
20435576438SJoel Granados 
205f2e7a626SJoel Granados static int test_sysctl_run_register_mount_point(void)
206f2e7a626SJoel Granados {
207f2e7a626SJoel Granados 	sysctl_test_headers.test_h_mnt
208f2e7a626SJoel Granados 		= register_sysctl_mount_point("debug/test_sysctl/mnt");
209f2e7a626SJoel Granados 	if (!sysctl_test_headers.test_h_mnt)
210f2e7a626SJoel Granados 		return -ENOMEM;
211f2e7a626SJoel Granados 
212f2e7a626SJoel Granados 	sysctl_test_headers.test_h_mnterror
213f2e7a626SJoel Granados 		= register_sysctl("debug/test_sysctl/mnt/mnt_error",
214f2e7a626SJoel Granados 				  test_table_unregister);
215f2e7a626SJoel Granados 	/*
216f2e7a626SJoel Granados 	 * Don't check the result.:
217f2e7a626SJoel Granados 	 * If it fails (expected behavior), return 0.
218f2e7a626SJoel Granados 	 * If successful (missbehavior of register mount point), we want to see
219f2e7a626SJoel Granados 	 * mnt_error when we run the sysctl test script
220f2e7a626SJoel Granados 	 */
221f2e7a626SJoel Granados 
222f2e7a626SJoel Granados 	return 0;
223f2e7a626SJoel Granados }
224f2e7a626SJoel Granados 
225*77774077SJoel Granados static struct ctl_table test_table_empty[] = { };
226*77774077SJoel Granados 
227*77774077SJoel Granados static int test_sysctl_run_register_empty(void)
228*77774077SJoel Granados {
229*77774077SJoel Granados 	/* Tets that an empty dir can be created */
230*77774077SJoel Granados 	sysctl_test_headers.empty_add
231*77774077SJoel Granados 		= register_sysctl("debug/test_sysctl/empty_add", test_table_empty);
232*77774077SJoel Granados 	if (!sysctl_test_headers.empty_add)
233*77774077SJoel Granados 		return -ENOMEM;
234*77774077SJoel Granados 
235*77774077SJoel Granados 	/* Test that register on top of an empty dir works */
236*77774077SJoel Granados 	sysctl_test_headers.empty
237*77774077SJoel Granados 		= register_sysctl("debug/test_sysctl/empty_add/empty", test_table_empty);
238*77774077SJoel Granados 	if (!sysctl_test_headers.empty)
239*77774077SJoel Granados 		return -ENOMEM;
240*77774077SJoel Granados 
241*77774077SJoel Granados 	return 0;
242*77774077SJoel Granados }
243*77774077SJoel Granados 
244e009bd5eSJoel Granados static int __init test_sysctl_init(void)
245e009bd5eSJoel Granados {
246e009bd5eSJoel Granados 	int err;
247e009bd5eSJoel Granados 
248e009bd5eSJoel Granados 	err = test_sysctl_setup_node_tests();
24935576438SJoel Granados 	if (err)
25035576438SJoel Granados 		goto out;
251e009bd5eSJoel Granados 
25235576438SJoel Granados 	err = test_sysctl_run_unregister_nested();
253f2e7a626SJoel Granados 	if (err)
254f2e7a626SJoel Granados 		goto out;
255f2e7a626SJoel Granados 
256f2e7a626SJoel Granados 	err = test_sysctl_run_register_mount_point();
257*77774077SJoel Granados 	if (err)
258*77774077SJoel Granados 		goto out;
259*77774077SJoel Granados 
260*77774077SJoel Granados 	err = test_sysctl_run_register_empty();
26135576438SJoel Granados 
26235576438SJoel Granados out:
263e009bd5eSJoel Granados 	return err;
264e009bd5eSJoel Granados }
2652f56f845SMasami Hiramatsu module_init(test_sysctl_init);
2669308f2f9SLuis R. Rodriguez 
2679308f2f9SLuis R. Rodriguez static void __exit test_sysctl_exit(void)
2689308f2f9SLuis R. Rodriguez {
2692ea622b8SEric Sandeen 	kfree(test_data.bitmap_0001);
270f2e7a626SJoel Granados 	if (sysctl_test_headers.test_h_setup_node)
271f2e7a626SJoel Granados 		unregister_sysctl_table(sysctl_test_headers.test_h_setup_node);
272f2e7a626SJoel Granados 	if (sysctl_test_headers.test_h_mnt)
273f2e7a626SJoel Granados 		unregister_sysctl_table(sysctl_test_headers.test_h_mnt);
274f2e7a626SJoel Granados 	if (sysctl_test_headers.test_h_mnterror)
275f2e7a626SJoel Granados 		unregister_sysctl_table(sysctl_test_headers.test_h_mnterror);
276*77774077SJoel Granados 	if (sysctl_test_headers.empty)
277*77774077SJoel Granados 		unregister_sysctl_table(sysctl_test_headers.empty);
278*77774077SJoel Granados 	if (sysctl_test_headers.empty_add)
279*77774077SJoel Granados 		unregister_sysctl_table(sysctl_test_headers.empty_add);
2809308f2f9SLuis R. Rodriguez }
2819308f2f9SLuis R. Rodriguez 
2829308f2f9SLuis R. Rodriguez module_exit(test_sysctl_exit);
2839308f2f9SLuis R. Rodriguez 
2849308f2f9SLuis R. Rodriguez MODULE_AUTHOR("Luis R. Rodriguez <mcgrof@kernel.org>");
2859308f2f9SLuis R. Rodriguez MODULE_LICENSE("GPL");
286