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
) {
39 uint8_t *data
= tuktest_file_from_srcdir(src
, &file_size
);
40 uint32_t checksum
= 0;
42 lzma_stream strm
= LZMA_STREAM_INIT
;
43 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
, 0), LZMA_OK
);
45 uint8_t *output_buffer
= tuktest_malloc(DECODE_CHUNK_SIZE
);
48 strm
.next_out
= output_buffer
;
49 strm
.avail_out
= DECODE_CHUNK_SIZE
;
51 // Feed 1 byte at a time to the decoder to look for any bugs
52 // when switching between decoding sequences
53 lzma_ret ret
= LZMA_OK
;
54 while (ret
== LZMA_OK
) {
56 ret
= lzma_code(&strm
, LZMA_RUN
);
57 if (strm
.avail_out
== 0) {
58 checksum
= lzma_crc32(output_buffer
,
59 (size_t)(strm
.next_out
- output_buffer
),
61 // No need to free output_buffer because it will
62 // automatically be freed at the end of the test by
64 output_buffer
= tuktest_malloc(DECODE_CHUNK_SIZE
);
65 strm
.next_out
= output_buffer
;
66 strm
.avail_out
= DECODE_CHUNK_SIZE
;
70 assert_lzma_ret(ret
, LZMA_STREAM_END
);
71 assert_uint_eq(strm
.total_in
, file_size
);
73 checksum
= lzma_crc32(output_buffer
,
74 (size_t)(strm
.next_out
- output_buffer
),
76 assert_uint_eq(checksum
, expected_crc
);
86 assert_lzma_ret(lzma_lzip_decoder(NULL
, MEMLIMIT
, 0),
90 lzma_stream strm
= LZMA_STREAM_INIT
;
91 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
, UINT32_MAX
),
93 // Memlimit tests are done elsewhere
98 test_v0_decode(void) {
99 // This tests if liblzma can decode lzip version 0 files.
100 // lzip 1.17 and older can decompress this, but lzip 1.18
101 // and newer can no longer decode these files.
102 basic_lzip_decode("files/good-1-v0.lz", hello_world_crc
);
107 test_v1_decode(void) {
108 // This tests decoding a basic lzip v1 file
109 basic_lzip_decode("files/good-1-v1.lz", hello_world_crc
);
113 // Helper function to decode a good file with trailing bytes after
116 trailing_helper(const char *src
, const uint32_t expected_data_checksum
,
117 const uint32_t expected_trailing_checksum
) {
119 uint32_t checksum
= 0;
120 uint8_t *data
= tuktest_file_from_srcdir(src
, &file_size
);
121 lzma_stream strm
= LZMA_STREAM_INIT
;
122 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
123 LZMA_CONCATENATED
), LZMA_OK
);
125 uint8_t *output_buffer
= tuktest_malloc(DECODE_CHUNK_SIZE
);
128 strm
.next_out
= output_buffer
;
129 strm
.avail_in
= file_size
;
130 strm
.avail_out
= DECODE_CHUNK_SIZE
;
132 lzma_ret ret
= LZMA_OK
;
133 while (ret
== LZMA_OK
) {
134 ret
= lzma_code(&strm
, LZMA_RUN
);
135 if (strm
.avail_out
== 0) {
136 checksum
= lzma_crc32(output_buffer
,
137 (size_t)(strm
.next_out
- output_buffer
),
139 // No need to free output_buffer because it will
140 // automatically be freed at the end of the test by
142 output_buffer
= tuktest_malloc(DECODE_CHUNK_SIZE
);
143 strm
.next_out
= output_buffer
;
144 strm
.avail_out
= DECODE_CHUNK_SIZE
;
148 assert_lzma_ret(ret
, LZMA_STREAM_END
);
149 assert_uint(strm
.total_in
, <, file_size
);
151 checksum
= lzma_crc32(output_buffer
,
152 (size_t)(strm
.next_out
- output_buffer
),
155 assert_uint_eq(checksum
, expected_data_checksum
);
157 // Trailing data should be readable from strm.next_in
158 checksum
= lzma_crc32(strm
.next_in
, strm
.avail_in
, 0);
159 assert_uint_eq(checksum
, expected_trailing_checksum
);
165 // Helper function to decode a bad file and compare to returned error to
166 // what the caller expects
168 decode_expect_error(const char *src
, lzma_ret expected_error
)
170 lzma_stream strm
= LZMA_STREAM_INIT
;
172 uint8_t *data
= tuktest_file_from_srcdir(src
, &file_size
);
174 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
175 LZMA_CONCATENATED
), LZMA_OK
);
177 uint8_t output_buffer
[DECODE_CHUNK_SIZE
];
179 strm
.avail_in
= file_size
;
181 strm
.avail_out
= DECODE_CHUNK_SIZE
;
182 strm
.next_out
= output_buffer
;
184 lzma_ret ret
= LZMA_OK
;
186 while (ret
== LZMA_OK
) {
187 // Discard output since we are only looking for errors
188 strm
.next_out
= output_buffer
;
189 strm
.avail_out
= DECODE_CHUNK_SIZE
;
190 if (strm
.avail_in
== 0)
191 ret
= lzma_code(&strm
, LZMA_FINISH
);
193 ret
= lzma_code(&strm
, LZMA_RUN
);
196 assert_lzma_ret(ret
, expected_error
);
202 test_v0_trailing(void) {
203 trailing_helper("files/good-1-v0-trailing-1.lz", hello_world_crc
,
204 trailing_garbage_crc
);
209 test_v1_trailing(void) {
210 trailing_helper("files/good-1-v1-trailing-1.lz", hello_world_crc
,
211 trailing_garbage_crc
);
213 // The second files/good-1-v1-trailing-2.lz will have the same
214 // expected output and trailing output as
215 // files/good-1-v1-trailing-1.lz, but this tests if the prefix
216 // to the trailing data contains lzip magic bytes.
217 // When this happens, the expected behavior is to silently ignore
218 // the magic byte prefix and consume it from the input file.
219 trailing_helper("files/good-1-v1-trailing-2.lz", hello_world_crc
,
220 trailing_garbage_crc
);
222 // Expect LZMA_BUF error if a file ends with the lzip magic bytes
223 // but does not contain any data after
224 decode_expect_error("files/bad-1-v1-trailing-magic.lz",
230 test_concatentated(void)
232 // First test a file with one v0 member and one v1 member
233 // The first member should contain "Hello\n" and
234 // the second member should contain "World!\n"
236 lzma_stream strm
= LZMA_STREAM_INIT
;
238 uint8_t *v0_v1
= tuktest_file_from_srcdir("files/good-2-v0-v1.lz",
241 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
242 LZMA_CONCATENATED
), LZMA_OK
);
244 uint8_t output_buffer
[DECODE_CHUNK_SIZE
];
246 strm
.avail_in
= file_size
;
247 strm
.next_in
= v0_v1
;
248 strm
.avail_out
= DECODE_CHUNK_SIZE
;
249 strm
.next_out
= output_buffer
;
251 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_STREAM_END
);
253 assert_uint_eq(strm
.total_in
, file_size
);
255 uint32_t checksum
= lzma_crc32(output_buffer
, strm
.total_out
, 0);
256 assert_uint_eq(checksum
, hello_world_crc
);
258 // The second file contains one v1 member and one v2 member
259 uint8_t *v1_v0
= tuktest_file_from_srcdir("files/good-2-v1-v0.lz",
262 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
263 LZMA_CONCATENATED
), LZMA_OK
);
265 strm
.avail_in
= file_size
;
266 strm
.next_in
= v1_v0
;
267 strm
.avail_out
= DECODE_CHUNK_SIZE
;
268 strm
.next_out
= output_buffer
;
270 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_STREAM_END
);
272 assert_uint_eq(strm
.total_in
, file_size
);
273 checksum
= lzma_crc32(output_buffer
, strm
.total_out
, 0);
274 assert_uint_eq(checksum
, hello_world_crc
);
276 // The third file contains 2 v1 members
277 uint8_t *v1_v1
= tuktest_file_from_srcdir("files/good-2-v1-v1.lz",
280 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
281 LZMA_CONCATENATED
), LZMA_OK
);
283 strm
.avail_in
= file_size
;
284 strm
.next_in
= v1_v1
;
285 strm
.avail_out
= DECODE_CHUNK_SIZE
;
286 strm
.next_out
= output_buffer
;
288 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_STREAM_END
);
290 assert_uint_eq(strm
.total_in
, file_size
);
291 checksum
= lzma_crc32(output_buffer
, strm
.total_out
, 0);
292 assert_uint_eq(checksum
, hello_world_crc
);
300 // Test invalid checksum
301 lzma_stream strm
= LZMA_STREAM_INIT
;
303 uint8_t *data
= tuktest_file_from_srcdir("files/bad-1-v1-crc32.lz",
306 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
307 LZMA_CONCATENATED
), LZMA_OK
);
309 uint8_t output_buffer
[DECODE_CHUNK_SIZE
];
311 strm
.avail_in
= file_size
;
313 strm
.avail_out
= DECODE_CHUNK_SIZE
;
314 strm
.next_out
= output_buffer
;
316 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_DATA_ERROR
);
318 // Test ignoring the checksum value - should decode successfully
319 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
320 LZMA_CONCATENATED
| LZMA_IGNORE_CHECK
), LZMA_OK
);
322 strm
.avail_in
= file_size
;
324 strm
.avail_out
= DECODE_CHUNK_SIZE
;
325 strm
.next_out
= output_buffer
;
327 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_STREAM_END
);
328 assert_uint_eq(strm
.total_in
, file_size
);
331 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
332 LZMA_CONCATENATED
| LZMA_TELL_ANY_CHECK
), LZMA_OK
);
334 strm
.avail_in
= file_size
;
336 strm
.avail_out
= DECODE_CHUNK_SIZE
;
337 strm
.next_out
= output_buffer
;
339 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_GET_CHECK
);
340 assert_uint_eq(lzma_get_check(&strm
), LZMA_CHECK_CRC32
);
341 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_DATA_ERROR
);
347 test_invalid_magic_bytes(void) {
348 uint8_t lzip_id_string
[] = { 0x4C, 0x5A, 0x49, 0x50 };
349 lzma_stream strm
= LZMA_STREAM_INIT
;
351 for (uint32_t i
= 0; i
< ARRAY_SIZE(lzip_id_string
); i
++) {
352 // Corrupt magic bytes
353 lzip_id_string
[i
] ^= 1;
354 uint8_t output_buffer
[DECODE_CHUNK_SIZE
];
356 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
, 0),
359 strm
.next_in
= lzip_id_string
;
360 strm
.avail_in
= sizeof(lzip_id_string
);
361 strm
.next_out
= output_buffer
;
362 strm
.avail_out
= DECODE_CHUNK_SIZE
;
364 assert_lzma_ret(lzma_code(&strm
, LZMA_RUN
),
368 lzip_id_string
[i
] ^= 1;
376 test_invalid_version(void)
378 // The file contains a version number that is not 0 or 1,
379 // so it should cause an error
380 decode_expect_error("files/unsupported-1-v234.lz",
386 test_invalid_dictionary_size(void) {
387 // First file has too small dictionary size field
388 decode_expect_error("files/bad-1-v1-dict-1.lz", LZMA_DATA_ERROR
);
390 // Second file has too large dictionary size field
391 decode_expect_error("files/bad-1-v1-dict-2.lz", LZMA_DATA_ERROR
);
396 test_invalid_uncomp_size(void) {
397 // Test invalid v0 lzip file uncomp size
398 decode_expect_error("files/bad-1-v0-uncomp-size.lz",
401 // Test invalid v1 lzip file uncomp size
402 decode_expect_error("files/bad-1-v1-uncomp-size.lz",
408 test_invalid_member_size(void) {
409 decode_expect_error("files/bad-1-v1-member-size.lz",
415 test_invalid_memlimit(void) {
416 // A very low memlimit should prevent decoding.
417 // Should be able to update the memlimit after failing
419 uint8_t *data
= tuktest_file_from_srcdir("files/good-1-v1.lz",
422 uint8_t output_buffer
[DECODE_CHUNK_SIZE
];
424 lzma_stream strm
= LZMA_STREAM_INIT
;
426 assert_lzma_ret(lzma_lzip_decoder(&strm
, 1, 0), LZMA_OK
);
429 strm
.avail_in
= file_size
;
430 strm
.next_out
= output_buffer
;
431 strm
.avail_out
= DECODE_CHUNK_SIZE
;
433 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_MEMLIMIT_ERROR
);
435 // Up the memlimit so decoding can continue.
436 // First only increase by a small amount and expect an error
437 assert_lzma_ret(lzma_memlimit_set(&strm
, 100), LZMA_MEMLIMIT_ERROR
);
438 assert_lzma_ret(lzma_memlimit_set(&strm
, MEMLIMIT
), LZMA_OK
);
441 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_STREAM_END
);
443 assert_uint_eq(strm
.total_in
, file_size
);
444 uint32_t checksum
= lzma_crc32(output_buffer
, strm
.total_out
, 0);
445 assert_uint_eq(checksum
, hello_world_crc
);
453 main(int argc
, char **argv
)
455 tuktest_start(argc
, argv
);
457 #ifndef HAVE_LZIP_DECODER
458 tuktest_early_skip("lzip decoder disabled");
460 tuktest_run(test_options
);
461 tuktest_run(test_v0_decode
);
462 tuktest_run(test_v1_decode
);
463 tuktest_run(test_v0_trailing
);
464 tuktest_run(test_v1_trailing
);
465 tuktest_run(test_concatentated
);
466 tuktest_run(test_crc
);
467 tuktest_run(test_invalid_magic_bytes
);
468 tuktest_run(test_invalid_version
);
469 tuktest_run(test_invalid_dictionary_size
);
470 tuktest_run(test_invalid_uncomp_size
);
471 tuktest_run(test_invalid_member_size
);
472 tuktest_run(test_invalid_memlimit
);
473 return tuktest_end();