1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file test_lzip_decoder.c
4 /// \brief Tests decoding lzip data
5 //
6 // Author: Jia Tan
7 //
8 // This file has been put into the public domain.
9 // You can do whatever you want with this file.
10 //
11 ///////////////////////////////////////////////////////////////////////////////
12
13 #include "tests.h"
14
15 #ifdef HAVE_LZIP_DECODER
16
17 // Memlimit large enough to pass all of the test files
18 #define MEMLIMIT (1U << 20)
19 #define DECODE_CHUNK_SIZE 1024
20
21
22 // The uncompressed data in the test files are short US-ASCII strings.
23 // The tests check if the decompressed output is what it is expected to be.
24 // Storing the strings here as text would break the tests on EBCDIC systems
25 // and storing the strings as an array of hex values is inconvenient, so
26 // store the CRC32 values of the expected data instead.
27 //
28 // CRC32 value of "Hello\nWorld\n"
29 static const uint32_t hello_world_crc = 0x15A2A343;
30
31 // CRC32 value of "Trailing garbage\n"
32 static const uint32_t trailing_garbage_crc = 0x87081A60;
33
34
35 // Helper function to decode a good file with no flags and plenty high memlimit
36 static void
37 basic_lzip_decode(const char *src, const uint32_t expected_crc)
38 {
39 size_t file_size;
40 uint8_t *data = tuktest_file_from_srcdir(src, &file_size);
41 uint32_t checksum = 0;
42
43 lzma_stream strm = LZMA_STREAM_INIT;
44 assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, 0), LZMA_OK);
45
46 uint8_t *output_buffer = tuktest_malloc(DECODE_CHUNK_SIZE);
47
48 strm.next_in = data;
49 strm.next_out = output_buffer;
50 strm.avail_out = DECODE_CHUNK_SIZE;
51
52 // Feed 1 byte at a time to the decoder to look for any bugs
53 // when switching between decoding sequences
54 lzma_ret ret = LZMA_OK;
55 while (ret == LZMA_OK) {
56 strm.avail_in = 1;
57 ret = lzma_code(&strm, LZMA_RUN);
58 if (strm.avail_out == 0) {
59 checksum = lzma_crc32(output_buffer,
60 (size_t)(strm.next_out - output_buffer),
61 checksum);
62 // No need to free output_buffer because it will
63 // automatically be freed at the end of the test by
64 // tuktest.
65 output_buffer = tuktest_malloc(DECODE_CHUNK_SIZE);
66 strm.next_out = output_buffer;
67 strm.avail_out = DECODE_CHUNK_SIZE;
68 }
69 }
70
71 assert_lzma_ret(ret, LZMA_STREAM_END);
72 assert_uint_eq(strm.total_in, file_size);
73
74 checksum = lzma_crc32(output_buffer,
75 (size_t)(strm.next_out - output_buffer),
76 checksum);
77 assert_uint_eq(checksum, expected_crc);
78
79 lzma_end(&strm);
80 }
81
82
83 static void
84 test_options(void)
85 {
86 // Test NULL stream
87 assert_lzma_ret(lzma_lzip_decoder(NULL, MEMLIMIT, 0),
88 LZMA_PROG_ERROR);
89
90 // Test invalid flags
91 lzma_stream strm = LZMA_STREAM_INIT;
92 assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, UINT32_MAX),
93 LZMA_OPTIONS_ERROR);
94 // Memlimit tests are done elsewhere
95 }
96
97
98 static void
99 test_v0_decode(void)
100 {
101 // This tests if liblzma can decode lzip version 0 files.
102 // lzip 1.17 and older can decompress this, but lzip 1.18
103 // and newer can no longer decode these files.
104 basic_lzip_decode("files/good-1-v0.lz", hello_world_crc);
105 }
106
107
108 static void
109 test_v1_decode(void)
110 {
111 // This tests decoding a basic lzip v1 file
112 basic_lzip_decode("files/good-1-v1.lz", hello_world_crc);
113 }
114
115
116 // Helper function to decode a good file with trailing bytes after
117 // the lzip stream
118 static void
119 trailing_helper(const char *src, const uint32_t expected_data_checksum,
120 const uint32_t expected_trailing_checksum)
121 {
122 size_t file_size;
123 uint32_t checksum = 0;
124 uint8_t *data = tuktest_file_from_srcdir(src, &file_size);
125 lzma_stream strm = LZMA_STREAM_INIT;
126 assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
127 LZMA_CONCATENATED), LZMA_OK);
128
129 uint8_t *output_buffer = tuktest_malloc(DECODE_CHUNK_SIZE);
130
131 strm.next_in = data;
132 strm.next_out = output_buffer;
133 strm.avail_in = file_size;
134 strm.avail_out = DECODE_CHUNK_SIZE;
135
136 lzma_ret ret = LZMA_OK;
137 while (ret == LZMA_OK) {
138 ret = lzma_code(&strm, LZMA_RUN);
139 if (strm.avail_out == 0) {
140 checksum = lzma_crc32(output_buffer,
141 (size_t)(strm.next_out - output_buffer),
142 checksum);
143 // No need to free output_buffer because it will
144 // automatically be freed at the end of the test by
145 // tuktest.
146 output_buffer = tuktest_malloc(DECODE_CHUNK_SIZE);
147 strm.next_out = output_buffer;
148 strm.avail_out = DECODE_CHUNK_SIZE;
149 }
150 }
151
152 assert_lzma_ret(ret, LZMA_STREAM_END);
153 assert_uint(strm.total_in, <, file_size);
154
155 checksum = lzma_crc32(output_buffer,
156 (size_t)(strm.next_out - output_buffer),
157 checksum);
158
159 assert_uint_eq(checksum, expected_data_checksum);
160
161 // Trailing data should be readable from strm.next_in
162 checksum = lzma_crc32(strm.next_in, strm.avail_in, 0);
163 assert_uint_eq(checksum, expected_trailing_checksum);
164
165 lzma_end(&strm);
166 }
167
168
169 // Helper function to decode a bad file and compare to returned error to
170 // what the caller expects
171 static void
172 decode_expect_error(const char *src, lzma_ret expected_error)
173 {
174 lzma_stream strm = LZMA_STREAM_INIT;
175 size_t file_size;
176 uint8_t *data = tuktest_file_from_srcdir(src, &file_size);
177
178 assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
179 LZMA_CONCATENATED), LZMA_OK);
180
181 uint8_t output_buffer[DECODE_CHUNK_SIZE];
182
183 strm.avail_in = file_size;
184 strm.next_in = data;
185 strm.avail_out = DECODE_CHUNK_SIZE;
186 strm.next_out = output_buffer;
187
188 lzma_ret ret = LZMA_OK;
189
190 while (ret == LZMA_OK) {
191 // Discard output since we are only looking for errors
192 strm.next_out = output_buffer;
193 strm.avail_out = DECODE_CHUNK_SIZE;
194 if (strm.avail_in == 0)
195 ret = lzma_code(&strm, LZMA_FINISH);
196 else
197 ret = lzma_code(&strm, LZMA_RUN);
198 }
199
200 assert_lzma_ret(ret, expected_error);
201 lzma_end(&strm);
202 }
203
204
205 static void
206 test_v0_trailing(void)
207 {
208 trailing_helper("files/good-1-v0-trailing-1.lz", hello_world_crc,
209 trailing_garbage_crc);
210 }
211
212
213 static void
214 test_v1_trailing(void)
215 {
216 trailing_helper("files/good-1-v1-trailing-1.lz", hello_world_crc,
217 trailing_garbage_crc);
218
219 // The second files/good-1-v1-trailing-2.lz will have the same
220 // expected output and trailing output as
221 // files/good-1-v1-trailing-1.lz, but this tests if the prefix
222 // to the trailing data contains lzip magic bytes.
223 // When this happens, the expected behavior is to silently ignore
224 // the magic byte prefix and consume it from the input file.
225 trailing_helper("files/good-1-v1-trailing-2.lz", hello_world_crc,
226 trailing_garbage_crc);
227
228 // Expect LZMA_BUF error if a file ends with the lzip magic bytes
229 // but does not contain any data after
230 decode_expect_error("files/bad-1-v1-trailing-magic.lz",
231 LZMA_BUF_ERROR);
232 }
233
234
235 static void
236 test_concatentated(void)
237 {
238 // First test a file with one v0 member and one v1 member
239 // The first member should contain "Hello\n" and
240 // the second member should contain "World!\n"
241
242 lzma_stream strm = LZMA_STREAM_INIT;
243 size_t file_size;
244 uint8_t *v0_v1 = tuktest_file_from_srcdir("files/good-2-v0-v1.lz",
245 &file_size);
246
247 assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
248 LZMA_CONCATENATED), LZMA_OK);
249
250 uint8_t output_buffer[DECODE_CHUNK_SIZE];
251
252 strm.avail_in = file_size;
253 strm.next_in = v0_v1;
254 strm.avail_out = DECODE_CHUNK_SIZE;
255 strm.next_out = output_buffer;
256
257 assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END);
258
259 assert_uint_eq(strm.total_in, file_size);
260
261 uint32_t checksum = lzma_crc32(output_buffer, strm.total_out, 0);
262 assert_uint_eq(checksum, hello_world_crc);
263
264 // The second file contains one v1 member and one v2 member
265 uint8_t *v1_v0 = tuktest_file_from_srcdir("files/good-2-v1-v0.lz",
266 &file_size);
267
268 assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
269 LZMA_CONCATENATED), LZMA_OK);
270
271 strm.avail_in = file_size;
272 strm.next_in = v1_v0;
273 strm.avail_out = DECODE_CHUNK_SIZE;
274 strm.next_out = output_buffer;
275
276 assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END);
277
278 assert_uint_eq(strm.total_in, file_size);
279 checksum = lzma_crc32(output_buffer, strm.total_out, 0);
280 assert_uint_eq(checksum, hello_world_crc);
281
282 // The third file contains 2 v1 members
283 uint8_t *v1_v1 = tuktest_file_from_srcdir("files/good-2-v1-v1.lz",
284 &file_size);
285
286 assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
287 LZMA_CONCATENATED), LZMA_OK);
288
289 strm.avail_in = file_size;
290 strm.next_in = v1_v1;
291 strm.avail_out = DECODE_CHUNK_SIZE;
292 strm.next_out = output_buffer;
293
294 assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END);
295
296 assert_uint_eq(strm.total_in, file_size);
297 checksum = lzma_crc32(output_buffer, strm.total_out, 0);
298 assert_uint_eq(checksum, hello_world_crc);
299
300 lzma_end(&strm);
301 }
302
303
304 static void
305 test_crc(void)
306 {
307 // Test invalid checksum
308 lzma_stream strm = LZMA_STREAM_INIT;
309 size_t file_size;
310 uint8_t *data = tuktest_file_from_srcdir("files/bad-1-v1-crc32.lz",
311 &file_size);
312
313 assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
314 LZMA_CONCATENATED), LZMA_OK);
315
316 uint8_t output_buffer[DECODE_CHUNK_SIZE];
317
318 strm.avail_in = file_size;
319 strm.next_in = data;
320 strm.avail_out = DECODE_CHUNK_SIZE;
321 strm.next_out = output_buffer;
322
323 assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_DATA_ERROR);
324
325 // Test ignoring the checksum value - should decode successfully
326 assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
327 LZMA_CONCATENATED | LZMA_IGNORE_CHECK), LZMA_OK);
328
329 strm.avail_in = file_size;
330 strm.next_in = data;
331 strm.avail_out = DECODE_CHUNK_SIZE;
332 strm.next_out = output_buffer;
333
334 assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END);
335 assert_uint_eq(strm.total_in, file_size);
336
337 // Test tell check
338 assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
339 LZMA_CONCATENATED | LZMA_TELL_ANY_CHECK), LZMA_OK);
340
341 strm.avail_in = file_size;
342 strm.next_in = data;
343 strm.avail_out = DECODE_CHUNK_SIZE;
344 strm.next_out = output_buffer;
345
346 assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_GET_CHECK);
347 assert_uint_eq(lzma_get_check(&strm), LZMA_CHECK_CRC32);
348 assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_DATA_ERROR);
349 lzma_end(&strm);
350 }
351
352
353 static void
354 test_invalid_magic_bytes(void)
355 {
356 uint8_t lzip_id_string[] = { 0x4C, 0x5A, 0x49, 0x50 };
357 lzma_stream strm = LZMA_STREAM_INIT;
358
359 for (uint32_t i = 0; i < ARRAY_SIZE(lzip_id_string); i++) {
360 // Corrupt magic bytes
361 lzip_id_string[i] ^= 1;
362 uint8_t output_buffer[DECODE_CHUNK_SIZE];
363
364 assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, 0),
365 LZMA_OK);
366
367 strm.next_in = lzip_id_string;
368 strm.avail_in = sizeof(lzip_id_string);
369 strm.next_out = output_buffer;
370 strm.avail_out = DECODE_CHUNK_SIZE;
371
372 assert_lzma_ret(lzma_code(&strm, LZMA_RUN),
373 LZMA_FORMAT_ERROR);
374
375 // Reset magic bytes
376 lzip_id_string[i] ^= 1;
377 }
378
379 lzma_end(&strm);
380 }
381
382
383 static void
384 test_invalid_version(void)
385 {
386 // The file contains a version number that is not 0 or 1,
387 // so it should cause an error
388 decode_expect_error("files/unsupported-1-v234.lz",
389 LZMA_OPTIONS_ERROR);
390 }
391
392
393 static void
394 test_invalid_dictionary_size(void)
395 {
396 // First file has too small dictionary size field
397 decode_expect_error("files/bad-1-v1-dict-1.lz", LZMA_DATA_ERROR);
398
399 // Second file has too large dictionary size field
400 decode_expect_error("files/bad-1-v1-dict-2.lz", LZMA_DATA_ERROR);
401 }
402
403
404 static void
405 test_invalid_uncomp_size(void)
406 {
407 // Test invalid v0 lzip file uncomp size
408 decode_expect_error("files/bad-1-v0-uncomp-size.lz",
409 LZMA_DATA_ERROR);
410
411 // Test invalid v1 lzip file uncomp size
412 decode_expect_error("files/bad-1-v1-uncomp-size.lz",
413 LZMA_DATA_ERROR);
414 }
415
416
417 static void
418 test_invalid_member_size(void)
419 {
420 decode_expect_error("files/bad-1-v1-member-size.lz",
421 LZMA_DATA_ERROR);
422 }
423
424
425 static void
426 test_invalid_memlimit(void)
427 {
428 // A very low memlimit should prevent decoding.
429 // Should be able to update the memlimit after failing
430 size_t file_size;
431 uint8_t *data = tuktest_file_from_srcdir("files/good-1-v1.lz",
432 &file_size);
433
434 uint8_t output_buffer[DECODE_CHUNK_SIZE];
435
436 lzma_stream strm = LZMA_STREAM_INIT;
437
438 assert_lzma_ret(lzma_lzip_decoder(&strm, 1, 0), LZMA_OK);
439
440 strm.next_in = data;
441 strm.avail_in = file_size;
442 strm.next_out = output_buffer;
443 strm.avail_out = DECODE_CHUNK_SIZE;
444
445 assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_MEMLIMIT_ERROR);
446
447 // Up the memlimit so decoding can continue.
448 // First only increase by a small amount and expect an error
449 assert_lzma_ret(lzma_memlimit_set(&strm, 100), LZMA_MEMLIMIT_ERROR);
450 assert_lzma_ret(lzma_memlimit_set(&strm, MEMLIMIT), LZMA_OK);
451
452 // Finish decoding
453 assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END);
454
455 assert_uint_eq(strm.total_in, file_size);
456 uint32_t checksum = lzma_crc32(output_buffer, strm.total_out, 0);
457 assert_uint_eq(checksum, hello_world_crc);
458
459 lzma_end(&strm);
460 }
461 #endif
462
463
464 extern int
465 main(int argc, char **argv)
466 {
467 tuktest_start(argc, argv);
468
469 #ifndef HAVE_LZIP_DECODER
470 tuktest_early_skip("lzip decoder disabled");
471 #else
472 tuktest_run(test_options);
473 tuktest_run(test_v0_decode);
474 tuktest_run(test_v1_decode);
475 tuktest_run(test_v0_trailing);
476 tuktest_run(test_v1_trailing);
477 tuktest_run(test_concatentated);
478 tuktest_run(test_crc);
479 tuktest_run(test_invalid_magic_bytes);
480 tuktest_run(test_invalid_version);
481 tuktest_run(test_invalid_dictionary_size);
482 tuktest_run(test_invalid_uncomp_size);
483 tuktest_run(test_invalid_member_size);
484 tuktest_run(test_invalid_memlimit);
485 return tuktest_end();
486 #endif
487
488 }