mirror of
https://git.tukaani.org/xz.git
synced 2024-04-04 12:36:23 +02:00
c47ecd6d39
Explicitly casting the integer to lzma_check silences the warning. Since such an invalid value is needed in multiple tests, a constant INVALID_LZMA_CHECK_ID was added to tests.h. The use of 0x1000 for lzma_block.check wasn't optimal as if the underlying type is a char then 0x1000 will be truncated to 0. However, in these test cases the value is ignored, thus even with such truncation the test would have passed.
514 lines
15 KiB
C
514 lines
15 KiB
C
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
/// \file test_block_header.c
|
|
/// \brief Tests Block Header coders
|
|
//
|
|
// Authors: Lasse Collin
|
|
// Jia Tan
|
|
//
|
|
// This file has been put into the public domain.
|
|
// You can do whatever you want with this file.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "tests.h"
|
|
|
|
|
|
static lzma_options_lzma opt_lzma;
|
|
|
|
|
|
#ifdef HAVE_ENCODERS
|
|
static lzma_filter filters_none[1] = {
|
|
{
|
|
.id = LZMA_VLI_UNKNOWN,
|
|
},
|
|
};
|
|
|
|
|
|
static lzma_filter filters_one[2] = {
|
|
{
|
|
.id = LZMA_FILTER_LZMA2,
|
|
.options = &opt_lzma,
|
|
}, {
|
|
.id = LZMA_VLI_UNKNOWN,
|
|
}
|
|
};
|
|
|
|
|
|
// These filters are only used in test_lzma_block_header_decode()
|
|
// which only runs if encoders and decoders are configured.
|
|
#ifdef HAVE_DECODERS
|
|
static lzma_filter filters_four[5] = {
|
|
{
|
|
.id = LZMA_FILTER_X86,
|
|
.options = NULL,
|
|
}, {
|
|
.id = LZMA_FILTER_X86,
|
|
.options = NULL,
|
|
}, {
|
|
.id = LZMA_FILTER_X86,
|
|
.options = NULL,
|
|
}, {
|
|
.id = LZMA_FILTER_LZMA2,
|
|
.options = &opt_lzma,
|
|
}, {
|
|
.id = LZMA_VLI_UNKNOWN,
|
|
}
|
|
};
|
|
#endif
|
|
|
|
|
|
static lzma_filter filters_five[6] = {
|
|
{
|
|
.id = LZMA_FILTER_X86,
|
|
.options = NULL,
|
|
}, {
|
|
.id = LZMA_FILTER_X86,
|
|
.options = NULL,
|
|
}, {
|
|
.id = LZMA_FILTER_X86,
|
|
.options = NULL,
|
|
}, {
|
|
.id = LZMA_FILTER_X86,
|
|
.options = NULL,
|
|
}, {
|
|
.id = LZMA_FILTER_LZMA2,
|
|
.options = &opt_lzma,
|
|
}, {
|
|
.id = LZMA_VLI_UNKNOWN,
|
|
}
|
|
};
|
|
#endif
|
|
|
|
|
|
static void
|
|
test_lzma_block_header_size(void)
|
|
{
|
|
#ifndef HAVE_ENCODERS
|
|
assert_skip("Encoder support disabled");
|
|
#else
|
|
if (!lzma_filter_encoder_is_supported(LZMA_FILTER_X86))
|
|
assert_skip("x86 BCJ encoder is disabled");
|
|
|
|
lzma_block block = {
|
|
.version = 0,
|
|
.filters = filters_one,
|
|
.compressed_size = LZMA_VLI_UNKNOWN,
|
|
.uncompressed_size = LZMA_VLI_UNKNOWN,
|
|
.check = LZMA_CHECK_CRC32
|
|
};
|
|
|
|
// Test that all initial options are valid
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK);
|
|
assert_uint(block.header_size, >=, LZMA_BLOCK_HEADER_SIZE_MIN);
|
|
assert_uint(block.header_size, <=, LZMA_BLOCK_HEADER_SIZE_MAX);
|
|
assert_uint_eq(block.header_size % 4, 0);
|
|
|
|
// Test invalid version number
|
|
for (uint32_t i = 2; i < 20; i++) {
|
|
block.version = i;
|
|
assert_lzma_ret(lzma_block_header_size(&block),
|
|
LZMA_OPTIONS_ERROR);
|
|
}
|
|
|
|
block.version = 1;
|
|
|
|
// Test invalid compressed size
|
|
block.compressed_size = 0;
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_PROG_ERROR);
|
|
|
|
block.compressed_size = LZMA_VLI_MAX + 1;
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_PROG_ERROR);
|
|
block.compressed_size = LZMA_VLI_UNKNOWN;
|
|
|
|
// Test invalid uncompressed size
|
|
block.uncompressed_size = LZMA_VLI_MAX + 1;
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_PROG_ERROR);
|
|
block.uncompressed_size = LZMA_VLI_MAX;
|
|
|
|
// Test invalid filters
|
|
block.filters = NULL;
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_PROG_ERROR);
|
|
|
|
block.filters = filters_none;
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_PROG_ERROR);
|
|
|
|
block.filters = filters_five;
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_PROG_ERROR);
|
|
|
|
block.filters = filters_one;
|
|
|
|
// Test setting compressed_size to something valid
|
|
block.compressed_size = 4096;
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK);
|
|
assert_uint(block.header_size, >=, LZMA_BLOCK_HEADER_SIZE_MIN);
|
|
assert_uint(block.header_size, <=, LZMA_BLOCK_HEADER_SIZE_MAX);
|
|
assert_uint_eq(block.header_size % 4, 0);
|
|
|
|
// Test setting uncompressed_size to something valid
|
|
block.uncompressed_size = 4096;
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK);
|
|
assert_uint(block.header_size, >=, LZMA_BLOCK_HEADER_SIZE_MIN);
|
|
assert_uint(block.header_size, <=, LZMA_BLOCK_HEADER_SIZE_MAX);
|
|
assert_uint_eq(block.header_size % 4, 0);
|
|
|
|
// This should pass, but header_size will be an invalid value
|
|
// because the total block size will not be able to fit in a valid
|
|
// lzma_vli. This way a temporary value can be used to reserve
|
|
// space for the header and later the actual value can be set.
|
|
block.compressed_size = LZMA_VLI_MAX;
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK);
|
|
assert_uint(block.header_size, >=, LZMA_BLOCK_HEADER_SIZE_MIN);
|
|
assert_uint(block.header_size, <=, LZMA_BLOCK_HEADER_SIZE_MAX);
|
|
assert_uint_eq(block.header_size % 4, 0);
|
|
|
|
// Use an invalid value for a filter option. This should still pass
|
|
// because the size of the LZMA2 properties is known by liblzma
|
|
// without reading any of the options so it doesn't validate them.
|
|
lzma_options_lzma bad_ops;
|
|
assert_false(lzma_lzma_preset(&bad_ops, 1));
|
|
bad_ops.pb = 0x1000;
|
|
|
|
lzma_filter bad_filters[2] = {
|
|
{
|
|
.id = LZMA_FILTER_LZMA2,
|
|
.options = &bad_ops
|
|
},
|
|
{
|
|
.id = LZMA_VLI_UNKNOWN,
|
|
.options = NULL
|
|
}
|
|
};
|
|
|
|
block.filters = bad_filters;
|
|
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK);
|
|
assert_uint(block.header_size, >=, LZMA_BLOCK_HEADER_SIZE_MIN);
|
|
assert_uint(block.header_size, <=, LZMA_BLOCK_HEADER_SIZE_MAX);
|
|
assert_uint_eq(block.header_size % 4, 0);
|
|
|
|
// Use an invalid block option. The check type isn't stored in
|
|
// the Block Header and so _header_size ignores it.
|
|
block.check = INVALID_LZMA_CHECK_ID;
|
|
block.ignore_check = false;
|
|
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK);
|
|
assert_uint(block.header_size, >=, LZMA_BLOCK_HEADER_SIZE_MIN);
|
|
assert_uint(block.header_size, <=, LZMA_BLOCK_HEADER_SIZE_MAX);
|
|
assert_uint_eq(block.header_size % 4, 0);
|
|
#endif
|
|
}
|
|
|
|
|
|
static void
|
|
test_lzma_block_header_encode(void)
|
|
{
|
|
#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS)
|
|
assert_skip("Encoder or decoder support disabled");
|
|
#else
|
|
|
|
if (!lzma_filter_encoder_is_supported(LZMA_FILTER_X86)
|
|
|| !lzma_filter_decoder_is_supported(LZMA_FILTER_X86))
|
|
assert_skip("x86 BCJ encoder and/or decoder "
|
|
"is disabled");
|
|
|
|
lzma_block block = {
|
|
.version = 1,
|
|
.filters = filters_one,
|
|
.compressed_size = LZMA_VLI_UNKNOWN,
|
|
.uncompressed_size = LZMA_VLI_UNKNOWN,
|
|
.check = LZMA_CHECK_CRC32,
|
|
};
|
|
|
|
// Ensure all block options are valid before changes are tested
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK);
|
|
|
|
uint8_t out[LZMA_BLOCK_HEADER_SIZE_MAX];
|
|
|
|
// Test invalid block version
|
|
for (uint32_t i = 2; i < 20; i++) {
|
|
block.version = i;
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out),
|
|
LZMA_PROG_ERROR);
|
|
}
|
|
|
|
block.version = 1;
|
|
|
|
// Test invalid header size (< min, > max, % 4 != 0)
|
|
block.header_size = LZMA_BLOCK_HEADER_SIZE_MIN - 4;
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out),
|
|
LZMA_PROG_ERROR);
|
|
block.header_size = LZMA_BLOCK_HEADER_SIZE_MIN + 2;
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out),
|
|
LZMA_PROG_ERROR);
|
|
block.header_size = LZMA_BLOCK_HEADER_SIZE_MAX + 4;
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out),
|
|
LZMA_PROG_ERROR);
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK);
|
|
|
|
// Test invalid compressed_size
|
|
block.compressed_size = 0;
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out),
|
|
LZMA_PROG_ERROR);
|
|
block.compressed_size = LZMA_VLI_MAX + 1;
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out),
|
|
LZMA_PROG_ERROR);
|
|
|
|
// This test passes test_lzma_block_header_size, but should
|
|
// fail here because there is not enough space to encode the
|
|
// proper block size because the total size is too big to fit
|
|
// in an lzma_vli
|
|
block.compressed_size = LZMA_VLI_MAX;
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out),
|
|
LZMA_PROG_ERROR);
|
|
block.compressed_size = LZMA_VLI_UNKNOWN;
|
|
|
|
// Test invalid uncompressed size
|
|
block.uncompressed_size = LZMA_VLI_MAX + 1;
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out),
|
|
LZMA_PROG_ERROR);
|
|
block.uncompressed_size = LZMA_VLI_UNKNOWN;
|
|
|
|
// Test invalid block check
|
|
block.check = INVALID_LZMA_CHECK_ID;
|
|
block.ignore_check = false;
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out),
|
|
LZMA_PROG_ERROR);
|
|
block.check = LZMA_CHECK_CRC32;
|
|
|
|
// Test invalid filters
|
|
block.filters = NULL;
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out),
|
|
LZMA_PROG_ERROR);
|
|
|
|
block.filters = filters_none;
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out),
|
|
LZMA_PROG_ERROR);
|
|
|
|
block.filters = filters_five;
|
|
block.header_size = LZMA_BLOCK_HEADER_SIZE_MAX - 4;
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out),
|
|
LZMA_PROG_ERROR);
|
|
|
|
// Test valid encoding and verify bytes of block header.
|
|
// More complicated tests for encoding headers are included
|
|
// in test_lzma_block_header_decode.
|
|
block.filters = filters_one;
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK);
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out), LZMA_OK);
|
|
|
|
// First read block header size from out and verify
|
|
// that it == (encoded size + 1) * 4
|
|
uint32_t header_size = (out[0] + 1U) * 4;
|
|
assert_uint_eq(header_size, block.header_size);
|
|
|
|
// Next read block flags
|
|
uint8_t flags = out[1];
|
|
|
|
// Should have number of filters = 1
|
|
assert_uint_eq((flags & 0x3) + 1, 1);
|
|
|
|
// Bits 2-7 must be empty not set
|
|
assert_uint_eq(flags & (0xFF - 0x3), 0);
|
|
|
|
// Verify filter flags
|
|
// Decode Filter ID
|
|
lzma_vli filter_id = 0;
|
|
size_t pos = 2;
|
|
assert_lzma_ret(lzma_vli_decode(&filter_id, NULL, out,
|
|
&pos, header_size), LZMA_OK);
|
|
assert_uint_eq(filter_id, filters_one[0].id);
|
|
|
|
// Decode Size of Properties
|
|
lzma_vli prop_size = 0;
|
|
assert_lzma_ret(lzma_vli_decode(&prop_size, NULL, out,
|
|
&pos, header_size), LZMA_OK);
|
|
|
|
// LZMA2 has 1 byte prop size
|
|
assert_uint_eq(prop_size, 1);
|
|
uint8_t expected_filter_props = 0;
|
|
assert_lzma_ret(lzma_properties_encode(filters_one,
|
|
&expected_filter_props), LZMA_OK);
|
|
assert_uint_eq(out[pos], expected_filter_props);
|
|
pos++;
|
|
|
|
// Check null-padding
|
|
for (size_t i = pos; i < header_size - 4; i++)
|
|
assert_uint_eq(out[i], 0);
|
|
|
|
// Check CRC32
|
|
assert_uint_eq(read32le(&out[header_size - 4]), lzma_crc32(out,
|
|
header_size - 4, 0));
|
|
#endif
|
|
}
|
|
|
|
|
|
#if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS)
|
|
// Helper function to compare two lzma_block structures field by field
|
|
static void
|
|
compare_blocks(lzma_block *block_expected, lzma_block *block_actual)
|
|
{
|
|
assert_uint_eq(block_actual->version, block_expected->version);
|
|
assert_uint_eq(block_actual->compressed_size,
|
|
block_expected->compressed_size);
|
|
assert_uint_eq(block_actual->uncompressed_size,
|
|
block_expected->uncompressed_size);
|
|
assert_uint_eq(block_actual->check, block_expected->check);
|
|
assert_uint_eq(block_actual->header_size, block_expected->header_size);
|
|
|
|
// Compare filter IDs
|
|
assert_true(block_expected->filters && block_actual->filters);
|
|
lzma_filter expected_filter = block_expected->filters[0];
|
|
uint32_t filter_count = 0;
|
|
while (expected_filter.id != LZMA_VLI_UNKNOWN) {
|
|
assert_uint_eq(block_actual->filters[filter_count].id,
|
|
expected_filter.id);
|
|
expected_filter = block_expected->filters[++filter_count];
|
|
}
|
|
|
|
assert_uint_eq(block_actual->filters[filter_count].id,
|
|
LZMA_VLI_UNKNOWN);
|
|
}
|
|
#endif
|
|
|
|
|
|
static void
|
|
test_lzma_block_header_decode(void)
|
|
{
|
|
#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS)
|
|
assert_skip("Encoder or decoder support disabled");
|
|
#else
|
|
if (!lzma_filter_encoder_is_supported(LZMA_FILTER_X86)
|
|
|| !lzma_filter_decoder_is_supported(LZMA_FILTER_X86))
|
|
assert_skip("x86 BCJ encoder and/or decoder "
|
|
"is disabled");
|
|
|
|
lzma_block block = {
|
|
.filters = filters_one,
|
|
.compressed_size = LZMA_VLI_UNKNOWN,
|
|
.uncompressed_size = LZMA_VLI_UNKNOWN,
|
|
.check = LZMA_CHECK_CRC32,
|
|
.version = 0
|
|
};
|
|
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK);
|
|
|
|
// Encode block header with simple options
|
|
uint8_t out[LZMA_BLOCK_HEADER_SIZE_MAX];
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out), LZMA_OK);
|
|
|
|
// Decode block header and check that the options match
|
|
lzma_filter decoded_filters[LZMA_FILTERS_MAX + 1];
|
|
lzma_block decoded_block = {
|
|
.version = 0,
|
|
.filters = decoded_filters,
|
|
.check = LZMA_CHECK_CRC32
|
|
};
|
|
decoded_block.header_size = lzma_block_header_size_decode(out[0]);
|
|
|
|
assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out),
|
|
LZMA_OK);
|
|
compare_blocks(&block, &decoded_block);
|
|
|
|
// Reset output buffer and decoded_block
|
|
memzero(out, LZMA_BLOCK_HEADER_SIZE_MAX);
|
|
memzero(&decoded_block, sizeof(lzma_block));
|
|
decoded_block.filters = decoded_filters;
|
|
decoded_block.check = LZMA_CHECK_CRC32;
|
|
|
|
// Test with compressed size set
|
|
block.compressed_size = 4096;
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK);
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out), LZMA_OK);
|
|
decoded_block.header_size = lzma_block_header_size_decode(out[0]);
|
|
assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out),
|
|
LZMA_OK);
|
|
compare_blocks(&block, &decoded_block);
|
|
|
|
memzero(out, LZMA_BLOCK_HEADER_SIZE_MAX);
|
|
memzero(&decoded_block, sizeof(lzma_block));
|
|
decoded_block.filters = decoded_filters;
|
|
decoded_block.check = LZMA_CHECK_CRC32;
|
|
|
|
// Test with uncompressed size set
|
|
block.uncompressed_size = 4096;
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK);
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out), LZMA_OK);
|
|
decoded_block.header_size = lzma_block_header_size_decode(out[0]);
|
|
assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out),
|
|
LZMA_OK);
|
|
compare_blocks(&block, &decoded_block);
|
|
|
|
memzero(out, LZMA_BLOCK_HEADER_SIZE_MAX);
|
|
memzero(&decoded_block, sizeof(lzma_block));
|
|
decoded_block.filters = decoded_filters;
|
|
decoded_block.check = LZMA_CHECK_CRC32;
|
|
|
|
// Test with multiple filters
|
|
block.filters = filters_four;
|
|
assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK);
|
|
assert_lzma_ret(lzma_block_header_encode(&block, out), LZMA_OK);
|
|
decoded_block.header_size = lzma_block_header_size_decode(out[0]);
|
|
assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out),
|
|
LZMA_OK);
|
|
compare_blocks(&block, &decoded_block);
|
|
|
|
memzero(&decoded_block, sizeof(lzma_block));
|
|
decoded_block.filters = decoded_filters;
|
|
decoded_block.check = LZMA_CHECK_CRC32;
|
|
decoded_block.header_size = lzma_block_header_size_decode(out[0]);
|
|
|
|
// Test with too high version. The decoder will set it to a version
|
|
// that it supports.
|
|
decoded_block.version = 2;
|
|
assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out),
|
|
LZMA_OK);
|
|
assert_uint_eq(decoded_block.version, 1);
|
|
|
|
// Test bad check type
|
|
decoded_block.check = INVALID_LZMA_CHECK_ID;
|
|
assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out),
|
|
LZMA_PROG_ERROR);
|
|
decoded_block.check = LZMA_CHECK_CRC32;
|
|
|
|
// Test bad check value
|
|
out[decoded_block.header_size - 1] -= 10;
|
|
assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out),
|
|
LZMA_DATA_ERROR);
|
|
out[decoded_block.header_size - 1] += 10;
|
|
|
|
// Test non-NULL padding
|
|
out[decoded_block.header_size - 5] = 1;
|
|
|
|
// Recompute CRC32
|
|
write32le(&out[decoded_block.header_size - 4], lzma_crc32(out,
|
|
decoded_block.header_size - 4, 0));
|
|
assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out),
|
|
LZMA_OPTIONS_ERROR);
|
|
|
|
// Test unsupported flags
|
|
out[1] = 0xFF;
|
|
|
|
// Recompute CRC32
|
|
write32le(&out[decoded_block.header_size - 4], lzma_crc32(out,
|
|
decoded_block.header_size - 4, 0));
|
|
assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out),
|
|
LZMA_OPTIONS_ERROR);
|
|
#endif
|
|
}
|
|
|
|
|
|
extern int
|
|
main(int argc, char **argv)
|
|
{
|
|
tuktest_start(argc, argv);
|
|
|
|
if (lzma_lzma_preset(&opt_lzma, 1))
|
|
tuktest_error("lzma_lzma_preset() failed");
|
|
|
|
tuktest_run(test_lzma_block_header_size);
|
|
tuktest_run(test_lzma_block_header_encode);
|
|
tuktest_run(test_lzma_block_header_decode);
|
|
|
|
return tuktest_end();
|
|
}
|