(root)/
xz-5.4.5/
tests/
test_lzip_decoder.c
       1  ///////////////////////////////////////////////////////////////////////////////
       2  //
       3  /// \file       test_lzip_decoder.c
       4  /// \brief      Tests decoding lzip data
       5  //
       6  //  Author:     Jia Tan
       7  //
       8  //  This file has been put into the public domain.
       9  //  You can do whatever you want with this file.
      10  //
      11  ///////////////////////////////////////////////////////////////////////////////
      12  
      13  #include "tests.h"
      14  
      15  #ifdef HAVE_LZIP_DECODER
      16  
      17  // Memlimit large enough to pass all of the test files
      18  #define MEMLIMIT (1U << 20)
      19  #define DECODE_CHUNK_SIZE 1024
      20  
      21  
      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.
      27  //
      28  // CRC32 value of "Hello\nWorld\n"
      29  static const uint32_t hello_world_crc = 0x15A2A343;
      30  
      31  // CRC32 value of "Trailing garbage\n"
      32  static const uint32_t trailing_garbage_crc = 0x87081A60;
      33  
      34  
      35  // Helper function to decode a good file with no flags and plenty high memlimit
      36  static void
      37  basic_lzip_decode(const char *src, const uint32_t expected_crc)
      38  {
      39  	size_t file_size;
      40  	uint8_t *data = tuktest_file_from_srcdir(src, &file_size);
      41  	uint32_t checksum = 0;
      42  
      43  	lzma_stream strm = LZMA_STREAM_INIT;
      44  	assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, 0), LZMA_OK);
      45  
      46  	uint8_t *output_buffer = tuktest_malloc(DECODE_CHUNK_SIZE);
      47  
      48  	strm.next_in = data;
      49  	strm.next_out = output_buffer;
      50  	strm.avail_out = DECODE_CHUNK_SIZE;
      51  
      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) {
      56  		strm.avail_in = 1;
      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),
      61  				checksum);
      62  			// No need to free output_buffer because it will
      63  			// automatically be freed at the end of the test by
      64  			// tuktest.
      65  			output_buffer = tuktest_malloc(DECODE_CHUNK_SIZE);
      66  			strm.next_out = output_buffer;
      67  			strm.avail_out = DECODE_CHUNK_SIZE;
      68  		}
      69  	}
      70  
      71  	assert_lzma_ret(ret, LZMA_STREAM_END);
      72  	assert_uint_eq(strm.total_in, file_size);
      73  
      74  	checksum = lzma_crc32(output_buffer,
      75  			(size_t)(strm.next_out - output_buffer),
      76  			checksum);
      77  	assert_uint_eq(checksum, expected_crc);
      78  
      79  	lzma_end(&strm);
      80  }
      81  
      82  
      83  static void
      84  test_options(void)
      85  {
      86  	// Test NULL stream
      87  	assert_lzma_ret(lzma_lzip_decoder(NULL, MEMLIMIT, 0),
      88  			LZMA_PROG_ERROR);
      89  
      90  	// Test invalid flags
      91  	lzma_stream strm = LZMA_STREAM_INIT;
      92  	assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, UINT32_MAX),
      93  			LZMA_OPTIONS_ERROR);
      94  	// Memlimit tests are done elsewhere
      95  }
      96  
      97  
      98  static void
      99  test_v0_decode(void)
     100  {
     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);
     105  }
     106  
     107  
     108  static void
     109  test_v1_decode(void)
     110  {
     111  	// This tests decoding a basic lzip v1 file
     112  	basic_lzip_decode("files/good-1-v1.lz", hello_world_crc);
     113  }
     114  
     115  
     116  // Helper function to decode a good file with trailing bytes after
     117  // the lzip stream
     118  static void
     119  trailing_helper(const char *src, const uint32_t expected_data_checksum,
     120  		const uint32_t expected_trailing_checksum)
     121  {
     122  	size_t file_size;
     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);
     128  
     129  	uint8_t *output_buffer = tuktest_malloc(DECODE_CHUNK_SIZE);
     130  
     131  	strm.next_in = data;
     132  	strm.next_out = output_buffer;
     133  	strm.avail_in = file_size;
     134  	strm.avail_out = DECODE_CHUNK_SIZE;
     135  
     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),
     142  				checksum);
     143  			// No need to free output_buffer because it will
     144  			// automatically be freed at the end of the test by
     145  			// tuktest.
     146  			output_buffer = tuktest_malloc(DECODE_CHUNK_SIZE);
     147  			strm.next_out = output_buffer;
     148  			strm.avail_out = DECODE_CHUNK_SIZE;
     149  		}
     150  	}
     151  
     152  	assert_lzma_ret(ret, LZMA_STREAM_END);
     153  	assert_uint(strm.total_in, <, file_size);
     154  
     155  	checksum = lzma_crc32(output_buffer,
     156  			(size_t)(strm.next_out - output_buffer),
     157  			checksum);
     158  
     159  	assert_uint_eq(checksum, expected_data_checksum);
     160  
     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);
     164  
     165  	lzma_end(&strm);
     166  }
     167  
     168  
     169  // Helper function to decode a bad file and compare to returned error to
     170  // what the caller expects
     171  static void
     172  decode_expect_error(const char *src, lzma_ret expected_error)
     173  {
     174  	lzma_stream strm = LZMA_STREAM_INIT;
     175  	size_t file_size;
     176  	uint8_t *data = tuktest_file_from_srcdir(src, &file_size);
     177  
     178  	assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
     179  			LZMA_CONCATENATED), LZMA_OK);
     180  
     181  	uint8_t output_buffer[DECODE_CHUNK_SIZE];
     182  
     183  	strm.avail_in = file_size;
     184  	strm.next_in = data;
     185  	strm.avail_out = DECODE_CHUNK_SIZE;
     186  	strm.next_out = output_buffer;
     187  
     188  	lzma_ret ret = LZMA_OK;
     189  
     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);
     196  		else
     197  			ret = lzma_code(&strm, LZMA_RUN);
     198  	}
     199  
     200  	assert_lzma_ret(ret, expected_error);
     201  	lzma_end(&strm);
     202  }
     203  
     204  
     205  static void
     206  test_v0_trailing(void)
     207  {
     208  	trailing_helper("files/good-1-v0-trailing-1.lz", hello_world_crc,
     209  			trailing_garbage_crc);
     210  }
     211  
     212  
     213  static void
     214  test_v1_trailing(void)
     215  {
     216  	trailing_helper("files/good-1-v1-trailing-1.lz", hello_world_crc,
     217  			trailing_garbage_crc);
     218  
     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);
     227  
     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",
     231  			LZMA_BUF_ERROR);
     232  }
     233  
     234  
     235  static void
     236  test_concatentated(void)
     237  {
     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"
     241  
     242  	lzma_stream strm = LZMA_STREAM_INIT;
     243  	size_t file_size;
     244  	uint8_t *v0_v1 = tuktest_file_from_srcdir("files/good-2-v0-v1.lz",
     245  		&file_size);
     246  
     247  	assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
     248  			LZMA_CONCATENATED), LZMA_OK);
     249  
     250  	uint8_t output_buffer[DECODE_CHUNK_SIZE];
     251  
     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;
     256  
     257  	assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END);
     258  
     259  	assert_uint_eq(strm.total_in, file_size);
     260  
     261  	uint32_t checksum = lzma_crc32(output_buffer, strm.total_out, 0);
     262  	assert_uint_eq(checksum, hello_world_crc);
     263  
     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",
     266  		&file_size);
     267  
     268  	assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
     269  			LZMA_CONCATENATED), LZMA_OK);
     270  
     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;
     275  
     276  	assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END);
     277  
     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);
     281  
     282  	// The third file contains 2 v1 members
     283  	uint8_t *v1_v1 = tuktest_file_from_srcdir("files/good-2-v1-v1.lz",
     284  		&file_size);
     285  
     286  	assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
     287  			LZMA_CONCATENATED), LZMA_OK);
     288  
     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;
     293  
     294  	assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END);
     295  
     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);
     299  
     300  	lzma_end(&strm);
     301  }
     302  
     303  
     304  static void
     305  test_crc(void)
     306  {
     307  	// Test invalid checksum
     308  	lzma_stream strm = LZMA_STREAM_INIT;
     309  	size_t file_size;
     310  	uint8_t *data = tuktest_file_from_srcdir("files/bad-1-v1-crc32.lz",
     311  			&file_size);
     312  
     313  	assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
     314  			LZMA_CONCATENATED), LZMA_OK);
     315  
     316  	uint8_t output_buffer[DECODE_CHUNK_SIZE];
     317  
     318  	strm.avail_in = file_size;
     319  	strm.next_in = data;
     320  	strm.avail_out = DECODE_CHUNK_SIZE;
     321  	strm.next_out = output_buffer;
     322  
     323  	assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_DATA_ERROR);
     324  
     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);
     328  
     329  	strm.avail_in = file_size;
     330  	strm.next_in = data;
     331  	strm.avail_out = DECODE_CHUNK_SIZE;
     332  	strm.next_out = output_buffer;
     333  
     334  	assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END);
     335  	assert_uint_eq(strm.total_in, file_size);
     336  
     337  	// Test tell check
     338  	assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT,
     339  			LZMA_CONCATENATED | LZMA_TELL_ANY_CHECK), LZMA_OK);
     340  
     341  	strm.avail_in = file_size;
     342  	strm.next_in = data;
     343  	strm.avail_out = DECODE_CHUNK_SIZE;
     344  	strm.next_out = output_buffer;
     345  
     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);
     349  	lzma_end(&strm);
     350  }
     351  
     352  
     353  static void
     354  test_invalid_magic_bytes(void)
     355  {
     356  	uint8_t lzip_id_string[] = { 0x4C, 0x5A, 0x49, 0x50 };
     357  	lzma_stream strm = LZMA_STREAM_INIT;
     358  
     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];
     363  
     364  		assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, 0),
     365  				LZMA_OK);
     366  
     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;
     371  
     372  		assert_lzma_ret(lzma_code(&strm, LZMA_RUN),
     373  				LZMA_FORMAT_ERROR);
     374  
     375  		// Reset magic bytes
     376  		lzip_id_string[i] ^= 1;
     377  	}
     378  
     379  	lzma_end(&strm);
     380  }
     381  
     382  
     383  static void
     384  test_invalid_version(void)
     385  {
     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",
     389  			LZMA_OPTIONS_ERROR);
     390  }
     391  
     392  
     393  static void
     394  test_invalid_dictionary_size(void)
     395  {
     396  	// First file has too small dictionary size field
     397  	decode_expect_error("files/bad-1-v1-dict-1.lz", LZMA_DATA_ERROR);
     398  
     399  	// Second file has too large dictionary size field
     400  	decode_expect_error("files/bad-1-v1-dict-2.lz", LZMA_DATA_ERROR);
     401  }
     402  
     403  
     404  static void
     405  test_invalid_uncomp_size(void)
     406  {
     407  	// Test invalid v0 lzip file uncomp size
     408  	decode_expect_error("files/bad-1-v0-uncomp-size.lz",
     409  			LZMA_DATA_ERROR);
     410  
     411  	// Test invalid v1 lzip file uncomp size
     412  	decode_expect_error("files/bad-1-v1-uncomp-size.lz",
     413  			LZMA_DATA_ERROR);
     414  }
     415  
     416  
     417  static void
     418  test_invalid_member_size(void)
     419  {
     420  	decode_expect_error("files/bad-1-v1-member-size.lz",
     421  			LZMA_DATA_ERROR);
     422  }
     423  
     424  
     425  static void
     426  test_invalid_memlimit(void)
     427  {
     428  	// A very low memlimit should prevent decoding.
     429  	// Should be able to update the memlimit after failing
     430  	size_t file_size;
     431  	uint8_t *data = tuktest_file_from_srcdir("files/good-1-v1.lz",
     432  			&file_size);
     433  
     434  	uint8_t output_buffer[DECODE_CHUNK_SIZE];
     435  
     436  	lzma_stream strm = LZMA_STREAM_INIT;
     437  
     438  	assert_lzma_ret(lzma_lzip_decoder(&strm, 1, 0), LZMA_OK);
     439  
     440  	strm.next_in = data;
     441  	strm.avail_in = file_size;
     442  	strm.next_out = output_buffer;
     443  	strm.avail_out = DECODE_CHUNK_SIZE;
     444  
     445  	assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_MEMLIMIT_ERROR);
     446  
     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);
     451  
     452  	// Finish decoding
     453  	assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END);
     454  
     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);
     458  
     459  	lzma_end(&strm);
     460  }
     461  #endif
     462  
     463  
     464  extern int
     465  main(int argc, char **argv)
     466  {
     467  	tuktest_start(argc, argv);
     468  
     469  #ifndef HAVE_LZIP_DECODER
     470  	tuktest_early_skip("lzip decoder disabled");
     471  #else
     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();
     486  #endif
     487  
     488  }