1 ///////////////////////////////////////////////////////////////////////////////
3 /// \file test_lzip_decoder.c
4 /// \brief Tests decoding lzip data
8 // This file has been put into the public domain.
9 // You can do whatever you want with this file.
11 ///////////////////////////////////////////////////////////////////////////////
15 #ifdef HAVE_LZIP_DECODER
17 // Memlimit large enough to pass all of the test files
18 #define MEMLIMIT (1U << 20)
19 #define DECODE_CHUNK_SIZE 1024
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.
28 // CRC32 value of "Hello\nWorld\n"
29 static const uint32_t hello_world_crc
= 0x15A2A343;
31 // CRC32 value of "Trailing garbage\n"
32 static const uint32_t trailing_garbage_crc
= 0x87081A60;
35 // Helper function to decode a good file with no flags and plenty high memlimit
37 basic_lzip_decode(const char *src
, const uint32_t expected_crc
)
40 uint8_t *data
= tuktest_file_from_srcdir(src
, &file_size
);
41 uint32_t checksum
= 0;
43 lzma_stream strm
= LZMA_STREAM_INIT
;
44 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
, 0), LZMA_OK
);
46 uint8_t *output_buffer
= tuktest_malloc(DECODE_CHUNK_SIZE
);
49 strm
.next_out
= output_buffer
;
50 strm
.avail_out
= DECODE_CHUNK_SIZE
;
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
) {
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
),
62 // No need to free output_buffer because it will
63 // automatically be freed at the end of the test by
65 output_buffer
= tuktest_malloc(DECODE_CHUNK_SIZE
);
66 strm
.next_out
= output_buffer
;
67 strm
.avail_out
= DECODE_CHUNK_SIZE
;
71 assert_lzma_ret(ret
, LZMA_STREAM_END
);
72 assert_uint_eq(strm
.total_in
, file_size
);
74 checksum
= lzma_crc32(output_buffer
,
75 (size_t)(strm
.next_out
- output_buffer
),
77 assert_uint_eq(checksum
, expected_crc
);
87 assert_lzma_ret(lzma_lzip_decoder(NULL
, MEMLIMIT
, 0),
91 lzma_stream strm
= LZMA_STREAM_INIT
;
92 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
, UINT32_MAX
),
94 // Memlimit tests are done elsewhere
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
);
111 // This tests decoding a basic lzip v1 file
112 basic_lzip_decode("files/good-1-v1.lz", hello_world_crc
);
116 // Helper function to decode a good file with trailing bytes after
119 trailing_helper(const char *src
, const uint32_t expected_data_checksum
,
120 const uint32_t expected_trailing_checksum
)
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
);
129 uint8_t *output_buffer
= tuktest_malloc(DECODE_CHUNK_SIZE
);
132 strm
.next_out
= output_buffer
;
133 strm
.avail_in
= file_size
;
134 strm
.avail_out
= DECODE_CHUNK_SIZE
;
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
),
143 // No need to free output_buffer because it will
144 // automatically be freed at the end of the test by
146 output_buffer
= tuktest_malloc(DECODE_CHUNK_SIZE
);
147 strm
.next_out
= output_buffer
;
148 strm
.avail_out
= DECODE_CHUNK_SIZE
;
152 assert_lzma_ret(ret
, LZMA_STREAM_END
);
153 assert_uint(strm
.total_in
, <, file_size
);
155 checksum
= lzma_crc32(output_buffer
,
156 (size_t)(strm
.next_out
- output_buffer
),
159 assert_uint_eq(checksum
, expected_data_checksum
);
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
);
169 // Helper function to decode a bad file and compare to returned error to
170 // what the caller expects
172 decode_expect_error(const char *src
, lzma_ret expected_error
)
174 lzma_stream strm
= LZMA_STREAM_INIT
;
176 uint8_t *data
= tuktest_file_from_srcdir(src
, &file_size
);
178 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
179 LZMA_CONCATENATED
), LZMA_OK
);
181 uint8_t output_buffer
[DECODE_CHUNK_SIZE
];
183 strm
.avail_in
= file_size
;
185 strm
.avail_out
= DECODE_CHUNK_SIZE
;
186 strm
.next_out
= output_buffer
;
188 lzma_ret ret
= LZMA_OK
;
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
);
197 ret
= lzma_code(&strm
, LZMA_RUN
);
200 assert_lzma_ret(ret
, expected_error
);
206 test_v0_trailing(void)
208 trailing_helper("files/good-1-v0-trailing-1.lz", hello_world_crc
,
209 trailing_garbage_crc
);
214 test_v1_trailing(void)
216 trailing_helper("files/good-1-v1-trailing-1.lz", hello_world_crc
,
217 trailing_garbage_crc
);
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
);
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",
236 test_concatentated(void)
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"
242 lzma_stream strm
= LZMA_STREAM_INIT
;
244 uint8_t *v0_v1
= tuktest_file_from_srcdir("files/good-2-v0-v1.lz",
247 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
248 LZMA_CONCATENATED
), LZMA_OK
);
250 uint8_t output_buffer
[DECODE_CHUNK_SIZE
];
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
;
257 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_STREAM_END
);
259 assert_uint_eq(strm
.total_in
, file_size
);
261 uint32_t checksum
= lzma_crc32(output_buffer
, strm
.total_out
, 0);
262 assert_uint_eq(checksum
, hello_world_crc
);
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",
268 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
269 LZMA_CONCATENATED
), LZMA_OK
);
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
;
276 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_STREAM_END
);
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
);
282 // The third file contains 2 v1 members
283 uint8_t *v1_v1
= tuktest_file_from_srcdir("files/good-2-v1-v1.lz",
286 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
287 LZMA_CONCATENATED
), LZMA_OK
);
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
;
294 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_STREAM_END
);
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
);
307 // Test invalid checksum
308 lzma_stream strm
= LZMA_STREAM_INIT
;
310 uint8_t *data
= tuktest_file_from_srcdir("files/bad-1-v1-crc32.lz",
313 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
314 LZMA_CONCATENATED
), LZMA_OK
);
316 uint8_t output_buffer
[DECODE_CHUNK_SIZE
];
318 strm
.avail_in
= file_size
;
320 strm
.avail_out
= DECODE_CHUNK_SIZE
;
321 strm
.next_out
= output_buffer
;
323 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_DATA_ERROR
);
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
);
329 strm
.avail_in
= file_size
;
331 strm
.avail_out
= DECODE_CHUNK_SIZE
;
332 strm
.next_out
= output_buffer
;
334 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_STREAM_END
);
335 assert_uint_eq(strm
.total_in
, file_size
);
338 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
339 LZMA_CONCATENATED
| LZMA_TELL_ANY_CHECK
), LZMA_OK
);
341 strm
.avail_in
= file_size
;
343 strm
.avail_out
= DECODE_CHUNK_SIZE
;
344 strm
.next_out
= output_buffer
;
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
);
354 test_invalid_magic_bytes(void)
356 uint8_t lzip_id_string
[] = { 0x4C, 0x5A, 0x49, 0x50 };
357 lzma_stream strm
= LZMA_STREAM_INIT
;
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
];
364 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
, 0),
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
;
372 assert_lzma_ret(lzma_code(&strm
, LZMA_RUN
),
376 lzip_id_string
[i
] ^= 1;
384 test_invalid_version(void)
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",
394 test_invalid_dictionary_size(void)
396 // First file has too small dictionary size field
397 decode_expect_error("files/bad-1-v1-dict-1.lz", LZMA_DATA_ERROR
);
399 // Second file has too large dictionary size field
400 decode_expect_error("files/bad-1-v1-dict-2.lz", LZMA_DATA_ERROR
);
405 test_invalid_uncomp_size(void)
407 // Test invalid v0 lzip file uncomp size
408 decode_expect_error("files/bad-1-v0-uncomp-size.lz",
411 // Test invalid v1 lzip file uncomp size
412 decode_expect_error("files/bad-1-v1-uncomp-size.lz",
418 test_invalid_member_size(void)
420 decode_expect_error("files/bad-1-v1-member-size.lz",
426 test_invalid_memlimit(void)
428 // A very low memlimit should prevent decoding.
429 // Should be able to update the memlimit after failing
431 uint8_t *data
= tuktest_file_from_srcdir("files/good-1-v1.lz",
434 uint8_t output_buffer
[DECODE_CHUNK_SIZE
];
436 lzma_stream strm
= LZMA_STREAM_INIT
;
438 assert_lzma_ret(lzma_lzip_decoder(&strm
, 1, 0), LZMA_OK
);
441 strm
.avail_in
= file_size
;
442 strm
.next_out
= output_buffer
;
443 strm
.avail_out
= DECODE_CHUNK_SIZE
;
445 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_MEMLIMIT_ERROR
);
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
);
453 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_STREAM_END
);
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
);
465 main(int argc
, char **argv
)
467 tuktest_start(argc
, argv
);
469 #ifndef HAVE_LZIP_DECODER
470 tuktest_early_skip("lzip decoder disabled");
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();