1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 19:47:34 +00:00

LibCompress: Fix off-by-one error in generate_huffman_lengths

Previously we would calculate the index of the first parent node as
heap.size() (which is initialized to non_zero_freqs), so in the edge
case in which all symbols had a non-zero frequency, we would use the
Size-index entry in the array for both the first symbol's leaf node,
and the first parent node.

The result would either be a non-optimal huffman code (bad), or an
illegal huffman code that would then go on to crash due to an error
check in CanonicalCode::from_bytes. (worse)

We now store parent nodes starting at heap.size() - 1, which eliminates
the potential overlap, and resolves the issue.
This commit is contained in:
Idan Horowitz 2023-12-01 20:42:54 +02:00 committed by Tim Schumacher
parent ec081a2ef5
commit b749167506
4 changed files with 21 additions and 3 deletions

View file

@ -11,8 +11,15 @@
#include <AK/MemoryStream.h>
#include <AK/Random.h>
#include <LibCompress/Deflate.h>
#include <LibCore/File.h>
#include <cstring>
#ifdef AK_OS_SERENITY
# define TEST_INPUT(x) ("/usr/Tests/LibCompress/deflate-test-files/" x)
#else
# define TEST_INPUT(x) ("deflate-test-files/" x)
#endif
TEST_CASE(canonical_code_simple)
{
Array<u8, 32> const code {
@ -155,3 +162,13 @@ TEST_CASE(deflate_compress_literals)
Array<u8, 0x13> test { 0, 0, 0, 0, 0x72, 0, 0, 0xee, 0, 0, 0, 0x26, 0, 0, 0, 0x28, 0, 0, 0x72 };
auto compressed = TRY_OR_FAIL(Compress::DeflateCompressor::compress_all(test, Compress::DeflateCompressor::CompressionLevel::GOOD));
}
TEST_CASE(ossfuzz_63183)
{
auto path = TEST_INPUT("clusterfuzz-testcase-minimized-FuzzDeflateCompression-6163230961303552.fuzz"sv);
auto test_file = MUST(Core::File::open(path, Core::File::OpenMode::Read));
auto test_data = MUST(test_file->read_until_eof());
auto compressed = TRY_OR_FAIL(Compress::DeflateCompressor::compress_all(test_data, Compress::DeflateCompressor::CompressionLevel::GOOD));
auto decompressed = TRY_OR_FAIL(Compress::DeflateDecompressor::decompress_all(compressed));
EXPECT(test_data == decompressed);
}