1 // SPDX-License-Identifier: 0BSD
3 ///////////////////////////////////////////////////////////////////////////////
5 /// \file test_lzip_decoder.c
6 /// \brief Tests decoding lzip data
10 ///////////////////////////////////////////////////////////////////////////////
14 #ifdef HAVE_LZIP_DECODER
16 // Memlimit large enough to pass all of the test files
17 #define MEMLIMIT (1U << 20)
18 #define DECODE_CHUNK_SIZE 1024
21 // The uncompressed data in the test files are short US-ASCII strings.
22 // The tests check if the decompressed output is what it is expected to be.
23 // Storing the strings here as text would break the tests on EBCDIC systems
24 // and storing the strings as an array of hex values is inconvenient, so
25 // store the CRC32 values of the expected data instead.
27 // CRC32 value of "Hello\nWorld\n"
28 static const uint32_t hello_world_crc
= 0x15A2A343;
30 // CRC32 value of "Trailing garbage\n"
31 static const uint32_t trailing_garbage_crc
= 0x87081A60;
34 // Helper function to decode a good file with no flags and plenty high memlimit
36 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
[DECODE_CHUNK_SIZE
];
48 strm
.next_out
= output_buffer
;
49 strm
.avail_out
= sizeof(output_buffer
);
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 strm
.next_out
= output_buffer
;
62 strm
.avail_out
= sizeof(output_buffer
);
66 assert_lzma_ret(ret
, LZMA_STREAM_END
);
67 assert_uint_eq(strm
.total_in
, file_size
);
69 checksum
= lzma_crc32(output_buffer
,
70 (size_t)(strm
.next_out
- output_buffer
),
72 assert_uint_eq(checksum
, expected_crc
);
82 assert_lzma_ret(lzma_lzip_decoder(NULL
, MEMLIMIT
, 0),
86 lzma_stream strm
= LZMA_STREAM_INIT
;
87 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
, UINT32_MAX
),
89 // Memlimit tests are done elsewhere
96 // This tests if liblzma can decode lzip version 0 files.
97 // lzip 1.17 and older can decompress this, but lzip 1.18
98 // and newer can no longer decode these files.
99 basic_lzip_decode("files/good-1-v0.lz", hello_world_crc
);
106 // This tests decoding a basic lzip v1 file
107 basic_lzip_decode("files/good-1-v1.lz", hello_world_crc
);
111 // Helper function to decode a good file with trailing bytes after
114 trailing_helper(const char *src
, const uint32_t expected_data_checksum
,
115 const uint32_t expected_trailing_checksum
)
118 uint32_t checksum
= 0;
119 uint8_t *data
= tuktest_file_from_srcdir(src
, &file_size
);
120 lzma_stream strm
= LZMA_STREAM_INIT
;
121 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
122 LZMA_CONCATENATED
), LZMA_OK
);
124 uint8_t output_buffer
[DECODE_CHUNK_SIZE
];
127 strm
.avail_in
= file_size
;
128 strm
.next_out
= output_buffer
;
129 strm
.avail_out
= sizeof(output_buffer
);
131 lzma_ret ret
= LZMA_OK
;
132 while (ret
== LZMA_OK
) {
133 ret
= lzma_code(&strm
, LZMA_RUN
);
134 if (strm
.avail_out
== 0) {
135 checksum
= lzma_crc32(output_buffer
,
136 (size_t)(strm
.next_out
- output_buffer
),
138 strm
.next_out
= output_buffer
;
139 strm
.avail_out
= sizeof(output_buffer
);
143 assert_lzma_ret(ret
, LZMA_STREAM_END
);
144 assert_uint(strm
.total_in
, <, file_size
);
146 checksum
= lzma_crc32(output_buffer
,
147 (size_t)(strm
.next_out
- output_buffer
),
150 assert_uint_eq(checksum
, expected_data_checksum
);
152 // Trailing data should be readable from strm.next_in
153 checksum
= lzma_crc32(strm
.next_in
, strm
.avail_in
, 0);
154 assert_uint_eq(checksum
, expected_trailing_checksum
);
160 // Helper function to decode a bad file and compare to returned error to
161 // what the caller expects
163 decode_expect_error(const char *src
, lzma_ret expected_error
)
165 lzma_stream strm
= LZMA_STREAM_INIT
;
167 uint8_t *data
= tuktest_file_from_srcdir(src
, &file_size
);
169 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
170 LZMA_CONCATENATED
), LZMA_OK
);
172 uint8_t output_buffer
[DECODE_CHUNK_SIZE
];
174 strm
.avail_in
= file_size
;
177 lzma_ret ret
= LZMA_OK
;
180 // Discard output since we are only looking for errors
181 strm
.next_out
= output_buffer
;
182 strm
.avail_out
= sizeof(output_buffer
);
183 if (strm
.avail_in
== 0)
184 ret
= lzma_code(&strm
, LZMA_FINISH
);
186 ret
= lzma_code(&strm
, LZMA_RUN
);
187 } while (ret
== LZMA_OK
);
189 assert_lzma_ret(ret
, expected_error
);
195 test_v0_trailing(void)
197 trailing_helper("files/good-1-v0-trailing-1.lz", hello_world_crc
,
198 trailing_garbage_crc
);
203 test_v1_trailing(void)
205 trailing_helper("files/good-1-v1-trailing-1.lz", hello_world_crc
,
206 trailing_garbage_crc
);
208 // The second files/good-1-v1-trailing-2.lz will have the same
209 // expected output and trailing output as
210 // files/good-1-v1-trailing-1.lz, but this tests if the prefix
211 // to the trailing data contains lzip magic bytes.
212 // When this happens, the expected behavior is to silently ignore
213 // the magic byte prefix and consume it from the input file.
214 trailing_helper("files/good-1-v1-trailing-2.lz", hello_world_crc
,
215 trailing_garbage_crc
);
217 // Expect LZMA_BUF error if a file ends with the lzip magic bytes
218 // but does not contain any data after
219 decode_expect_error("files/bad-1-v1-trailing-magic.lz",
225 test_concatenated(void)
227 // First test a file with one v0 member and one v1 member
228 // The first member should contain "Hello\n" and
229 // the second member should contain "World!\n"
230 lzma_stream strm
= LZMA_STREAM_INIT
;
232 uint8_t *v0_v1
= tuktest_file_from_srcdir("files/good-2-v0-v1.lz",
235 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
236 LZMA_CONCATENATED
), LZMA_OK
);
238 uint8_t output_buffer
[DECODE_CHUNK_SIZE
];
240 strm
.next_in
= v0_v1
;
241 strm
.avail_in
= file_size
;
242 strm
.next_out
= output_buffer
;
243 strm
.avail_out
= sizeof(output_buffer
);
245 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_STREAM_END
);
247 assert_uint_eq(strm
.total_in
, file_size
);
249 uint32_t checksum
= lzma_crc32(output_buffer
, strm
.total_out
, 0);
250 assert_uint_eq(checksum
, hello_world_crc
);
252 // The second file contains one v1 member and one v2 member
253 uint8_t *v1_v0
= tuktest_file_from_srcdir("files/good-2-v1-v0.lz",
256 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
257 LZMA_CONCATENATED
), LZMA_OK
);
259 strm
.next_in
= v1_v0
;
260 strm
.avail_in
= file_size
;
261 strm
.next_out
= output_buffer
;
262 strm
.avail_out
= sizeof(output_buffer
);
264 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_STREAM_END
);
266 assert_uint_eq(strm
.total_in
, file_size
);
267 checksum
= lzma_crc32(output_buffer
, strm
.total_out
, 0);
268 assert_uint_eq(checksum
, hello_world_crc
);
270 // The third file contains 2 v1 members
271 uint8_t *v1_v1
= tuktest_file_from_srcdir("files/good-2-v1-v1.lz",
274 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
275 LZMA_CONCATENATED
), LZMA_OK
);
277 strm
.next_in
= v1_v1
;
278 strm
.avail_in
= file_size
;
279 strm
.next_out
= output_buffer
;
280 strm
.avail_out
= sizeof(output_buffer
);
282 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_STREAM_END
);
284 assert_uint_eq(strm
.total_in
, file_size
);
285 checksum
= lzma_crc32(output_buffer
, strm
.total_out
, 0);
286 assert_uint_eq(checksum
, hello_world_crc
);
295 // Test invalid checksum
296 lzma_stream strm
= LZMA_STREAM_INIT
;
298 uint8_t *data
= tuktest_file_from_srcdir("files/bad-1-v1-crc32.lz",
301 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
302 LZMA_CONCATENATED
), LZMA_OK
);
304 uint8_t output_buffer
[DECODE_CHUNK_SIZE
];
307 strm
.avail_in
= file_size
;
308 strm
.next_out
= output_buffer
;
309 strm
.avail_out
= sizeof(output_buffer
);
311 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_DATA_ERROR
);
313 // Test ignoring the checksum value - should decode successfully
314 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
315 LZMA_CONCATENATED
| LZMA_IGNORE_CHECK
), LZMA_OK
);
318 strm
.avail_in
= file_size
;
319 strm
.next_out
= output_buffer
;
320 strm
.avail_out
= sizeof(output_buffer
);
322 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_STREAM_END
);
323 assert_uint_eq(strm
.total_in
, file_size
);
326 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
,
327 LZMA_CONCATENATED
| LZMA_TELL_ANY_CHECK
), LZMA_OK
);
330 strm
.avail_in
= file_size
;
331 strm
.next_out
= output_buffer
;
332 strm
.avail_out
= sizeof(output_buffer
);
334 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_GET_CHECK
);
335 assert_uint_eq(lzma_get_check(&strm
), LZMA_CHECK_CRC32
);
336 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_DATA_ERROR
);
342 test_invalid_magic_bytes(void)
344 uint8_t lzip_id_string
[] = { 0x4C, 0x5A, 0x49, 0x50 };
345 lzma_stream strm
= LZMA_STREAM_INIT
;
347 for (uint32_t i
= 0; i
< ARRAY_SIZE(lzip_id_string
); i
++) {
348 // Corrupt magic bytes
349 lzip_id_string
[i
] ^= 1;
350 uint8_t output_buffer
[DECODE_CHUNK_SIZE
];
352 assert_lzma_ret(lzma_lzip_decoder(&strm
, MEMLIMIT
, 0),
355 strm
.next_in
= lzip_id_string
;
356 strm
.avail_in
= sizeof(lzip_id_string
);
357 strm
.next_out
= output_buffer
;
358 strm
.avail_out
= sizeof(output_buffer
);
360 assert_lzma_ret(lzma_code(&strm
, LZMA_RUN
),
364 lzip_id_string
[i
] ^= 1;
372 test_invalid_version(void)
374 // The file contains a version number that is not 0 or 1,
375 // so it should cause an error
376 decode_expect_error("files/unsupported-1-v234.lz",
382 test_invalid_dictionary_size(void)
384 // The first file has a too small dictionary size field.
385 decode_expect_error("files/bad-1-v1-dict-1.lz", LZMA_DATA_ERROR
);
387 // The second file has a too large dictionary size field.
388 decode_expect_error("files/bad-1-v1-dict-2.lz", LZMA_DATA_ERROR
);
393 test_invalid_uncomp_size(void)
395 // Test invalid v0 lzip file uncomp size
396 decode_expect_error("files/bad-1-v0-uncomp-size.lz",
399 // Test invalid v1 lzip file uncomp size
400 decode_expect_error("files/bad-1-v1-uncomp-size.lz",
406 test_invalid_member_size(void)
408 decode_expect_error("files/bad-1-v1-member-size.lz",
414 test_invalid_memlimit(void)
416 // A very low memlimit should prevent decoding.
417 // It should be possible to update the memlimit after the error.
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
= sizeof(output_buffer
);
433 assert_lzma_ret(lzma_code(&strm
, LZMA_FINISH
), LZMA_MEMLIMIT_ERROR
);
435 // Up the memlimit so that 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_concatenated
);
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();