Home | History | Annotate | Line # | Download | only in tests
      1 // SPDX-License-Identifier: 0BSD
      2 
      3 ///////////////////////////////////////////////////////////////////////////////
      4 //
      5 /// \file       test_lzip_decoder.c
      6 /// \brief      Tests decoding lzip data
      7 //
      8 //  Author:     Jia Tan
      9 //
     10 ///////////////////////////////////////////////////////////////////////////////
     11 
     12 #include "tests.h"
     13 
     14 #ifdef HAVE_LZIP_DECODER
     15 
     16 // Memlimit large enough to pass all of the test files
     17 #define MEMLIMIT (1U << 20)
     18 #define DECODE_CHUNK_SIZE 1024
     19 
     20 
     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.
     26 //
     27 // CRC32 value of "Hello\nWorld\n"
     28 static const uint32_t hello_world_crc = 0x15A2A343;
     29 
     30 // CRC32 value of "Trailing garbage\n"
     31 static const uint32_t trailing_garbage_crc = 0x87081A60;
     32 
     33 
     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)
     37 {
     38 	size_t file_size;
     39 	uint8_t *data = tuktest_file_from_srcdir(src, &file_size);
     40 	uint32_t checksum = 0;
     41 
     42 	lzma_stream strm = LZMA_STREAM_INIT;
     43 	assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, 0), LZMA_OK);
     44 
     45 	uint8_t output_buffer[DECODE_CHUNK_SIZE];
     46 
     47 	strm.next_in = data;
     48 	strm.next_out = output_buffer;
     49 	strm.avail_out = sizeof(output_buffer);
     50 
     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);
     63 		}
     64 	}
     65 
     66 	assert_lzma_ret(ret, LZMA_STREAM_END);
     67 	assert_uint_eq(strm.total_in, file_size);
     68 
     69 	checksum = lzma_crc32(output_buffer,
     70 			(size_t)(strm.next_out - output_buffer),
     71 			checksum);
     72 	assert_uint_eq(checksum, expected_crc);
     73 
     74 	lzma_end(&strm);
     75 }
     76 
     77 
     78 static void
     79 test_options(void)
     80 {
     81 	// Test NULL stream
     82 	assert_lzma_ret(lzma_lzip_decoder(NULL, MEMLIMIT, 0),
     83 			LZMA_PROG_ERROR);
     84 
     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
     90 }
     91 
     92 
     93 static void
     94 test_v0_decode(void)
     95 {
     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);
    100 }
    101 
    102 
    103 static void
    104 test_v1_decode(void)
    105 {
    106 	// This tests decoding a basic lzip v1 file
    107 	basic_lzip_decode("files/good-1-v1.lz", hello_world_crc);
    108 }
    109 
    110 
    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)
    116 {
    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);
    123 
    124 	uint8_t output_buffer[DECODE_CHUNK_SIZE];
    125 
    126 	strm.next_in = data;
    127 	strm.avail_in = file_size;
    128 	strm.next_out = output_buffer;
    129 	strm.avail_out = sizeof(output_buffer);
    130 
    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);
    140 		}
    141 	}
    142 
    143 	assert_lzma_ret(ret, LZMA_STREAM_END);
    144 	assert_uint(strm.total_in, <, file_size);
    145 
    146 	checksum = lzma_crc32(output_buffer,
    147 			(size_t)(strm.next_out - output_buffer),
    148 			checksum);
    149 
    150 	assert_uint_eq(checksum, expected_data_checksum);
    151 
    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);
    155 
    156 	lzma_end(&strm);
    157 }
    158 
    159 
    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)
    164 {
    165 	lzma_stream strm = LZMA_STREAM_INIT;
    166 	size_t file_size;
    167 	uint8_t *data = tuktest_file_from_srcdir(src, &file_size);
    168 
    169 	assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
    170 			LZMA_CONCATENATED), LZMA_OK);
    171 
    172 	uint8_t output_buffer[DECODE_CHUNK_SIZE];
    173 
    174 	strm.avail_in = file_size;
    175 	strm.next_in = data;
    176 
    177 	lzma_ret ret = LZMA_OK;
    178 
    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);
    188 
    189 	assert_lzma_ret(ret, expected_error);
    190 	lzma_end(&strm);
    191 }
    192 
    193 
    194 static void
    195 test_v0_trailing(void)
    196 {
    197 	trailing_helper("files/good-1-v0-trailing-1.lz", hello_world_crc,
    198 			trailing_garbage_crc);
    199 }
    200 
    201 
    202 static void
    203 test_v1_trailing(void)
    204 {
    205 	trailing_helper("files/good-1-v1-trailing-1.lz", hello_world_crc,
    206 			trailing_garbage_crc);
    207 
    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);
    216 
    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);
    221 }
    222 
    223 
    224 static void
    225 test_concatenated(void)
    226 {
    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);
    234 
    235 	assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
    236 			LZMA_CONCATENATED), LZMA_OK);
    237 
    238 	uint8_t output_buffer[DECODE_CHUNK_SIZE];
    239 
    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);
    244 
    245 	assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END);
    246 
    247 	assert_uint_eq(strm.total_in, file_size);
    248 
    249 	uint32_t checksum = lzma_crc32(output_buffer, strm.total_out, 0);
    250 	assert_uint_eq(checksum, hello_world_crc);
    251 
    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);
    255 
    256 	assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
    257 			LZMA_CONCATENATED), LZMA_OK);
    258 
    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);
    263 
    264 	assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END);
    265 
    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);
    269 
    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);
    273 
    274 	assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
    275 			LZMA_CONCATENATED), LZMA_OK);
    276 
    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);
    281 
    282 	assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END);
    283 
    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);
    287 
    288 	lzma_end(&strm);
    289 }
    290 
    291 
    292 static void
    293 test_crc(void)
    294 {
    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);
    300 
    301 	assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
    302 			LZMA_CONCATENATED), LZMA_OK);
    303 
    304 	uint8_t output_buffer[DECODE_CHUNK_SIZE];
    305 
    306 	strm.next_in = data;
    307 	strm.avail_in = file_size;
    308 	strm.next_out = output_buffer;
    309 	strm.avail_out = sizeof(output_buffer);
    310 
    311 	assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_DATA_ERROR);
    312 
    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);
    316 
    317 	strm.next_in = data;
    318 	strm.avail_in = file_size;
    319 	strm.next_out = output_buffer;
    320 	strm.avail_out = sizeof(output_buffer);
    321 
    322 	assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END);
    323 	assert_uint_eq(strm.total_in, file_size);
    324 
    325 	// Test tell check
    326 	assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
    327 			LZMA_CONCATENATED | LZMA_TELL_ANY_CHECK), LZMA_OK);
    328 
    329 	strm.next_in = data;
    330 	strm.avail_in = file_size;
    331 	strm.next_out = output_buffer;
    332 	strm.avail_out = sizeof(output_buffer);
    333 
    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);
    338 }
    339 
    340 
    341 static void
    342 test_invalid_magic_bytes(void)
    343 {
    344 	uint8_t lzip_id_string[] = { 0x4C, 0x5A, 0x49, 0x50 };
    345 	lzma_stream strm = LZMA_STREAM_INIT;
    346 
    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];
    351 
    352 		assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, 0),
    353 				LZMA_OK);
    354 
    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);
    359 
    360 		assert_lzma_ret(lzma_code(&strm, LZMA_RUN),
    361 				LZMA_FORMAT_ERROR);
    362 
    363 		// Reset magic bytes
    364 		lzip_id_string[i] ^= 1;
    365 	}
    366 
    367 	lzma_end(&strm);
    368 }
    369 
    370 
    371 static void
    372 test_invalid_version(void)
    373 {
    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);
    378 }
    379 
    380 
    381 static void
    382 test_invalid_dictionary_size(void)
    383 {
    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);
    386 
    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);
    389 }
    390 
    391 
    392 static void
    393 test_invalid_uncomp_size(void)
    394 {
    395 	// Test invalid v0 lzip file uncomp size
    396 	decode_expect_error("files/bad-1-v0-uncomp-size.lz",
    397 			LZMA_DATA_ERROR);
    398 
    399 	// Test invalid v1 lzip file uncomp size
    400 	decode_expect_error("files/bad-1-v1-uncomp-size.lz",
    401 			LZMA_DATA_ERROR);
    402 }
    403 
    404 
    405 static void
    406 test_invalid_member_size(void)
    407 {
    408 	decode_expect_error("files/bad-1-v1-member-size.lz",
    409 			LZMA_DATA_ERROR);
    410 }
    411 
    412 
    413 static void
    414 test_invalid_memlimit(void)
    415 {
    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);
    421 
    422 	uint8_t output_buffer[DECODE_CHUNK_SIZE];
    423 
    424 	lzma_stream strm = LZMA_STREAM_INIT;
    425 
    426 	assert_lzma_ret(lzma_lzip_decoder(&strm, 1, 0), LZMA_OK);
    427 
    428 	strm.next_in = data;
    429 	strm.avail_in = file_size;
    430 	strm.next_out = output_buffer;
    431 	strm.avail_out = sizeof(output_buffer);
    432 
    433 	assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_MEMLIMIT_ERROR);
    434 
    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);
    439 
    440 	// Finish decoding
    441 	assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END);
    442 
    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);
    446 
    447 	lzma_end(&strm);
    448 }
    449 #endif
    450 
    451 
    452 extern int
    453 main(int argc, char **argv)
    454 {
    455 	tuktest_start(argc, argv);
    456 
    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
    475 }
    476