]>
Commit | Line | Data |
---|---|---|
2cb80dbb IZ |
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 | ||
2cb80dbb IZ |
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, | |
c06a17fe LS |
29 | .extra1 = SYSCTL_ZERO, |
30 | .extra2 = SYSCTL_ONE_HUNDRED, | |
2cb80dbb IZ |
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)); | |
388ca2e0 | 49 | KUNIT_EXPECT_EQ(test, 0, len); |
2cb80dbb IZ |
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)); | |
388ca2e0 | 58 | KUNIT_EXPECT_EQ(test, 0, len); |
2cb80dbb IZ |
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, | |
c06a17fe LS |
79 | .extra1 = SYSCTL_ZERO, |
80 | .extra2 = SYSCTL_ONE_HUNDRED, | |
2cb80dbb IZ |
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)); | |
388ca2e0 | 95 | KUNIT_EXPECT_EQ(test, 0, len); |
2cb80dbb IZ |
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)); | |
388ca2e0 | 104 | KUNIT_EXPECT_EQ(test, 0, len); |
2cb80dbb IZ |
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, | |
c06a17fe LS |
122 | .extra1 = SYSCTL_ZERO, |
123 | .extra2 = SYSCTL_ONE_HUNDRED, | |
2cb80dbb IZ |
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)); | |
388ca2e0 | 135 | KUNIT_EXPECT_EQ(test, 0, len); |
2cb80dbb IZ |
136 | |
137 | KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE, buffer, | |
138 | &len, &pos)); | |
388ca2e0 | 139 | KUNIT_EXPECT_EQ(test, 0, len); |
2cb80dbb IZ |
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, | |
c06a17fe LS |
156 | .extra1 = SYSCTL_ZERO, |
157 | .extra2 = SYSCTL_ONE_HUNDRED, | |
2cb80dbb IZ |
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)); | |
388ca2e0 | 174 | KUNIT_EXPECT_EQ(test, 0, len); |
2cb80dbb IZ |
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, | |
c06a17fe LS |
191 | .extra1 = SYSCTL_ZERO, |
192 | .extra2 = SYSCTL_ONE_HUNDRED, | |
2cb80dbb IZ |
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)); | |
388ca2e0 | 203 | KUNIT_ASSERT_EQ(test, 3, len); |
2cb80dbb IZ |
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, | |
c06a17fe LS |
222 | .extra1 = SYSCTL_ZERO, |
223 | .extra2 = SYSCTL_ONE_HUNDRED, | |
2cb80dbb IZ |
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)); | |
388ca2e0 | 233 | KUNIT_ASSERT_EQ(test, 4, len); |
2cb80dbb | 234 | buffer[len] = '\0'; |
388ca2e0 | 235 | KUNIT_EXPECT_STREQ(test, "-16\n", buffer); |
2cb80dbb IZ |
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, | |
c06a17fe LS |
251 | .extra1 = SYSCTL_ZERO, |
252 | .extra2 = SYSCTL_ONE_HUNDRED, | |
2cb80dbb IZ |
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); | |
388ca2e0 | 265 | KUNIT_EXPECT_EQ(test, sizeof(input) - 1, pos); |
2cb80dbb IZ |
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, | |
c06a17fe LS |
281 | .extra1 = SYSCTL_ZERO, |
282 | .extra2 = SYSCTL_ONE_HUNDRED, | |
2cb80dbb IZ |
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); | |
388ca2e0 | 295 | KUNIT_EXPECT_EQ(test, sizeof(input) - 1, pos); |
2cb80dbb IZ |
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, | |
c06a17fe LS |
313 | .extra1 = SYSCTL_ZERO, |
314 | .extra2 = SYSCTL_ONE_HUNDRED, | |
2cb80dbb IZ |
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, | |
c06a17fe LS |
351 | .extra1 = SYSCTL_ZERO, |
352 | .extra2 = SYSCTL_ONE_HUNDRED, | |
2cb80dbb IZ |
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 | ||
c475c77d AM |
389 | kunit_test_suites(&sysctl_test_suite); |
390 | ||
391 | MODULE_LICENSE("GPL v2"); |