History

kernel/sysctl-test: Add null pointer test for sysctl.c:proc_dointvec()

KUnit tests for initialized data behavior of proc_dointvec that is explicitly checked in the code. Includes basic parsing tests including int min/max overflow.

Iurii Zaikin committed 7 years ago

Commit 2cb80db1 parent 804ec793 files · +405 −0Browse files

Showing the changes to kernel/sysctl-test.c (1 of 3 files). Show all

Unified · Split

kernel/sysctl-test.c+392−0
@@ -0,0 +1,392 @@
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, (size_t)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, (size_t)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, (size_t)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, (size_t)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, (size_t)0, len);
139+
140+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE, buffer,
141+ &len, &pos));
142+ KUNIT_EXPECT_EQ(test, (size_t)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, (size_t)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, (size_t)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, (size_t)4, len);
237+ buffer[len] = '\0';
238+ KUNIT_EXPECT_STREQ(test, "-16\n", (char *)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, (size_t)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, (size_t)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_suite(sysctl_test_suite);