diff options
Diffstat (limited to 'lib/test-string_helpers.c')
-rw-r--r-- | lib/test-string_helpers.c | 277 |
1 files changed, 262 insertions, 15 deletions
diff --git a/lib/test-string_helpers.c b/lib/test-string_helpers.c index 6ac48de04c0e..ab0d30e1e18f 100644 --- a/lib/test-string_helpers.c +++ b/lib/test-string_helpers.c | |||
@@ -5,11 +5,32 @@ | |||
5 | 5 | ||
6 | #include <linux/init.h> | 6 | #include <linux/init.h> |
7 | #include <linux/kernel.h> | 7 | #include <linux/kernel.h> |
8 | #include <linux/slab.h> | ||
8 | #include <linux/module.h> | 9 | #include <linux/module.h> |
9 | #include <linux/random.h> | 10 | #include <linux/random.h> |
10 | #include <linux/string.h> | 11 | #include <linux/string.h> |
11 | #include <linux/string_helpers.h> | 12 | #include <linux/string_helpers.h> |
12 | 13 | ||
14 | static __init bool test_string_check_buf(const char *name, unsigned int flags, | ||
15 | char *in, size_t p, | ||
16 | char *out_real, size_t q_real, | ||
17 | char *out_test, size_t q_test) | ||
18 | { | ||
19 | if (q_real == q_test && !memcmp(out_test, out_real, q_test)) | ||
20 | return true; | ||
21 | |||
22 | pr_warn("Test '%s' failed: flags = %u\n", name, flags); | ||
23 | |||
24 | print_hex_dump(KERN_WARNING, "Input: ", DUMP_PREFIX_NONE, 16, 1, | ||
25 | in, p, true); | ||
26 | print_hex_dump(KERN_WARNING, "Expected: ", DUMP_PREFIX_NONE, 16, 1, | ||
27 | out_test, q_test, true); | ||
28 | print_hex_dump(KERN_WARNING, "Got: ", DUMP_PREFIX_NONE, 16, 1, | ||
29 | out_real, q_real, true); | ||
30 | |||
31 | return false; | ||
32 | } | ||
33 | |||
13 | struct test_string { | 34 | struct test_string { |
14 | const char *in; | 35 | const char *in; |
15 | const char *out; | 36 | const char *out; |
@@ -39,12 +60,17 @@ static const struct test_string strings[] __initconst = { | |||
39 | }, | 60 | }, |
40 | }; | 61 | }; |
41 | 62 | ||
42 | static void __init test_string_unescape(unsigned int flags, bool inplace) | 63 | static void __init test_string_unescape(const char *name, unsigned int flags, |
64 | bool inplace) | ||
43 | { | 65 | { |
44 | char in[256]; | 66 | int q_real = 256; |
45 | char out_test[256]; | 67 | char *in = kmalloc(q_real, GFP_KERNEL); |
46 | char out_real[256]; | 68 | char *out_test = kmalloc(q_real, GFP_KERNEL); |
47 | int i, p = 0, q_test = 0, q_real = sizeof(out_real); | 69 | char *out_real = kmalloc(q_real, GFP_KERNEL); |
70 | int i, p = 0, q_test = 0; | ||
71 | |||
72 | if (!in || !out_test || !out_real) | ||
73 | goto out; | ||
48 | 74 | ||
49 | for (i = 0; i < ARRAY_SIZE(strings); i++) { | 75 | for (i = 0; i < ARRAY_SIZE(strings); i++) { |
50 | const char *s = strings[i].in; | 76 | const char *s = strings[i].in; |
@@ -77,15 +103,225 @@ static void __init test_string_unescape(unsigned int flags, bool inplace) | |||
77 | q_real = string_unescape(in, out_real, q_real, flags); | 103 | q_real = string_unescape(in, out_real, q_real, flags); |
78 | } | 104 | } |
79 | 105 | ||
80 | if (q_real != q_test || memcmp(out_test, out_real, q_test)) { | 106 | test_string_check_buf(name, flags, in, p - 1, out_real, q_real, |
81 | pr_warn("Test failed: flags = %u\n", flags); | 107 | out_test, q_test); |
82 | print_hex_dump(KERN_WARNING, "Input: ", | 108 | out: |
83 | DUMP_PREFIX_NONE, 16, 1, in, p - 1, true); | 109 | kfree(out_real); |
84 | print_hex_dump(KERN_WARNING, "Expected: ", | 110 | kfree(out_test); |
85 | DUMP_PREFIX_NONE, 16, 1, out_test, q_test, true); | 111 | kfree(in); |
86 | print_hex_dump(KERN_WARNING, "Got: ", | 112 | } |
87 | DUMP_PREFIX_NONE, 16, 1, out_real, q_real, true); | 113 | |
114 | struct test_string_1 { | ||
115 | const char *out; | ||
116 | unsigned int flags; | ||
117 | }; | ||
118 | |||
119 | #define TEST_STRING_2_MAX_S1 32 | ||
120 | struct test_string_2 { | ||
121 | const char *in; | ||
122 | struct test_string_1 s1[TEST_STRING_2_MAX_S1]; | ||
123 | }; | ||
124 | |||
125 | #define TEST_STRING_2_DICT_0 NULL | ||
126 | static const struct test_string_2 escape0[] __initconst = {{ | ||
127 | .in = "\f\\ \n\r\t\v", | ||
128 | .s1 = {{ | ||
129 | .out = "\\f\\ \\n\\r\\t\\v", | ||
130 | .flags = ESCAPE_SPACE, | ||
131 | },{ | ||
132 | .out = "\\f\\134\\040\\n\\r\\t\\v", | ||
133 | .flags = ESCAPE_SPACE | ESCAPE_OCTAL, | ||
134 | },{ | ||
135 | .out = "\\f\\x5c\\x20\\n\\r\\t\\v", | ||
136 | .flags = ESCAPE_SPACE | ESCAPE_HEX, | ||
137 | },{ | ||
138 | /* terminator */ | ||
139 | }}, | ||
140 | },{ | ||
141 | .in = "\\h\\\"\a\e\\", | ||
142 | .s1 = {{ | ||
143 | .out = "\\\\h\\\\\"\\a\\e\\\\", | ||
144 | .flags = ESCAPE_SPECIAL, | ||
145 | },{ | ||
146 | .out = "\\\\\\150\\\\\\042\\a\\e\\\\", | ||
147 | .flags = ESCAPE_SPECIAL | ESCAPE_OCTAL, | ||
148 | },{ | ||
149 | .out = "\\\\\\x68\\\\\\x22\\a\\e\\\\", | ||
150 | .flags = ESCAPE_SPECIAL | ESCAPE_HEX, | ||
151 | },{ | ||
152 | /* terminator */ | ||
153 | }}, | ||
154 | },{ | ||
155 | .in = "\eb \\C\007\"\x90\r]", | ||
156 | .s1 = {{ | ||
157 | .out = "\eb \\C\007\"\x90\\r]", | ||
158 | .flags = ESCAPE_SPACE, | ||
159 | },{ | ||
160 | .out = "\\eb \\\\C\\a\"\x90\r]", | ||
161 | .flags = ESCAPE_SPECIAL, | ||
162 | },{ | ||
163 | .out = "\\eb \\\\C\\a\"\x90\\r]", | ||
164 | .flags = ESCAPE_SPACE | ESCAPE_SPECIAL, | ||
165 | },{ | ||
166 | .out = "\\033\\142\\040\\134\\103\\007\\042\\220\\015\\135", | ||
167 | .flags = ESCAPE_OCTAL, | ||
168 | },{ | ||
169 | .out = "\\033\\142\\040\\134\\103\\007\\042\\220\\r\\135", | ||
170 | .flags = ESCAPE_SPACE | ESCAPE_OCTAL, | ||
171 | },{ | ||
172 | .out = "\\e\\142\\040\\\\\\103\\a\\042\\220\\015\\135", | ||
173 | .flags = ESCAPE_SPECIAL | ESCAPE_OCTAL, | ||
174 | },{ | ||
175 | .out = "\\e\\142\\040\\\\\\103\\a\\042\\220\\r\\135", | ||
176 | .flags = ESCAPE_SPACE | ESCAPE_SPECIAL | ESCAPE_OCTAL, | ||
177 | },{ | ||
178 | .out = "\eb \\C\007\"\x90\r]", | ||
179 | .flags = ESCAPE_NP, | ||
180 | },{ | ||
181 | .out = "\eb \\C\007\"\x90\\r]", | ||
182 | .flags = ESCAPE_SPACE | ESCAPE_NP, | ||
183 | },{ | ||
184 | .out = "\\eb \\C\\a\"\x90\r]", | ||
185 | .flags = ESCAPE_SPECIAL | ESCAPE_NP, | ||
186 | },{ | ||
187 | .out = "\\eb \\C\\a\"\x90\\r]", | ||
188 | .flags = ESCAPE_SPACE | ESCAPE_SPECIAL | ESCAPE_NP, | ||
189 | },{ | ||
190 | .out = "\\033b \\C\\007\"\\220\\015]", | ||
191 | .flags = ESCAPE_OCTAL | ESCAPE_NP, | ||
192 | },{ | ||
193 | .out = "\\033b \\C\\007\"\\220\\r]", | ||
194 | .flags = ESCAPE_SPACE | ESCAPE_OCTAL | ESCAPE_NP, | ||
195 | },{ | ||
196 | .out = "\\eb \\C\\a\"\\220\\r]", | ||
197 | .flags = ESCAPE_SPECIAL | ESCAPE_SPACE | ESCAPE_OCTAL | | ||
198 | ESCAPE_NP, | ||
199 | },{ | ||
200 | .out = "\\x1bb \\C\\x07\"\\x90\\x0d]", | ||
201 | .flags = ESCAPE_NP | ESCAPE_HEX, | ||
202 | },{ | ||
203 | /* terminator */ | ||
204 | }}, | ||
205 | },{ | ||
206 | /* terminator */ | ||
207 | }}; | ||
208 | |||
209 | #define TEST_STRING_2_DICT_1 "b\\ \t\r" | ||
210 | static const struct test_string_2 escape1[] __initconst = {{ | ||
211 | .in = "\f\\ \n\r\t\v", | ||
212 | .s1 = {{ | ||
213 | .out = "\f\\134\\040\n\\015\\011\v", | ||
214 | .flags = ESCAPE_OCTAL, | ||
215 | },{ | ||
216 | .out = "\f\\x5c\\x20\n\\x0d\\x09\v", | ||
217 | .flags = ESCAPE_HEX, | ||
218 | },{ | ||
219 | /* terminator */ | ||
220 | }}, | ||
221 | },{ | ||
222 | .in = "\\h\\\"\a\e\\", | ||
223 | .s1 = {{ | ||
224 | .out = "\\134h\\134\"\a\e\\134", | ||
225 | .flags = ESCAPE_OCTAL, | ||
226 | },{ | ||
227 | /* terminator */ | ||
228 | }}, | ||
229 | },{ | ||
230 | .in = "\eb \\C\007\"\x90\r]", | ||
231 | .s1 = {{ | ||
232 | .out = "\e\\142\\040\\134C\007\"\x90\\015]", | ||
233 | .flags = ESCAPE_OCTAL, | ||
234 | },{ | ||
235 | /* terminator */ | ||
236 | }}, | ||
237 | },{ | ||
238 | /* terminator */ | ||
239 | }}; | ||
240 | |||
241 | static __init const char *test_string_find_match(const struct test_string_2 *s2, | ||
242 | unsigned int flags) | ||
243 | { | ||
244 | const struct test_string_1 *s1 = s2->s1; | ||
245 | unsigned int i; | ||
246 | |||
247 | if (!flags) | ||
248 | return s2->in; | ||
249 | |||
250 | /* Test cases are NULL-aware */ | ||
251 | flags &= ~ESCAPE_NULL; | ||
252 | |||
253 | /* ESCAPE_OCTAL has a higher priority */ | ||
254 | if (flags & ESCAPE_OCTAL) | ||
255 | flags &= ~ESCAPE_HEX; | ||
256 | |||
257 | for (i = 0; i < TEST_STRING_2_MAX_S1 && s1->out; i++, s1++) | ||
258 | if (s1->flags == flags) | ||
259 | return s1->out; | ||
260 | return NULL; | ||
261 | } | ||
262 | |||
263 | static __init void test_string_escape(const char *name, | ||
264 | const struct test_string_2 *s2, | ||
265 | unsigned int flags, const char *esc) | ||
266 | { | ||
267 | int q_real = 512; | ||
268 | char *out_test = kmalloc(q_real, GFP_KERNEL); | ||
269 | char *out_real = kmalloc(q_real, GFP_KERNEL); | ||
270 | char *in = kmalloc(256, GFP_KERNEL); | ||
271 | char *buf = out_real; | ||
272 | int p = 0, q_test = 0; | ||
273 | |||
274 | if (!out_test || !out_real || !in) | ||
275 | goto out; | ||
276 | |||
277 | for (; s2->in; s2++) { | ||
278 | const char *out; | ||
279 | int len; | ||
280 | |||
281 | /* NULL injection */ | ||
282 | if (flags & ESCAPE_NULL) { | ||
283 | in[p++] = '\0'; | ||
284 | out_test[q_test++] = '\\'; | ||
285 | out_test[q_test++] = '0'; | ||
286 | } | ||
287 | |||
288 | /* Don't try strings that have no output */ | ||
289 | out = test_string_find_match(s2, flags); | ||
290 | if (!out) | ||
291 | continue; | ||
292 | |||
293 | /* Copy string to in buffer */ | ||
294 | len = strlen(s2->in); | ||
295 | memcpy(&in[p], s2->in, len); | ||
296 | p += len; | ||
297 | |||
298 | /* Copy expected result for given flags */ | ||
299 | len = strlen(out); | ||
300 | memcpy(&out_test[q_test], out, len); | ||
301 | q_test += len; | ||
88 | } | 302 | } |
303 | |||
304 | q_real = string_escape_mem(in, p, &buf, q_real, flags, esc); | ||
305 | |||
306 | test_string_check_buf(name, flags, in, p, out_real, q_real, out_test, | ||
307 | q_test); | ||
308 | out: | ||
309 | kfree(in); | ||
310 | kfree(out_real); | ||
311 | kfree(out_test); | ||
312 | } | ||
313 | |||
314 | static __init void test_string_escape_nomem(void) | ||
315 | { | ||
316 | char *in = "\eb \\C\007\"\x90\r]"; | ||
317 | char out[64], *buf = out; | ||
318 | int rc = -ENOMEM, ret; | ||
319 | |||
320 | ret = string_escape_str_any_np(in, &buf, strlen(in), NULL); | ||
321 | if (ret == rc) | ||
322 | return; | ||
323 | |||
324 | pr_err("Test 'escape nomem' failed: got %d instead of %d\n", ret, rc); | ||
89 | } | 325 | } |
90 | 326 | ||
91 | static int __init test_string_helpers_init(void) | 327 | static int __init test_string_helpers_init(void) |
@@ -94,8 +330,19 @@ static int __init test_string_helpers_init(void) | |||
94 | 330 | ||
95 | pr_info("Running tests...\n"); | 331 | pr_info("Running tests...\n"); |
96 | for (i = 0; i < UNESCAPE_ANY + 1; i++) | 332 | for (i = 0; i < UNESCAPE_ANY + 1; i++) |
97 | test_string_unescape(i, false); | 333 | test_string_unescape("unescape", i, false); |
98 | test_string_unescape(get_random_int() % (UNESCAPE_ANY + 1), true); | 334 | test_string_unescape("unescape inplace", |
335 | get_random_int() % (UNESCAPE_ANY + 1), true); | ||
336 | |||
337 | /* Without dictionary */ | ||
338 | for (i = 0; i < (ESCAPE_ANY_NP | ESCAPE_HEX) + 1; i++) | ||
339 | test_string_escape("escape 0", escape0, i, TEST_STRING_2_DICT_0); | ||
340 | |||
341 | /* With dictionary */ | ||
342 | for (i = 0; i < (ESCAPE_ANY_NP | ESCAPE_HEX) + 1; i++) | ||
343 | test_string_escape("escape 1", escape1, i, TEST_STRING_2_DICT_1); | ||
344 | |||
345 | test_string_escape_nomem(); | ||
99 | 346 | ||
100 | return -EINVAL; | 347 | return -EINVAL; |
101 | } | 348 | } |