xref: /linux/kernel/sysctl-test.c (revision 0526b56cbc3c489642bd6a5fe4b718dea7ef0ee8)
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 /*
13  * Test that proc_dointvec will not try to use a NULL .data field even when the
14  * length is non-zero.
15  */
16 static void sysctl_test_api_dointvec_null_tbl_data(struct kunit *test)
17 {
18 	struct ctl_table null_data_table = {
19 		.procname = "foo",
20 		/*
21 		 * Here we are testing that proc_dointvec behaves correctly when
22 		 * we give it a NULL .data field. Normally this would point to a
23 		 * piece of memory where the value would be stored.
24 		 */
25 		.data		= NULL,
26 		.maxlen		= sizeof(int),
27 		.mode		= 0644,
28 		.proc_handler	= proc_dointvec,
29 		.extra1		= SYSCTL_ZERO,
30 		.extra2         = SYSCTL_ONE_HUNDRED,
31 	};
32 	/*
33 	 * proc_dointvec expects a buffer in user space, so we allocate one. We
34 	 * also need to cast it to __user so sparse doesn't get mad.
35 	 */
36 	void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
37 							   GFP_USER);
38 	size_t len;
39 	loff_t pos;
40 
41 	/*
42 	 * We don't care what the starting length is since proc_dointvec should
43 	 * not try to read because .data is NULL.
44 	 */
45 	len = 1234;
46 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&null_data_table,
47 					       KUNIT_PROC_READ, buffer, &len,
48 					       &pos));
49 	KUNIT_EXPECT_EQ(test, 0, len);
50 
51 	/*
52 	 * See above.
53 	 */
54 	len = 1234;
55 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&null_data_table,
56 					       KUNIT_PROC_WRITE, buffer, &len,
57 					       &pos));
58 	KUNIT_EXPECT_EQ(test, 0, len);
59 }
60 
61 /*
62  * Similar to the previous test, we create a struct ctrl_table that has a .data
63  * field that proc_dointvec cannot do anything with; however, this time it is
64  * because we tell proc_dointvec that the size is 0.
65  */
66 static void sysctl_test_api_dointvec_table_maxlen_unset(struct kunit *test)
67 {
68 	int data = 0;
69 	struct ctl_table data_maxlen_unset_table = {
70 		.procname = "foo",
71 		.data		= &data,
72 		/*
73 		 * So .data is no longer NULL, but we tell proc_dointvec its
74 		 * length is 0, so it still shouldn't try to use it.
75 		 */
76 		.maxlen		= 0,
77 		.mode		= 0644,
78 		.proc_handler	= proc_dointvec,
79 		.extra1		= SYSCTL_ZERO,
80 		.extra2         = SYSCTL_ONE_HUNDRED,
81 	};
82 	void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
83 							   GFP_USER);
84 	size_t len;
85 	loff_t pos;
86 
87 	/*
88 	 * As before, we don't care what buffer length is because proc_dointvec
89 	 * cannot do anything because its internal .data buffer has zero length.
90 	 */
91 	len = 1234;
92 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&data_maxlen_unset_table,
93 					       KUNIT_PROC_READ, buffer, &len,
94 					       &pos));
95 	KUNIT_EXPECT_EQ(test, 0, len);
96 
97 	/*
98 	 * See previous comment.
99 	 */
100 	len = 1234;
101 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&data_maxlen_unset_table,
102 					       KUNIT_PROC_WRITE, buffer, &len,
103 					       &pos));
104 	KUNIT_EXPECT_EQ(test, 0, len);
105 }
106 
107 /*
108  * Here we provide a valid struct ctl_table, but we try to read and write from
109  * it using a buffer of zero length, so it should still fail in a similar way as
110  * before.
111  */
112 static void sysctl_test_api_dointvec_table_len_is_zero(struct kunit *test)
113 {
114 	int data = 0;
115 	/* Good table. */
116 	struct ctl_table table = {
117 		.procname = "foo",
118 		.data		= &data,
119 		.maxlen		= sizeof(int),
120 		.mode		= 0644,
121 		.proc_handler	= proc_dointvec,
122 		.extra1		= SYSCTL_ZERO,
123 		.extra2         = SYSCTL_ONE_HUNDRED,
124 	};
125 	void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
126 							   GFP_USER);
127 	/*
128 	 * However, now our read/write buffer has zero length.
129 	 */
130 	size_t len = 0;
131 	loff_t pos;
132 
133 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ, buffer,
134 					       &len, &pos));
135 	KUNIT_EXPECT_EQ(test, 0, len);
136 
137 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE, buffer,
138 					       &len, &pos));
139 	KUNIT_EXPECT_EQ(test, 0, len);
140 }
141 
142 /*
143  * Test that proc_dointvec refuses to read when the file position is non-zero.
144  */
145 static void sysctl_test_api_dointvec_table_read_but_position_set(
146 		struct kunit *test)
147 {
148 	int data = 0;
149 	/* Good table. */
150 	struct ctl_table table = {
151 		.procname = "foo",
152 		.data		= &data,
153 		.maxlen		= sizeof(int),
154 		.mode		= 0644,
155 		.proc_handler	= proc_dointvec,
156 		.extra1		= SYSCTL_ZERO,
157 		.extra2         = SYSCTL_ONE_HUNDRED,
158 	};
159 	void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
160 							   GFP_USER);
161 	/*
162 	 * We don't care about our buffer length because we start off with a
163 	 * non-zero file position.
164 	 */
165 	size_t len = 1234;
166 	/*
167 	 * proc_dointvec should refuse to read into the buffer since the file
168 	 * pos is non-zero.
169 	 */
170 	loff_t pos = 1;
171 
172 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ, buffer,
173 					       &len, &pos));
174 	KUNIT_EXPECT_EQ(test, 0, len);
175 }
176 
177 /*
178  * Test that we can read a two digit number in a sufficiently size buffer.
179  * Nothing fancy.
180  */
181 static void sysctl_test_dointvec_read_happy_single_positive(struct kunit *test)
182 {
183 	int data = 0;
184 	/* Good table. */
185 	struct ctl_table table = {
186 		.procname = "foo",
187 		.data		= &data,
188 		.maxlen		= sizeof(int),
189 		.mode		= 0644,
190 		.proc_handler	= proc_dointvec,
191 		.extra1		= SYSCTL_ZERO,
192 		.extra2         = SYSCTL_ONE_HUNDRED,
193 	};
194 	size_t len = 4;
195 	loff_t pos = 0;
196 	char *buffer = kunit_kzalloc(test, len, GFP_USER);
197 	char __user *user_buffer = (char __user *)buffer;
198 	/* Store 13 in the data field. */
199 	*((int *)table.data) = 13;
200 
201 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ,
202 					       user_buffer, &len, &pos));
203 	KUNIT_ASSERT_EQ(test, 3, len);
204 	buffer[len] = '\0';
205 	/* And we read 13 back out. */
206 	KUNIT_EXPECT_STREQ(test, "13\n", buffer);
207 }
208 
209 /*
210  * Same as previous test, just now with negative numbers.
211  */
212 static void sysctl_test_dointvec_read_happy_single_negative(struct kunit *test)
213 {
214 	int data = 0;
215 	/* Good table. */
216 	struct ctl_table table = {
217 		.procname = "foo",
218 		.data		= &data,
219 		.maxlen		= sizeof(int),
220 		.mode		= 0644,
221 		.proc_handler	= proc_dointvec,
222 		.extra1		= SYSCTL_ZERO,
223 		.extra2         = SYSCTL_ONE_HUNDRED,
224 	};
225 	size_t len = 5;
226 	loff_t pos = 0;
227 	char *buffer = kunit_kzalloc(test, len, GFP_USER);
228 	char __user *user_buffer = (char __user *)buffer;
229 	*((int *)table.data) = -16;
230 
231 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ,
232 					       user_buffer, &len, &pos));
233 	KUNIT_ASSERT_EQ(test, 4, len);
234 	buffer[len] = '\0';
235 	KUNIT_EXPECT_STREQ(test, "-16\n", buffer);
236 }
237 
238 /*
239  * Test that a simple positive write works.
240  */
241 static void sysctl_test_dointvec_write_happy_single_positive(struct kunit *test)
242 {
243 	int data = 0;
244 	/* Good table. */
245 	struct ctl_table table = {
246 		.procname = "foo",
247 		.data		= &data,
248 		.maxlen		= sizeof(int),
249 		.mode		= 0644,
250 		.proc_handler	= proc_dointvec,
251 		.extra1		= SYSCTL_ZERO,
252 		.extra2         = SYSCTL_ONE_HUNDRED,
253 	};
254 	char input[] = "9";
255 	size_t len = sizeof(input) - 1;
256 	loff_t pos = 0;
257 	char *buffer = kunit_kzalloc(test, len, GFP_USER);
258 	char __user *user_buffer = (char __user *)buffer;
259 
260 	memcpy(buffer, input, len);
261 
262 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE,
263 					       user_buffer, &len, &pos));
264 	KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len);
265 	KUNIT_EXPECT_EQ(test, sizeof(input) - 1, pos);
266 	KUNIT_EXPECT_EQ(test, 9, *((int *)table.data));
267 }
268 
269 /*
270  * Same as previous test, but now with negative numbers.
271  */
272 static void sysctl_test_dointvec_write_happy_single_negative(struct kunit *test)
273 {
274 	int data = 0;
275 	struct ctl_table table = {
276 		.procname = "foo",
277 		.data		= &data,
278 		.maxlen		= sizeof(int),
279 		.mode		= 0644,
280 		.proc_handler	= proc_dointvec,
281 		.extra1		= SYSCTL_ZERO,
282 		.extra2         = SYSCTL_ONE_HUNDRED,
283 	};
284 	char input[] = "-9";
285 	size_t len = sizeof(input) - 1;
286 	loff_t pos = 0;
287 	char *buffer = kunit_kzalloc(test, len, GFP_USER);
288 	char __user *user_buffer = (char __user *)buffer;
289 
290 	memcpy(buffer, input, len);
291 
292 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE,
293 					       user_buffer, &len, &pos));
294 	KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len);
295 	KUNIT_EXPECT_EQ(test, sizeof(input) - 1, pos);
296 	KUNIT_EXPECT_EQ(test, -9, *((int *)table.data));
297 }
298 
299 /*
300  * Test that writing a value smaller than the minimum possible value is not
301  * allowed.
302  */
303 static void sysctl_test_api_dointvec_write_single_less_int_min(
304 		struct kunit *test)
305 {
306 	int data = 0;
307 	struct ctl_table table = {
308 		.procname = "foo",
309 		.data		= &data,
310 		.maxlen		= sizeof(int),
311 		.mode		= 0644,
312 		.proc_handler	= proc_dointvec,
313 		.extra1		= SYSCTL_ZERO,
314 		.extra2         = SYSCTL_ONE_HUNDRED,
315 	};
316 	size_t max_len = 32, len = max_len;
317 	loff_t pos = 0;
318 	char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
319 	char __user *user_buffer = (char __user *)buffer;
320 	unsigned long abs_of_less_than_min = (unsigned long)INT_MAX
321 					     - (INT_MAX + INT_MIN) + 1;
322 
323 	/*
324 	 * We use this rigmarole to create a string that contains a value one
325 	 * less than the minimum accepted value.
326 	 */
327 	KUNIT_ASSERT_LT(test,
328 			(size_t)snprintf(buffer, max_len, "-%lu",
329 					 abs_of_less_than_min),
330 			max_len);
331 
332 	KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec(&table, KUNIT_PROC_WRITE,
333 						     user_buffer, &len, &pos));
334 	KUNIT_EXPECT_EQ(test, max_len, len);
335 	KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
336 }
337 
338 /*
339  * Test that writing the maximum possible value works.
340  */
341 static void sysctl_test_api_dointvec_write_single_greater_int_max(
342 		struct kunit *test)
343 {
344 	int data = 0;
345 	struct ctl_table table = {
346 		.procname = "foo",
347 		.data		= &data,
348 		.maxlen		= sizeof(int),
349 		.mode		= 0644,
350 		.proc_handler	= proc_dointvec,
351 		.extra1		= SYSCTL_ZERO,
352 		.extra2         = SYSCTL_ONE_HUNDRED,
353 	};
354 	size_t max_len = 32, len = max_len;
355 	loff_t pos = 0;
356 	char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
357 	char __user *user_buffer = (char __user *)buffer;
358 	unsigned long greater_than_max = (unsigned long)INT_MAX + 1;
359 
360 	KUNIT_ASSERT_GT(test, greater_than_max, (unsigned long)INT_MAX);
361 	KUNIT_ASSERT_LT(test, (size_t)snprintf(buffer, max_len, "%lu",
362 					       greater_than_max),
363 			max_len);
364 	KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec(&table, KUNIT_PROC_WRITE,
365 						     user_buffer, &len, &pos));
366 	KUNIT_ASSERT_EQ(test, max_len, len);
367 	KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
368 }
369 
370 static struct kunit_case sysctl_test_cases[] = {
371 	KUNIT_CASE(sysctl_test_api_dointvec_null_tbl_data),
372 	KUNIT_CASE(sysctl_test_api_dointvec_table_maxlen_unset),
373 	KUNIT_CASE(sysctl_test_api_dointvec_table_len_is_zero),
374 	KUNIT_CASE(sysctl_test_api_dointvec_table_read_but_position_set),
375 	KUNIT_CASE(sysctl_test_dointvec_read_happy_single_positive),
376 	KUNIT_CASE(sysctl_test_dointvec_read_happy_single_negative),
377 	KUNIT_CASE(sysctl_test_dointvec_write_happy_single_positive),
378 	KUNIT_CASE(sysctl_test_dointvec_write_happy_single_negative),
379 	KUNIT_CASE(sysctl_test_api_dointvec_write_single_less_int_min),
380 	KUNIT_CASE(sysctl_test_api_dointvec_write_single_greater_int_max),
381 	{}
382 };
383 
384 static struct kunit_suite sysctl_test_suite = {
385 	.name = "sysctl_test",
386 	.test_cases = sysctl_test_cases,
387 };
388 
389 kunit_test_suites(&sysctl_test_suite);
390 
391 MODULE_LICENSE("GPL v2");
392