| // SPDX-License-Identifier: 0BSD |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // |
| /// \file test_stream_flags.c |
| /// \brief Tests Stream Header and Stream Footer coders |
| // |
| // Authors: Jia Tan |
| // Lasse Collin |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include "tests.h" |
| |
| |
| // Size of the Stream Flags field |
| // (taken from src/liblzma/common/stream_flags_common.h) |
| #define XZ_STREAM_FLAGS_SIZE 2 |
| |
| #ifdef HAVE_ENCODERS |
| // Header and footer magic bytes for .xz file format |
| // (taken from src/liblzma/common/stream_flags_common.c) |
| static const uint8_t xz_header_magic[6] |
| = { 0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00 }; |
| static const uint8_t xz_footer_magic[2] = { 0x59, 0x5A }; |
| #endif |
| |
| |
| #ifdef HAVE_ENCODERS |
| static void |
| stream_header_encode_helper(lzma_check check) |
| { |
| lzma_stream_flags flags = { |
| .version = 0, |
| .check = check, |
| }; |
| |
| uint8_t header[LZMA_STREAM_HEADER_SIZE]; |
| |
| // Encode Stream Header |
| assert_lzma_ret(lzma_stream_header_encode(&flags, header), LZMA_OK); |
| |
| // Stream Header must start with Header Magic Bytes |
| const uint32_t magic_size = sizeof(xz_header_magic); |
| assert_array_eq(header, xz_header_magic, magic_size); |
| |
| // Next must come Stream Flags |
| const uint8_t *encoded_stream_flags = header + magic_size; |
| |
| // First byte is always null-byte. |
| // Second byte must have the Check ID in the lowest four bits |
| // and the highest four bits zero. |
| const uint8_t expected_stream_flags[] = { 0, check }; |
| assert_array_eq(encoded_stream_flags, expected_stream_flags, |
| XZ_STREAM_FLAGS_SIZE); |
| |
| // Last part is the CRC32 of the Stream Flags |
| const uint8_t *crc_ptr = encoded_stream_flags + XZ_STREAM_FLAGS_SIZE; |
| const uint32_t expected_crc = lzma_crc32(expected_stream_flags, |
| XZ_STREAM_FLAGS_SIZE, 0); |
| assert_uint_eq(read32le(crc_ptr), expected_crc); |
| } |
| #endif |
| |
| |
| static void |
| test_lzma_stream_header_encode(void) |
| { |
| #ifndef HAVE_ENCODERS |
| assert_skip("Encoder support disabled"); |
| #else |
| for (lzma_check i = 0; i < LZMA_CHECK_ID_MAX; i++) |
| stream_header_encode_helper(i); |
| |
| lzma_stream_flags flags = { |
| .version = 0, |
| .check = LZMA_CHECK_CRC32 |
| }; |
| |
| uint8_t header[LZMA_STREAM_HEADER_SIZE]; |
| |
| // Should fail if version > 0 |
| flags.version = 1; |
| assert_lzma_ret(lzma_stream_header_encode(&flags, header), |
| LZMA_OPTIONS_ERROR); |
| flags.version = 0; |
| |
| // Should fail if Check ID is invalid |
| flags.check = INVALID_LZMA_CHECK_ID; |
| assert_lzma_ret(lzma_stream_header_encode(&flags, header), |
| LZMA_PROG_ERROR); |
| flags.check = LZMA_CHECK_CRC32; |
| |
| // Should pass even if Backward Size is invalid |
| // because Stream Header doesn't have that field. |
| flags.backward_size = LZMA_VLI_MAX + 1; |
| assert_lzma_ret(lzma_stream_header_encode(&flags, header), LZMA_OK); |
| #endif |
| } |
| |
| |
| #if defined(HAVE_ENCODERS) |
| static void |
| stream_footer_encode_helper(lzma_check check) |
| { |
| lzma_stream_flags flags = { |
| .version = 0, |
| .check = check, |
| .backward_size = LZMA_BACKWARD_SIZE_MIN, |
| }; |
| |
| uint8_t footer[LZMA_STREAM_HEADER_SIZE]; |
| |
| // Encode Stream Footer |
| assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), LZMA_OK); |
| |
| // Stream Footer must start with CRC32 |
| const uint32_t crc = read32le(footer); |
| const uint32_t expected_crc = lzma_crc32(footer + sizeof(uint32_t), |
| LZMA_STREAM_HEADER_SIZE - (sizeof(uint32_t) + |
| sizeof(xz_footer_magic)), 0); |
| assert_uint_eq(crc, expected_crc); |
| |
| // Next the Backward Size |
| const uint32_t backwards_size = read32le(footer + sizeof(uint32_t)); |
| const uint32_t expected_backwards_size = flags.backward_size / 4 - 1; |
| assert_uint_eq(backwards_size, expected_backwards_size); |
| |
| // Next the Stream Flags |
| const uint8_t *stream_flags = footer + sizeof(uint32_t) * 2; |
| |
| // First byte must be null |
| assert_uint_eq(stream_flags[0], 0); |
| |
| // Second byte must have the Check ID in the lowest four bits |
| // and the highest four bits zero. |
| assert_uint_eq(stream_flags[1], check); |
| |
| // And ends with Footer Magic Bytes |
| const uint8_t *expected_footer_magic = stream_flags + |
| XZ_STREAM_FLAGS_SIZE; |
| assert_array_eq(expected_footer_magic, xz_footer_magic, |
| sizeof(xz_footer_magic)); |
| } |
| #endif |
| |
| |
| static void |
| test_lzma_stream_footer_encode(void) |
| { |
| #ifndef HAVE_ENCODERS |
| assert_skip("Encoder support disabled"); |
| #else |
| for (lzma_check i = 0; i < LZMA_CHECK_ID_MAX; i++) |
| stream_footer_encode_helper(i); |
| |
| lzma_stream_flags flags = { |
| .version = 0, |
| .backward_size = LZMA_BACKWARD_SIZE_MIN, |
| .check = LZMA_CHECK_CRC32 |
| }; |
| |
| uint8_t footer[LZMA_STREAM_HEADER_SIZE]; |
| |
| // Should fail if version > 0 |
| flags.version = 1; |
| assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), |
| LZMA_OPTIONS_ERROR); |
| flags.version = 0; |
| |
| // Should fail if Check ID is invalid |
| flags.check = INVALID_LZMA_CHECK_ID; |
| assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), |
| LZMA_PROG_ERROR); |
| |
| // Should fail if Backward Size is invalid |
| flags.backward_size -= 1; |
| assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), |
| LZMA_PROG_ERROR); |
| flags.backward_size += 2; |
| assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), |
| LZMA_PROG_ERROR); |
| flags.backward_size = LZMA_BACKWARD_SIZE_MAX + 4; |
| assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), |
| LZMA_PROG_ERROR); |
| #endif |
| } |
| |
| |
| #if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS) |
| static void |
| stream_header_decode_helper(lzma_check check) |
| { |
| lzma_stream_flags flags = { |
| .version = 0, |
| .check = check |
| }; |
| |
| uint8_t header[LZMA_STREAM_HEADER_SIZE]; |
| |
| assert_lzma_ret(lzma_stream_header_encode(&flags, header), LZMA_OK); |
| |
| lzma_stream_flags dest_flags; |
| assert_lzma_ret(lzma_stream_header_decode(&dest_flags, header), |
| LZMA_OK); |
| |
| // Version should be 0 |
| assert_uint_eq(dest_flags.version, 0); |
| |
| // Backward Size should be LZMA_VLI_UNKNOWN |
| assert_uint_eq(dest_flags.backward_size, LZMA_VLI_UNKNOWN); |
| |
| // Check ID must equal the argument given to this function. |
| assert_uint_eq(dest_flags.check, check); |
| } |
| #endif |
| |
| |
| static void |
| test_lzma_stream_header_decode(void) |
| { |
| #if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) |
| assert_skip("Encoder or decoder support disabled"); |
| #else |
| for (lzma_check i = 0; i < LZMA_CHECK_ID_MAX; i++) |
| stream_header_decode_helper(i); |
| |
| lzma_stream_flags flags = { |
| .version = 0, |
| .check = LZMA_CHECK_CRC32 |
| }; |
| |
| uint8_t header[LZMA_STREAM_HEADER_SIZE]; |
| lzma_stream_flags dest; |
| |
| // First encode known flags to header buffer |
| assert_lzma_ret(lzma_stream_header_encode(&flags, header), LZMA_OK); |
| |
| // Should fail if magic bytes do not match |
| header[0] ^= 1; |
| assert_lzma_ret(lzma_stream_header_decode(&dest, header), |
| LZMA_FORMAT_ERROR); |
| header[0] ^= 1; |
| |
| // Should fail if a reserved bit is set |
| uint8_t *stream_flags = header + sizeof(xz_header_magic); |
| stream_flags[0] = 1; |
| |
| // Need to adjust CRC32 after making a change since the CRC32 |
| // is verified before decoding the Stream Flags field. |
| uint8_t *crc32_ptr = header + sizeof(xz_header_magic) |
| + XZ_STREAM_FLAGS_SIZE; |
| const uint32_t crc_orig = read32le(crc32_ptr); |
| uint32_t new_crc32 = lzma_crc32( |
| stream_flags, XZ_STREAM_FLAGS_SIZE, 0); |
| write32le(crc32_ptr, new_crc32); |
| assert_lzma_ret(lzma_stream_header_decode(&dest, header), |
| LZMA_OPTIONS_ERROR); |
| stream_flags[0] = 0; |
| write32le(crc32_ptr, crc_orig); |
| |
| // Should fail if upper bits of check ID are set |
| stream_flags[1] |= 0xF0; |
| new_crc32 = lzma_crc32(stream_flags, XZ_STREAM_FLAGS_SIZE, 0); |
| write32le(crc32_ptr, new_crc32); |
| assert_lzma_ret(lzma_stream_header_decode(&dest, header), |
| LZMA_OPTIONS_ERROR); |
| stream_flags[1] = flags.check; |
| write32le(crc32_ptr, crc_orig); |
| |
| // Should fail if CRC32 does not match. |
| // First, alter a byte in the Stream Flags. |
| stream_flags[0] = 1; |
| assert_lzma_ret(lzma_stream_header_decode(&dest, header), |
| LZMA_DATA_ERROR); |
| stream_flags[0] = 0; |
| |
| // Next, change the CRC32. |
| *crc32_ptr ^= 1; |
| assert_lzma_ret(lzma_stream_header_decode(&dest, header), |
| LZMA_DATA_ERROR); |
| #endif |
| } |
| |
| |
| #if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS) |
| static void |
| stream_footer_decode_helper(lzma_check check) |
| { |
| lzma_stream_flags flags = { |
| .version = 0, |
| .backward_size = LZMA_BACKWARD_SIZE_MIN, |
| .check = check, |
| }; |
| |
| uint8_t footer[LZMA_STREAM_HEADER_SIZE]; |
| assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), LZMA_OK); |
| |
| lzma_stream_flags dest_flags; |
| assert_lzma_ret(lzma_stream_footer_decode(&dest_flags, footer), |
| LZMA_OK); |
| |
| // Version should be 0. |
| assert_uint_eq(dest_flags.version, 0); |
| |
| // Backward Size should equal the value from the flags. |
| assert_uint_eq(dest_flags.backward_size, flags.backward_size); |
| |
| // Check ID must equal argument given to this function. |
| assert_uint_eq(dest_flags.check, check); |
| } |
| #endif |
| |
| |
| static void |
| test_lzma_stream_footer_decode(void) |
| { |
| #if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) |
| assert_skip("Encoder or decoder support disabled"); |
| #else |
| for (lzma_check i = 0; i < LZMA_CHECK_ID_MAX; i++) |
| stream_footer_decode_helper(i); |
| |
| lzma_stream_flags flags = { |
| .version = 0, |
| .check = LZMA_CHECK_CRC32, |
| .backward_size = LZMA_BACKWARD_SIZE_MIN |
| }; |
| |
| uint8_t footer[LZMA_STREAM_HEADER_SIZE]; |
| lzma_stream_flags dest; |
| |
| // First encode known flags to the footer buffer |
| assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), LZMA_OK); |
| |
| // Should fail if magic bytes do not match |
| footer[LZMA_STREAM_HEADER_SIZE - 1] ^= 1; |
| assert_lzma_ret(lzma_stream_footer_decode(&dest, footer), |
| LZMA_FORMAT_ERROR); |
| footer[LZMA_STREAM_HEADER_SIZE - 1] ^= 1; |
| |
| // Should fail if a reserved bit is set. |
| // In the Stream Footer, the Stream Flags follow the CRC32 (4 bytes) |
| // and the Backward Size (4 bytes) |
| uint8_t *stream_flags = footer + sizeof(uint32_t) * 2; |
| stream_flags[0] = 1; |
| |
| // Need to adjust the CRC32 so it will not fail that check instead |
| uint8_t *crc32_ptr = footer; |
| const uint32_t crc_orig = read32le(crc32_ptr); |
| uint8_t *backward_size = footer + sizeof(uint32_t); |
| uint32_t new_crc32 = lzma_crc32(backward_size, sizeof(uint32_t) + |
| XZ_STREAM_FLAGS_SIZE, 0); |
| write32le(crc32_ptr, new_crc32); |
| assert_lzma_ret(lzma_stream_footer_decode(&dest, footer), |
| LZMA_OPTIONS_ERROR); |
| stream_flags[0] = 0; |
| write32le(crc32_ptr, crc_orig); |
| |
| // Should fail if upper bits of check ID are set |
| stream_flags[1] |= 0xF0; |
| new_crc32 = lzma_crc32(backward_size, sizeof(uint32_t) + |
| XZ_STREAM_FLAGS_SIZE, 0); |
| write32le(crc32_ptr, new_crc32); |
| assert_lzma_ret(lzma_stream_footer_decode(&dest, footer), |
| LZMA_OPTIONS_ERROR); |
| stream_flags[1] = flags.check; |
| write32le(crc32_ptr, crc_orig); |
| |
| // Should fail if CRC32 does not match. |
| // First, alter a byte in the Stream Flags. |
| stream_flags[0] = 1; |
| assert_lzma_ret(lzma_stream_footer_decode(&dest, footer), |
| LZMA_DATA_ERROR); |
| stream_flags[0] = 0; |
| |
| // Next, change the CRC32 |
| *crc32_ptr ^= 1; |
| assert_lzma_ret(lzma_stream_footer_decode(&dest, footer), |
| LZMA_DATA_ERROR); |
| #endif |
| } |
| |
| |
| static void |
| test_lzma_stream_flags_compare(void) |
| { |
| lzma_stream_flags first = { |
| .version = 0, |
| .backward_size = LZMA_BACKWARD_SIZE_MIN, |
| .check = LZMA_CHECK_CRC32, |
| }; |
| |
| lzma_stream_flags second = first; |
| |
| // First test should pass |
| assert_lzma_ret(lzma_stream_flags_compare(&first, &second), LZMA_OK); |
| |
| // Altering either version should cause an error |
| first.version = 1; |
| assert_lzma_ret(lzma_stream_flags_compare(&first, &second), |
| LZMA_OPTIONS_ERROR); |
| second.version = 1; |
| assert_lzma_ret(lzma_stream_flags_compare(&first, &second), |
| LZMA_OPTIONS_ERROR); |
| first.version = 0; |
| assert_lzma_ret(lzma_stream_flags_compare(&first, &second), |
| LZMA_OPTIONS_ERROR); |
| second.version = 0; |
| |
| // Check types must be under the maximum |
| first.check = INVALID_LZMA_CHECK_ID; |
| assert_lzma_ret(lzma_stream_flags_compare(&first, &second), |
| LZMA_PROG_ERROR); |
| second.check = INVALID_LZMA_CHECK_ID; |
| assert_lzma_ret(lzma_stream_flags_compare(&first, &second), |
| LZMA_PROG_ERROR); |
| first.check = LZMA_CHECK_CRC32; |
| assert_lzma_ret(lzma_stream_flags_compare(&first, &second), |
| LZMA_PROG_ERROR); |
| second.check = LZMA_CHECK_CRC32; |
| |
| // Check types must be equal |
| for (lzma_check i = 0; i < LZMA_CHECK_ID_MAX; i++) { |
| first.check = i; |
| if (i == second.check) |
| assert_lzma_ret(lzma_stream_flags_compare(&first, |
| &second), LZMA_OK); |
| else |
| assert_lzma_ret(lzma_stream_flags_compare(&first, |
| &second), LZMA_DATA_ERROR); |
| } |
| first.check = LZMA_CHECK_CRC32; |
| |
| // Backward Size comparison is skipped if either are LZMA_VLI_UNKNOWN |
| first.backward_size = LZMA_VLI_UNKNOWN; |
| assert_lzma_ret(lzma_stream_flags_compare(&first, &second), LZMA_OK); |
| second.backward_size = LZMA_VLI_MAX + 1; |
| assert_lzma_ret(lzma_stream_flags_compare(&first, &second), LZMA_OK); |
| second.backward_size = LZMA_BACKWARD_SIZE_MIN; |
| |
| // Backward Sizes need to be valid |
| first.backward_size = LZMA_VLI_MAX + 4; |
| assert_lzma_ret(lzma_stream_flags_compare(&first, &second), |
| LZMA_PROG_ERROR); |
| second.backward_size = LZMA_VLI_MAX + 4; |
| assert_lzma_ret(lzma_stream_flags_compare(&first, &second), |
| LZMA_PROG_ERROR); |
| first.backward_size = LZMA_BACKWARD_SIZE_MIN; |
| assert_lzma_ret(lzma_stream_flags_compare(&first, &second), |
| LZMA_PROG_ERROR); |
| second.backward_size = LZMA_BACKWARD_SIZE_MIN; |
| |
| // Backward Sizes must be equal |
| second.backward_size = first.backward_size + 4; |
| assert_lzma_ret(lzma_stream_flags_compare(&first, &second), |
| LZMA_DATA_ERROR); |
| |
| // Should fail if Backward Sizes are > LZMA_BACKWARD_SIZE_MAX |
| // even though they are equal |
| first.backward_size = LZMA_BACKWARD_SIZE_MAX + 1; |
| second.backward_size = LZMA_BACKWARD_SIZE_MAX + 1; |
| assert_lzma_ret(lzma_stream_flags_compare(&first, &second), |
| LZMA_PROG_ERROR); |
| } |
| |
| |
| extern int |
| main(int argc, char **argv) |
| { |
| tuktest_start(argc, argv); |
| tuktest_run(test_lzma_stream_header_encode); |
| tuktest_run(test_lzma_stream_footer_encode); |
| tuktest_run(test_lzma_stream_header_decode); |
| tuktest_run(test_lzma_stream_footer_decode); |
| tuktest_run(test_lzma_stream_flags_compare); |
| return tuktest_end(); |
| } |