LibCompress: Brotli support metadata of skip_length=0

The relevant RFC section from
https://www.rfc-editor.org/rfc/rfc7932#section-9.2

MSKIPBYTES * 8 bits: MSKIPLEN - 1, where MSKIPLEN is
       the number of metadata bytes; this field is
       only present if MSKIPBYTES is positive;
       otherwise, MSKIPLEN is 0 (if MSKIPBYTES is
       greater than 1, and the last byte is all
       zeros, then the stream should be rejected as
       invalid)

So when skip_bytes is zero we need to break and
re-align bytes.

Added the relevant test case that demonstrates this from:
https://github.com/google/brotli/blob/master/tests/testdata/x.compressed
This commit is contained in:
Tommy Murphy 2022-09-18 13:38:07 -04:00 committed by Andrew Kaster
parent 7ea987456a
commit 0ee98c69c1
Notes: sideshowbarker 2024-07-17 06:01:56 +09:00
4 changed files with 53 additions and 1 deletions

View file

@ -80,6 +80,11 @@ TEST_CASE(brotli_single_z)
run_test("single-z.txt"sv);
}
TEST_CASE(brotli_single_x)
{
run_test("single-x.txt"sv);
}
TEST_CASE(brotli_decompress_zero_one_bin)
{
// This makes sure that the tests will run both on target and in Lagom.

View file

@ -0,0 +1 @@
X

Binary file not shown.

View file

@ -589,22 +589,59 @@ ErrorOr<Bytes> BrotliDecompressionStream::read(Bytes output_buffer)
if (m_read_final_block)
break;
// RFC 7932 section 9.1
//
// 1 bit: ISLAST, set to 1 if this is the last meta-block
m_read_final_block = TRY(m_input_stream.read_bit());
if (m_read_final_block) {
// 1 bit: ISLASTEMPTY, if set to 1, the meta-block is empty; this
// field is only present if ISLAST bit is set -- if it is 1,
// then the meta-block and the brotli stream ends at that
// bit, with any remaining bits in the last byte of the
// compressed stream filled with zeros (if the fill bits are
// not zero, then the stream should be rejected as invalid)
bool is_last_block_empty = TRY(m_input_stream.read_bit());
// If the last block is empty we are done decompressing
if (is_last_block_empty)
break;
}
// 2 bits: MNIBBLES, number of nibbles to represent the uncompressed
// length
size_t size_number_of_nibbles = TRY(read_size_number_of_nibbles());
// If MNIBBLES is 0, the meta-block is empty, i.e., it does
// not generate any uncompressed data. In this case, the
// rest of the meta-block has the following format:
if (size_number_of_nibbles == 0) {
// This block only contains meta-data
// 1 bit: reserved, must be zero
bool reserved = TRY(m_input_stream.read_bit());
if (reserved)
return Error::from_string_literal("invalid reserved bit");
// 2 bits: MSKIPBYTES, number of bytes to represent
// metadata length
//
// MSKIPBYTES * 8 bits: MSKIPLEN - 1, where MSKIPLEN is
// the number of metadata bytes; this field is
// only present if MSKIPBYTES is positive;
// otherwise, MSKIPLEN is 0 (if MSKIPBYTES is
// greater than 1, and the last byte is all
// zeros, then the stream should be rejected as
// invalid)
size_t skip_bytes = TRY(m_input_stream.read_bits(2));
if (skip_bytes == 0) {
// 0..7 bits: fill bits until the next byte boundary,
// must be all zeros
u8 remainder = m_input_stream.align_to_byte_boundary();
if (remainder != 0)
return Error::from_string_literal("remainder bits are non-zero");
continue;
}
// MSKIPLEN bytes of metadata, not part of the
// uncompressed data or the sliding window
size_t skip_length = 1 + TRY(m_input_stream.read_bits(8 * skip_bytes));
u8 remainder = m_input_stream.align_to_byte_boundary();
@ -619,6 +656,8 @@ ErrorOr<Bytes> BrotliDecompressionStream::read(Bytes output_buffer)
auto metadata_bytes = TRY(m_input_stream.read(temp_bytes_slice));
if (metadata_bytes.is_empty())
return Error::from_string_literal("eof");
if (metadata_bytes.last() == 0)
return Error::from_string_literal("invalid stream");
skip_length -= metadata_bytes.size();
}
@ -626,6 +665,13 @@ ErrorOr<Bytes> BrotliDecompressionStream::read(Bytes output_buffer)
}
size_t uncompressed_size = 1 + TRY(m_input_stream.read_bits(4 * size_number_of_nibbles));
// 1 bit: ISUNCOMPRESSED, if set to 1, any bits of compressed data
// up to the next byte boundary are ignored, and the rest of
// the meta-block contains MLEN bytes of literal data; this
// field is only present if the ISLAST bit is not set (if the
// ignored bits are not all zeros, the stream should be
// rejected as invalid)
bool is_uncompressed = false;
if (!m_read_final_block)
is_uncompressed = TRY(m_input_stream.read_bit());