lzmainfo: Avoid integer overflow
[xz/debian.git] / tests / test_lzip_decoder.c
blobcee97b86d292022a1f06462ae61c5a2df152c004
1 // SPDX-License-Identifier: 0BSD
3 ///////////////////////////////////////////////////////////////////////////////
4 //
5 /// \file test_lzip_decoder.c
6 /// \brief Tests decoding lzip data
7 //
8 // Author: Jia Tan
9 //
10 ///////////////////////////////////////////////////////////////////////////////
12 #include "tests.h"
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
35 static void
36 basic_lzip_decode(const char *src, const uint32_t expected_crc)
38 size_t file_size;
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];
47 strm.next_in = data;
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) {
55 strm.avail_in = 1;
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),
60 checksum);
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),
71 checksum);
72 assert_uint_eq(checksum, expected_crc);
74 lzma_end(&strm);
78 static void
79 test_options(void)
81 // Test NULL stream
82 assert_lzma_ret(lzma_lzip_decoder(NULL, MEMLIMIT, 0),
83 LZMA_PROG_ERROR);
85 // Test invalid flags
86 lzma_stream strm = LZMA_STREAM_INIT;
87 assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, UINT32_MAX),
88 LZMA_OPTIONS_ERROR);
89 // Memlimit tests are done elsewhere
93 static void
94 test_v0_decode(void)
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);
103 static void
104 test_v1_decode(void)
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
112 // the lzip stream
113 static void
114 trailing_helper(const char *src, const uint32_t expected_data_checksum,
115 const uint32_t expected_trailing_checksum)
117 size_t file_size;
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];
126 strm.next_in = data;
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),
137 checksum);
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),
148 checksum);
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);
156 lzma_end(&strm);
160 // Helper function to decode a bad file and compare to returned error to
161 // what the caller expects
162 static void
163 decode_expect_error(const char *src, lzma_ret expected_error)
165 lzma_stream strm = LZMA_STREAM_INIT;
166 size_t file_size;
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;
175 strm.next_in = data;
177 lzma_ret ret = LZMA_OK;
179 do {
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);
185 else
186 ret = lzma_code(&strm, LZMA_RUN);
187 } while (ret == LZMA_OK);
189 assert_lzma_ret(ret, expected_error);
190 lzma_end(&strm);
194 static void
195 test_v0_trailing(void)
197 trailing_helper("files/good-1-v0-trailing-1.lz", hello_world_crc,
198 trailing_garbage_crc);
202 static void
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",
220 LZMA_BUF_ERROR);
224 static void
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;
231 size_t file_size;
232 uint8_t *v0_v1 = tuktest_file_from_srcdir("files/good-2-v0-v1.lz",
233 &file_size);
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",
254 &file_size);
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",
272 &file_size);
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);
288 lzma_end(&strm);
292 static void
293 test_crc(void)
295 // Test invalid checksum
296 lzma_stream strm = LZMA_STREAM_INIT;
297 size_t file_size;
298 uint8_t *data = tuktest_file_from_srcdir("files/bad-1-v1-crc32.lz",
299 &file_size);
301 assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
302 LZMA_CONCATENATED), LZMA_OK);
304 uint8_t output_buffer[DECODE_CHUNK_SIZE];
306 strm.next_in = data;
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);
317 strm.next_in = data;
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);
325 // Test tell check
326 assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
327 LZMA_CONCATENATED | LZMA_TELL_ANY_CHECK), LZMA_OK);
329 strm.next_in = data;
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);
337 lzma_end(&strm);
341 static void
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),
353 LZMA_OK);
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),
361 LZMA_FORMAT_ERROR);
363 // Reset magic bytes
364 lzip_id_string[i] ^= 1;
367 lzma_end(&strm);
371 static void
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",
377 LZMA_OPTIONS_ERROR);
381 static void
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);
392 static void
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",
397 LZMA_DATA_ERROR);
399 // Test invalid v1 lzip file uncomp size
400 decode_expect_error("files/bad-1-v1-uncomp-size.lz",
401 LZMA_DATA_ERROR);
405 static void
406 test_invalid_member_size(void)
408 decode_expect_error("files/bad-1-v1-member-size.lz",
409 LZMA_DATA_ERROR);
413 static void
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.
418 size_t file_size;
419 uint8_t *data = tuktest_file_from_srcdir("files/good-1-v1.lz",
420 &file_size);
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);
428 strm.next_in = data;
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);
440 // Finish decoding
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);
447 lzma_end(&strm);
449 #endif
452 extern int
453 main(int argc, char **argv)
455 tuktest_start(argc, argv);
457 #ifndef HAVE_LZIP_DECODER
458 tuktest_early_skip("lzip decoder disabled");
459 #else
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();
474 #endif