Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : test suite for the compression functions
4 :
5 : Copyright (C) Jelmer Vernooij 2007
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include "includes.h"
22 : #include "torture/torture.h"
23 : #include "torture/local/proto.h"
24 : #include "talloc.h"
25 : #include "lzxpress.h"
26 : #include "lib/util/base64.h"
27 :
28 : /* Tests based on [MS-XCA] 3.1 Examples */
29 0 : static bool test_msft_data1(
30 : struct torture_context *test
31 : )
32 : {
33 0 : TALLOC_CTX *tmp_ctx = talloc_new(test);
34 :
35 0 : const char *fixed_data = "abcdefghijklmnopqrstuvwxyz";
36 0 : const uint8_t fixed_out[] = {
37 : 0x3f, 0x00, 0x00, 0x00, 0x61, 0x62, 0x63, 0x64,
38 : 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
39 : 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
40 : 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a };
41 :
42 : ssize_t c_size;
43 : uint8_t *out, *out2;
44 :
45 0 : out = talloc_size(tmp_ctx, 2048);
46 0 : memset(out, 0x42, talloc_get_size(out));
47 :
48 0 : torture_comment(test, "lzxpress fixed compression\n");
49 0 : c_size = lzxpress_compress((const uint8_t *)fixed_data,
50 0 : strlen(fixed_data),
51 : out,
52 0 : talloc_get_size(out));
53 :
54 0 : torture_assert_int_equal(test, c_size, sizeof(fixed_out),
55 : "fixed lzxpress_compress size");
56 0 : torture_assert_mem_equal(test, out, fixed_out, c_size,
57 : "fixed lzxpress_compress data");
58 :
59 0 : torture_comment(test, "lzxpress fixed decompression\n");
60 0 : out2 = talloc_size(tmp_ctx, strlen(fixed_data));
61 0 : c_size = lzxpress_decompress(out,
62 : sizeof(fixed_out),
63 : out2,
64 0 : talloc_get_size(out2));
65 :
66 0 : torture_assert_int_equal(test, c_size, strlen(fixed_data),
67 : "fixed lzxpress_decompress size");
68 0 : torture_assert_mem_equal(test, out2, fixed_data, c_size,
69 : "fixed lzxpress_decompress data");
70 :
71 0 : talloc_free(tmp_ctx);
72 0 : return true;
73 : }
74 :
75 :
76 0 : static bool test_msft_data2(
77 : struct torture_context *test
78 : )
79 : {
80 0 : TALLOC_CTX *tmp_ctx = talloc_new(test);
81 :
82 0 : const char *fixed_data =
83 : "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc"
84 : "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc"
85 : "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc"
86 : "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc"
87 : "abcabcabcabcabcabcabcabc";
88 0 : const uint8_t fixed_out[] = {
89 : 0xff, 0xff, 0xff, 0x1f, 0x61, 0x62, 0x63, 0x17,
90 : 0x00, 0x0f, 0xff, 0x26, 0x01};
91 :
92 : ssize_t c_size;
93 : uint8_t *out, *out2;
94 :
95 0 : out = talloc_size(tmp_ctx, 2048);
96 0 : memset(out, 0x42, talloc_get_size(out));
97 :
98 0 : torture_comment(test, "lzxpress fixed compression\n");
99 0 : c_size = lzxpress_compress((const uint8_t *)fixed_data,
100 0 : strlen(fixed_data),
101 : out,
102 0 : talloc_get_size(out));
103 :
104 0 : torture_assert_int_equal(test, c_size, sizeof(fixed_out),
105 : "fixed lzxpress_compress size");
106 0 : torture_assert_mem_equal(test, out, fixed_out, c_size,
107 : "fixed lzxpress_compress data");
108 :
109 0 : torture_comment(test, "lzxpress fixed decompression\n");
110 0 : out2 = talloc_size(tmp_ctx, strlen(fixed_data));
111 0 : c_size = lzxpress_decompress(out,
112 : sizeof(fixed_out),
113 : out2,
114 0 : talloc_get_size(out2));
115 :
116 0 : torture_comment(test, "out2: %.*s\n", (int)c_size, (char *)out2);
117 :
118 0 : torture_assert_int_equal(test, c_size, strlen(fixed_data),
119 : "fixed lzxpress_decompress size");
120 0 : torture_assert_mem_equal(test, out2, fixed_data, c_size,
121 : "fixed lzxpress_decompress data");
122 :
123 0 : talloc_free(tmp_ctx);
124 0 : return true;
125 : }
126 :
127 : /*
128 : test lzxpress
129 : */
130 0 : static bool test_lzxpress(struct torture_context *test)
131 : {
132 0 : TALLOC_CTX *tmp_ctx = talloc_new(test);
133 0 : const char *fixed_data = "this is a test. and this is a test too";
134 0 : const uint8_t fixed_out[] = {
135 : 0xff, 0x21, 0x00, 0x04, 0x74, 0x68, 0x69, 0x73,
136 : 0x20, 0x10, 0x00, 0x61, 0x20, 0x74, 0x65, 0x73,
137 : 0x74, 0x2E, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x9F,
138 : 0x00, 0x04, 0x20, 0x74, 0x6F, 0x6F };
139 :
140 0 : const uint8_t fixed_out_old_version[] = {
141 : 0x00, 0x20, 0x00, 0x04, 0x74, 0x68, 0x69, 0x73,
142 : 0x20, 0x10, 0x00, 0x61, 0x20, 0x74, 0x65, 0x73,
143 : 0x74, 0x2E, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x9F,
144 : 0x00, 0x04, 0x20, 0x74, 0x6F, 0x6F, 0x00, 0x00,
145 : 0x00, 0x00 };
146 :
147 : ssize_t c_size;
148 : uint8_t *out, *out2, *out3;
149 :
150 0 : out = talloc_size(tmp_ctx, 2048);
151 0 : memset(out, 0x42, talloc_get_size(out));
152 :
153 0 : torture_comment(test, "lzxpress fixed compression\n");
154 0 : c_size = lzxpress_compress((const uint8_t *)fixed_data,
155 0 : strlen(fixed_data),
156 : out,
157 0 : talloc_get_size(out));
158 :
159 0 : torture_assert_int_equal(test, c_size, sizeof(fixed_out),
160 : "fixed lzxpress_compress size");
161 0 : torture_assert_mem_equal(test, out, fixed_out, c_size,
162 : "fixed lzxpress_compress data");
163 :
164 0 : torture_comment(test, "lzxpress fixed decompression\n");
165 0 : out2 = talloc_size(tmp_ctx, strlen(fixed_data));
166 0 : c_size = lzxpress_decompress(out,
167 : sizeof(fixed_out),
168 : out2,
169 0 : talloc_get_size(out2));
170 :
171 0 : torture_assert_int_equal(test, c_size, strlen(fixed_data),
172 : "fixed lzxpress_decompress size");
173 0 : torture_assert_mem_equal(test, out2, fixed_data, c_size,
174 : "fixed lzxpress_decompress data");
175 :
176 :
177 0 : torture_comment(test, "lzxpress fixed decompression (old data)\n");
178 0 : out3 = talloc_size(tmp_ctx, strlen(fixed_data));
179 0 : c_size = lzxpress_decompress(fixed_out_old_version,
180 : sizeof(fixed_out_old_version),
181 : out3,
182 0 : talloc_get_size(out3));
183 :
184 0 : torture_assert_int_equal(test, c_size, strlen(fixed_data),
185 : "fixed lzxpress_decompress size");
186 0 : torture_assert_mem_equal(test, out3, fixed_data, c_size,
187 : "fixed lzxpress_decompress data");
188 :
189 0 : talloc_free(tmp_ctx);
190 0 : return true;
191 : }
192 :
193 0 : static bool test_lzxpress2(struct torture_context *test)
194 : {
195 : /*
196 : * Use two matches, separated by a literal, and each with a length
197 : * greater than 10, to test the use of nibble_index. Both length values
198 : * (less ten) should be stored as adjacent nibbles to form the 0x21
199 : * byte.
200 : */
201 :
202 0 : TALLOC_CTX *tmp_ctx = talloc_new(test);
203 0 : const char *fixed_data = "aaaaaaaaaaaabaaaaaaaaaaaa";
204 0 : const uint8_t fixed_out[] = {
205 : 0xff, 0xff, 0xff, 0x5f, 0x61, 0x07, 0x00, 0x21,
206 : 0x62, 0x67, 0x00};
207 :
208 : ssize_t c_size;
209 : uint8_t *out, *out2;
210 :
211 0 : out = talloc_size(tmp_ctx, 2048);
212 0 : memset(out, 0x42, talloc_get_size(out));
213 :
214 0 : torture_comment(test, "lzxpress fixed compression\n");
215 0 : c_size = lzxpress_compress((const uint8_t *)fixed_data,
216 0 : strlen(fixed_data),
217 : out,
218 0 : talloc_get_size(out));
219 :
220 0 : torture_assert_int_equal(test, c_size, sizeof(fixed_out),
221 : "fixed lzxpress_compress size");
222 0 : torture_assert_mem_equal(test, out, fixed_out, c_size,
223 : "fixed lzxpress_compress data");
224 :
225 0 : torture_comment(test, "lzxpress fixed decompression\n");
226 0 : out2 = talloc_size(tmp_ctx, strlen(fixed_data));
227 0 : c_size = lzxpress_decompress(out,
228 : sizeof(fixed_out),
229 : out2,
230 0 : talloc_get_size(out2));
231 :
232 0 : torture_assert_int_equal(test, c_size, strlen(fixed_data),
233 : "fixed lzxpress_decompress size");
234 0 : torture_assert_mem_equal(test, out2, fixed_data, c_size,
235 : "fixed lzxpress_decompress data");
236 :
237 0 : talloc_free(tmp_ctx);
238 0 : return true;
239 : }
240 :
241 0 : static bool test_lzxpress3(struct torture_context *test)
242 : {
243 : /*
244 : * Use a series of 31 literals, followed by a single minimum-length
245 : * match (and a terminating literal), to test setting indic_pos when the
246 : * 32-bit flags value overflows after a match.
247 : */
248 :
249 0 : TALLOC_CTX *tmp_ctx = talloc_new(test);
250 0 : const char *fixed_data = "abcdefghijklmnopqrstuvwxyz01234abca";
251 0 : const uint8_t fixed_out[] = {
252 : 0x01, 0x00, 0x00, 0x00, 0x61, 0x62, 0x63, 0x64,
253 : 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
254 : 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
255 : 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x30, 0x31,
256 : 0x32, 0x33, 0x34, 0xf0, 0x00, 0xff, 0xff, 0xff,
257 : 0x7f, 0x61};
258 :
259 : ssize_t c_size;
260 : uint8_t *out, *out2;
261 :
262 0 : out = talloc_size(tmp_ctx, 2048);
263 0 : memset(out, 0x42, talloc_get_size(out));
264 :
265 0 : torture_comment(test, "lzxpress fixed compression\n");
266 0 : c_size = lzxpress_compress((const uint8_t *)fixed_data,
267 0 : strlen(fixed_data),
268 : out,
269 0 : talloc_get_size(out));
270 :
271 0 : torture_assert_int_equal(test, c_size, sizeof(fixed_out),
272 : "fixed lzxpress_compress size");
273 0 : torture_assert_mem_equal(test, out, fixed_out, c_size,
274 : "fixed lzxpress_compress data");
275 :
276 0 : torture_comment(test, "lzxpress fixed decompression\n");
277 0 : out2 = talloc_size(tmp_ctx, strlen(fixed_data));
278 0 : c_size = lzxpress_decompress(out,
279 : sizeof(fixed_out),
280 : out2,
281 0 : talloc_get_size(out2));
282 :
283 0 : torture_assert_int_equal(test, c_size, strlen(fixed_data),
284 : "fixed lzxpress_decompress size");
285 0 : torture_assert_mem_equal(test, out2, fixed_data, c_size,
286 : "fixed lzxpress_decompress data");
287 :
288 0 : talloc_free(tmp_ctx);
289 0 : return true;
290 : }
291 :
292 0 : static bool test_lzxpress4(struct torture_context *test)
293 : {
294 : /*
295 : * Use a series of 31 literals, followed by a single minimum-length
296 : * match, to test that the final set of 32-bit flags is written
297 : * correctly when it is empty.
298 : */
299 :
300 0 : TALLOC_CTX *tmp_ctx = talloc_new(test);
301 0 : const char *fixed_data = "abcdefghijklmnopqrstuvwxyz01234abc";
302 0 : const uint8_t fixed_out[] = {
303 : 0x01, 0x00, 0x00, 0x00, 0x61, 0x62, 0x63, 0x64,
304 : 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
305 : 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
306 : 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x30, 0x31,
307 : 0x32, 0x33, 0x34, 0xf0, 0x00, 0xff, 0xff, 0xff,
308 : 0xff};
309 :
310 : ssize_t c_size;
311 : uint8_t *out, *out2;
312 :
313 0 : out = talloc_size(tmp_ctx, 2048);
314 0 : memset(out, 0x42, talloc_get_size(out));
315 :
316 0 : torture_comment(test, "lzxpress fixed compression\n");
317 0 : c_size = lzxpress_compress((const uint8_t *)fixed_data,
318 0 : strlen(fixed_data),
319 : out,
320 0 : talloc_get_size(out));
321 :
322 0 : torture_assert_int_equal(test, c_size, sizeof(fixed_out),
323 : "fixed lzxpress_compress size");
324 0 : torture_assert_mem_equal(test, out, fixed_out, c_size,
325 : "fixed lzxpress_compress data");
326 :
327 0 : torture_comment(test, "lzxpress fixed decompression\n");
328 0 : out2 = talloc_size(tmp_ctx, strlen(fixed_data));
329 0 : c_size = lzxpress_decompress(out,
330 : sizeof(fixed_out),
331 : out2,
332 0 : talloc_get_size(out2));
333 :
334 0 : torture_assert_int_equal(test, c_size, strlen(fixed_data),
335 : "fixed lzxpress_decompress size");
336 0 : torture_assert_mem_equal(test, out2, fixed_data, c_size,
337 : "fixed lzxpress_decompress data");
338 :
339 0 : talloc_free(tmp_ctx);
340 0 : return true;
341 : }
342 :
343 :
344 0 : static bool test_lzxpress_many_zeros(struct torture_context *test)
345 : {
346 : /*
347 : * Repeated values (zero is convenient but not special) will lead to
348 : * very long substring searches in compression, which can be very slow
349 : * if we're not careful.
350 : *
351 : * This test makes a very loose assertion about how long it should
352 : * take to compress a million zeros.
353 : *
354 : * Wall clock time *should* be < 0.1 seconds with the fix and around a
355 : * minute without it. We try for CLOCK_THREAD_CPUTIME_ID which should
356 : * filter out some noise on the machine, and set the threshold at 5
357 : * seconds.
358 : */
359 :
360 0 : TALLOC_CTX *tmp_ctx = talloc_new(test);
361 0 : const size_t N_ZEROS = 1000000;
362 0 : const uint8_t *zeros = talloc_zero_size(tmp_ctx, N_ZEROS);
363 0 : const ssize_t expected_c_size = 93;
364 : ssize_t c_size;
365 : uint8_t *comp, *decomp;
366 : static struct timespec t_start, t_end;
367 : uint64_t elapsed_ns;
368 :
369 0 : if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t_start) != 0) {
370 0 : if (clock_gettime(CUSTOM_CLOCK_MONOTONIC, &t_start) != 0) {
371 0 : clock_gettime(CLOCK_REALTIME, &t_start);
372 : }
373 : }
374 :
375 0 : comp = talloc_zero_size(tmp_ctx, 2048);
376 :
377 0 : c_size = lzxpress_compress(zeros,
378 : N_ZEROS,
379 : comp,
380 0 : talloc_get_size(comp));
381 :
382 0 : torture_assert_int_equal(test, c_size, expected_c_size,
383 : "fixed lzxpress_compress size");
384 :
385 0 : decomp = talloc_size(tmp_ctx, N_ZEROS * 2);
386 0 : c_size = lzxpress_decompress(comp,
387 : c_size,
388 : decomp,
389 : N_ZEROS * 2);
390 :
391 0 : if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t_end) != 0) {
392 0 : if (clock_gettime(CUSTOM_CLOCK_MONOTONIC, &t_end) != 0) {
393 0 : clock_gettime(CLOCK_REALTIME, &t_end);
394 : }
395 : }
396 0 : elapsed_ns = (
397 0 : (t_end.tv_sec - t_start.tv_sec) * 1000U * 1000U * 1000U) +
398 0 : (t_end.tv_nsec - t_start.tv_nsec);
399 0 : torture_comment(test, "round-trip time: %"PRIu64" ns\n", elapsed_ns);
400 0 : torture_assert(test, elapsed_ns < 3 * 1000U * 1000U * 1000U,
401 : "million zeros round trip tool > 3 seconds");
402 0 : torture_assert_mem_equal(test, decomp, zeros, N_ZEROS,
403 : "fixed lzxpress_decompress data");
404 :
405 0 : talloc_free(tmp_ctx);
406 0 : return true;
407 : }
408 :
409 :
410 0 : static bool test_lzxpress_round_trip(struct torture_context *test)
411 : {
412 : /*
413 : * Examples found using via fuzzing.
414 : */
415 0 : TALLOC_CTX *tmp_ctx = talloc_new(test);
416 : size_t i;
417 : struct b64_pair {
418 : const char *uncompressed;
419 : const char *compressed;
420 0 : } pairs[] = {
421 : { /* this results in a trailing flags block */
422 : "AAICAmq/EKdP785YU2Ddh7d4vUtdlQyLeHV09LHpUBw=",
423 : "AAAAAAACAgJqvxCnT+/OWFNg3Ye3eL1LXZUMi3h1dPSx6VAc/////w==",
424 : },
425 : { /* empty string compresses to empty string */
426 : "", ""
427 : },
428 : };
429 0 : const size_t alloc_size = 1000;
430 0 : uint8_t *data = talloc_array(tmp_ctx, uint8_t, alloc_size);
431 :
432 0 : for (i = 0; i < ARRAY_SIZE(pairs); i++) {
433 : ssize_t len;
434 0 : DATA_BLOB uncomp = base64_decode_data_blob_talloc(
435 : tmp_ctx,
436 : pairs[i].uncompressed);
437 0 : DATA_BLOB comp = base64_decode_data_blob_talloc(
438 : tmp_ctx,
439 : pairs[i].compressed);
440 :
441 0 : len = lzxpress_compress(uncomp.data,
442 0 : uncomp.length,
443 : data,
444 : alloc_size);
445 :
446 0 : torture_assert_int_equal(test, len, comp.length,
447 : "lzexpress compression size");
448 :
449 0 : torture_assert_mem_equal(test, comp.data, data, len,
450 : "lzxpress compression data");
451 :
452 0 : len = lzxpress_decompress(comp.data,
453 0 : comp.length,
454 : data,
455 : alloc_size);
456 :
457 0 : torture_assert_int_equal(test, len, uncomp.length,
458 : "lzexpress decompression size");
459 :
460 0 : torture_assert_mem_equal(test, uncomp.data, data, len,
461 : "lzxpress decompression data");
462 : }
463 0 : talloc_free(tmp_ctx);
464 0 : return true;
465 : }
466 :
467 :
468 964 : struct torture_suite *torture_local_compression(TALLOC_CTX *mem_ctx)
469 : {
470 964 : struct torture_suite *suite = torture_suite_create(mem_ctx, "compression");
471 :
472 964 : torture_suite_add_simple_test(suite, "lzxpress", test_lzxpress);
473 964 : torture_suite_add_simple_test(suite, "lzxpress_msft_data1", test_msft_data1);
474 964 : torture_suite_add_simple_test(suite, "lzxpress_msft_data2", test_msft_data2);
475 964 : torture_suite_add_simple_test(suite, "lzxpress2", test_lzxpress2);
476 964 : torture_suite_add_simple_test(suite, "lzxpress3", test_lzxpress3);
477 964 : torture_suite_add_simple_test(suite, "lzxpress4", test_lzxpress4);
478 964 : torture_suite_add_simple_test(suite, "lzxpress_many_zeros",
479 : test_lzxpress_many_zeros);
480 964 : torture_suite_add_simple_test(suite, "lzxpress_round_trip",
481 : test_lzxpress_round_trip);
482 964 : return suite;
483 : }
|