xref: /openbmc/linux/kernel/sysctl-test.c (revision 35267cea)
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, 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, 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, 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, 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, 0, len);
139 
140 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE, buffer,
141 					       &len, &pos));
142 	KUNIT_EXPECT_EQ(test, 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, 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, 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, 4, len);
237 	buffer[len] = '\0';
238 	KUNIT_EXPECT_STREQ(test, "-16\n", 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, 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, 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