1 | /* |
2 | * Test cases for lib/hexdump.c module. |
3 | */ |
4 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
5 | |
6 | #include <linux/init.h> |
7 | #include <linux/kernel.h> |
8 | #include <linux/module.h> |
9 | #include <linux/random.h> |
10 | #include <linux/string.h> |
11 | |
12 | static const unsigned char data_b[] = { |
13 | '\xbe', '\x32', '\xdb', '\x7b', '\x0a', '\x18', '\x93', '\xb2', /* 00 - 07 */ |
14 | '\x70', '\xba', '\xc4', '\x24', '\x7d', '\x83', '\x34', '\x9b', /* 08 - 0f */ |
15 | '\xa6', '\x9c', '\x31', '\xad', '\x9c', '\x0f', '\xac', '\xe9', /* 10 - 17 */ |
16 | '\x4c', '\xd1', '\x19', '\x99', '\x43', '\xb1', '\xaf', '\x0c', /* 18 - 1f */ |
17 | }; |
18 | |
19 | static const unsigned char data_a[] = ".2.{....p..$}.4...1.....L...C..." ; |
20 | |
21 | static const char * const test_data_1[] __initconst = { |
22 | "be" , "32" , "db" , "7b" , "0a" , "18" , "93" , "b2" , |
23 | "70" , "ba" , "c4" , "24" , "7d" , "83" , "34" , "9b" , |
24 | "a6" , "9c" , "31" , "ad" , "9c" , "0f" , "ac" , "e9" , |
25 | "4c" , "d1" , "19" , "99" , "43" , "b1" , "af" , "0c" , |
26 | }; |
27 | |
28 | static const char * const test_data_2_le[] __initconst = { |
29 | "32be" , "7bdb" , "180a" , "b293" , |
30 | "ba70" , "24c4" , "837d" , "9b34" , |
31 | "9ca6" , "ad31" , "0f9c" , "e9ac" , |
32 | "d14c" , "9919" , "b143" , "0caf" , |
33 | }; |
34 | |
35 | static const char * const test_data_2_be[] __initconst = { |
36 | "be32" , "db7b" , "0a18" , "93b2" , |
37 | "70ba" , "c424" , "7d83" , "349b" , |
38 | "a69c" , "31ad" , "9c0f" , "ace9" , |
39 | "4cd1" , "1999" , "43b1" , "af0c" , |
40 | }; |
41 | |
42 | static const char * const test_data_4_le[] __initconst = { |
43 | "7bdb32be" , "b293180a" , "24c4ba70" , "9b34837d" , |
44 | "ad319ca6" , "e9ac0f9c" , "9919d14c" , "0cafb143" , |
45 | }; |
46 | |
47 | static const char * const test_data_4_be[] __initconst = { |
48 | "be32db7b" , "0a1893b2" , "70bac424" , "7d83349b" , |
49 | "a69c31ad" , "9c0face9" , "4cd11999" , "43b1af0c" , |
50 | }; |
51 | |
52 | static const char * const test_data_8_le[] __initconst = { |
53 | "b293180a7bdb32be" , "9b34837d24c4ba70" , |
54 | "e9ac0f9cad319ca6" , "0cafb1439919d14c" , |
55 | }; |
56 | |
57 | static const char * const test_data_8_be[] __initconst = { |
58 | "be32db7b0a1893b2" , "70bac4247d83349b" , |
59 | "a69c31ad9c0face9" , "4cd1199943b1af0c" , |
60 | }; |
61 | |
62 | #define FILL_CHAR '#' |
63 | |
64 | static unsigned total_tests __initdata; |
65 | static unsigned failed_tests __initdata; |
66 | |
67 | static void __init test_hexdump_prepare_test(size_t len, int rowsize, |
68 | int groupsize, char *test, |
69 | size_t testlen, bool ascii) |
70 | { |
71 | char *p; |
72 | const char * const *result; |
73 | size_t l = len; |
74 | int gs = groupsize, rs = rowsize; |
75 | unsigned int i; |
76 | const bool is_be = IS_ENABLED(CONFIG_CPU_BIG_ENDIAN); |
77 | |
78 | if (rs != 16 && rs != 32) |
79 | rs = 16; |
80 | |
81 | if (l > rs) |
82 | l = rs; |
83 | |
84 | if (!is_power_of_2(n: gs) || gs > 8 || (len % gs != 0)) |
85 | gs = 1; |
86 | |
87 | if (gs == 8) |
88 | result = is_be ? test_data_8_be : test_data_8_le; |
89 | else if (gs == 4) |
90 | result = is_be ? test_data_4_be : test_data_4_le; |
91 | else if (gs == 2) |
92 | result = is_be ? test_data_2_be : test_data_2_le; |
93 | else |
94 | result = test_data_1; |
95 | |
96 | /* hex dump */ |
97 | p = test; |
98 | for (i = 0; i < l / gs; i++) { |
99 | const char *q = *result++; |
100 | size_t amount = strlen(q); |
101 | |
102 | memcpy(p, q, amount); |
103 | p += amount; |
104 | |
105 | *p++ = ' '; |
106 | } |
107 | if (i) |
108 | p--; |
109 | |
110 | /* ASCII part */ |
111 | if (ascii) { |
112 | do { |
113 | *p++ = ' '; |
114 | } while (p < test + rs * 2 + rs / gs + 1); |
115 | |
116 | strncpy(p, q: data_a, size: l); |
117 | p += l; |
118 | } |
119 | |
120 | *p = '\0'; |
121 | } |
122 | |
123 | #define TEST_HEXDUMP_BUF_SIZE (32 * 3 + 2 + 32 + 1) |
124 | |
125 | static void __init test_hexdump(size_t len, int rowsize, int groupsize, |
126 | bool ascii) |
127 | { |
128 | char test[TEST_HEXDUMP_BUF_SIZE]; |
129 | char real[TEST_HEXDUMP_BUF_SIZE]; |
130 | |
131 | total_tests++; |
132 | |
133 | memset(real, FILL_CHAR, sizeof(real)); |
134 | hex_dump_to_buffer(buf: data_b, len, rowsize, groupsize, linebuf: real, linebuflen: sizeof(real), |
135 | ascii); |
136 | |
137 | memset(test, FILL_CHAR, sizeof(test)); |
138 | test_hexdump_prepare_test(len, rowsize, groupsize, test, testlen: sizeof(test), |
139 | ascii); |
140 | |
141 | if (memcmp(p: test, q: real, TEST_HEXDUMP_BUF_SIZE)) { |
142 | pr_err("Len: %zu row: %d group: %d\n" , len, rowsize, groupsize); |
143 | pr_err("Result: '%s'\n" , real); |
144 | pr_err("Expect: '%s'\n" , test); |
145 | failed_tests++; |
146 | } |
147 | } |
148 | |
149 | static void __init test_hexdump_set(int rowsize, bool ascii) |
150 | { |
151 | size_t d = min_t(size_t, sizeof(data_b), rowsize); |
152 | size_t len = get_random_u32_inclusive(floor: 1, ceil: d); |
153 | |
154 | test_hexdump(len, rowsize, groupsize: 4, ascii); |
155 | test_hexdump(len, rowsize, groupsize: 2, ascii); |
156 | test_hexdump(len, rowsize, groupsize: 8, ascii); |
157 | test_hexdump(len, rowsize, groupsize: 1, ascii); |
158 | } |
159 | |
160 | static void __init test_hexdump_overflow(size_t buflen, size_t len, |
161 | int rowsize, int groupsize, |
162 | bool ascii) |
163 | { |
164 | char test[TEST_HEXDUMP_BUF_SIZE]; |
165 | char buf[TEST_HEXDUMP_BUF_SIZE]; |
166 | int rs = rowsize, gs = groupsize; |
167 | int ae, he, e, f, r; |
168 | bool a; |
169 | |
170 | total_tests++; |
171 | |
172 | memset(buf, FILL_CHAR, sizeof(buf)); |
173 | |
174 | r = hex_dump_to_buffer(buf: data_b, len, rowsize: rs, groupsize: gs, linebuf: buf, linebuflen: buflen, ascii); |
175 | |
176 | /* |
177 | * Caller must provide the data length multiple of groupsize. The |
178 | * calculations below are made with that assumption in mind. |
179 | */ |
180 | ae = rs * 2 /* hex */ + rs / gs /* spaces */ + 1 /* space */ + len /* ascii */; |
181 | he = (gs * 2 /* hex */ + 1 /* space */) * len / gs - 1 /* no trailing space */; |
182 | |
183 | if (ascii) |
184 | e = ae; |
185 | else |
186 | e = he; |
187 | |
188 | f = min_t(int, e + 1, buflen); |
189 | if (buflen) { |
190 | test_hexdump_prepare_test(len, rowsize: rs, groupsize: gs, test, testlen: sizeof(test), ascii); |
191 | test[f - 1] = '\0'; |
192 | } |
193 | memset(test + f, FILL_CHAR, sizeof(test) - f); |
194 | |
195 | a = r == e && !memcmp(p: test, q: buf, TEST_HEXDUMP_BUF_SIZE); |
196 | |
197 | buf[sizeof(buf) - 1] = '\0'; |
198 | |
199 | if (!a) { |
200 | pr_err("Len: %zu buflen: %zu strlen: %zu\n" , |
201 | len, buflen, strnlen(buf, sizeof(buf))); |
202 | pr_err("Result: %d '%s'\n" , r, buf); |
203 | pr_err("Expect: %d '%s'\n" , e, test); |
204 | failed_tests++; |
205 | } |
206 | } |
207 | |
208 | static void __init test_hexdump_overflow_set(size_t buflen, bool ascii) |
209 | { |
210 | unsigned int i = 0; |
211 | int rs = get_random_u32_inclusive(floor: 1, ceil: 2) * 16; |
212 | |
213 | do { |
214 | int gs = 1 << i; |
215 | size_t len = get_random_u32_below(ceil: rs) + gs; |
216 | |
217 | test_hexdump_overflow(buflen, rounddown(len, gs), rowsize: rs, groupsize: gs, ascii); |
218 | } while (i++ < 3); |
219 | } |
220 | |
221 | static int __init test_hexdump_init(void) |
222 | { |
223 | unsigned int i; |
224 | int rowsize; |
225 | |
226 | rowsize = get_random_u32_inclusive(floor: 1, ceil: 2) * 16; |
227 | for (i = 0; i < 16; i++) |
228 | test_hexdump_set(rowsize, ascii: false); |
229 | |
230 | rowsize = get_random_u32_inclusive(floor: 1, ceil: 2) * 16; |
231 | for (i = 0; i < 16; i++) |
232 | test_hexdump_set(rowsize, ascii: true); |
233 | |
234 | for (i = 0; i <= TEST_HEXDUMP_BUF_SIZE; i++) |
235 | test_hexdump_overflow_set(buflen: i, ascii: false); |
236 | |
237 | for (i = 0; i <= TEST_HEXDUMP_BUF_SIZE; i++) |
238 | test_hexdump_overflow_set(buflen: i, ascii: true); |
239 | |
240 | if (failed_tests == 0) |
241 | pr_info("all %u tests passed\n" , total_tests); |
242 | else |
243 | pr_err("failed %u out of %u tests\n" , failed_tests, total_tests); |
244 | |
245 | return failed_tests ? -EINVAL : 0; |
246 | } |
247 | module_init(test_hexdump_init); |
248 | |
249 | static void __exit test_hexdump_exit(void) |
250 | { |
251 | /* do nothing */ |
252 | } |
253 | module_exit(test_hexdump_exit); |
254 | |
255 | MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>" ); |
256 | MODULE_LICENSE("Dual BSD/GPL" ); |
257 | |