diff --git a/Meta/Lagom/Fuzzers/FuzzZlibDecompression.cpp b/Meta/Lagom/Fuzzers/FuzzZlibDecompression.cpp index 064d0ec2d7..d36cc00b50 100644 --- a/Meta/Lagom/Fuzzers/FuzzZlibDecompression.cpp +++ b/Meta/Lagom/Fuzzers/FuzzZlibDecompression.cpp @@ -4,11 +4,18 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) { - (void)Compress::ZlibDecompressor::decompress_all(ReadonlyBytes { data, size }); + auto stream = make(ReadonlyBytes { data, size }); + + auto decompressor_or_error = Compress::ZlibDecompressor::create(move(stream)); + if (decompressor_or_error.is_error()) + return 0; + auto decompressor = decompressor_or_error.release_value(); + (void)decompressor->read_until_eof(); return 0; } diff --git a/Tests/LibCompress/TestZlib.cpp b/Tests/LibCompress/TestZlib.cpp index 8c72e8f3a8..d1cd37a211 100644 --- a/Tests/LibCompress/TestZlib.cpp +++ b/Tests/LibCompress/TestZlib.cpp @@ -7,6 +7,7 @@ #include #include +#include #include TEST_CASE(zlib_decompress_simple) @@ -20,8 +21,10 @@ TEST_CASE(zlib_decompress_simple) const u8 uncompressed[] = "This is a simple text file :)"; - auto const decompressed = Compress::ZlibDecompressor::decompress_all(compressed); - EXPECT(decompressed.value().bytes() == (ReadonlyBytes { uncompressed, sizeof(uncompressed) - 1 })); + auto stream = make(compressed); + auto decompressor = TRY_OR_FAIL(Compress::ZlibDecompressor::create(move(stream))); + auto decompressed = TRY_OR_FAIL(decompressor->read_until_eof()); + EXPECT(decompressed.bytes() == (ReadonlyBytes { uncompressed, sizeof(uncompressed) - 1 })); } TEST_CASE(zlib_compress_simple) @@ -58,7 +61,7 @@ TEST_CASE(zlib_decompress_with_missing_end_bits) 0x17, 0x17, 0x08, 0x43, 0xC5, 0xC9, 0x05, 0xA8, 0x4B, 0x50, 0x50, 0x50, 0xC4, 0xD1, 0x45, 0x50, 0x80, 0x01, 0x06, 0x00, 0xB6, 0x1F, 0x15, 0xEF }; - Array const decompressed { + Array const uncompressed { 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x10, 0x00, 0x32, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x10, 0x00, 0x32, 0x55, 0x22, 0x25, 0x52, 0x22, 0x22, 0x10, 0x00, 0x32, 0x55, 0x22, 0x25, 0x52, 0x22, 0x22, 0x10, @@ -73,7 +76,8 @@ TEST_CASE(zlib_decompress_with_missing_end_bits) 0x44, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - auto const maybe_decompressed = Compress::ZlibDecompressor::decompress_all(compressed); - EXPECT(maybe_decompressed.has_value()); - EXPECT_EQ(maybe_decompressed.value().span(), decompressed.span()); + auto stream = make(compressed); + auto decompressor = TRY_OR_FAIL(Compress::ZlibDecompressor::create(move(stream))); + auto decompressed = TRY_OR_FAIL(decompressor->read_until_eof()); + EXPECT_EQ(decompressed.span(), uncompressed.span()); } diff --git a/Userland/Libraries/LibCompress/Zlib.cpp b/Userland/Libraries/LibCompress/Zlib.cpp index 201b998901..6978eae94c 100644 --- a/Userland/Libraries/LibCompress/Zlib.cpp +++ b/Userland/Libraries/LibCompress/Zlib.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -14,59 +15,53 @@ namespace Compress { -constexpr static size_t Adler32Size = sizeof(u32); - -Optional ZlibDecompressor::try_create(ReadonlyBytes data) +ErrorOr> ZlibDecompressor::create(MaybeOwned stream) { - if (data.size() < (sizeof(ZlibHeader) + Adler32Size)) - return {}; - - ZlibHeader header { .as_u16 = data.at(0) << 8 | data.at(1) }; + auto header = TRY(stream->read_value()); if (header.compression_method != ZlibCompressionMethod::Deflate || header.compression_info > 7) - return {}; // non-deflate compression + return Error::from_string_literal("Non-DEFLATE compression inside Zlib is not supported"); if (header.present_dictionary) - return {}; // we dont support pre-defined dictionaries + return Error::from_string_literal("Zlib compression with a pre-defined dictionary is currently not supported"); if (header.as_u16 % 31 != 0) - return {}; // error correction code doesn't match + return Error::from_string_literal("Zlib error correction code does not match"); - ZlibDecompressor zlib { header, data }; - zlib.m_data_bytes = data.slice(2, data.size() - sizeof(ZlibHeader) - Adler32Size); - return zlib; + auto bit_stream = make(move(stream)); + auto deflate_stream = TRY(Compress::DeflateDecompressor::construct(move(bit_stream))); + + return adopt_nonnull_own_or_enomem(new (nothrow) ZlibDecompressor(header, move(deflate_stream))); } -ZlibDecompressor::ZlibDecompressor(ZlibHeader header, ReadonlyBytes data) +ZlibDecompressor::ZlibDecompressor(ZlibHeader header, NonnullOwnPtr stream) : m_header(header) - , m_input_data(data) + , m_stream(move(stream)) { } -Optional ZlibDecompressor::decompress() +ErrorOr ZlibDecompressor::read_some(Bytes bytes) { - auto buffer_or_error = DeflateDecompressor::decompress_all(m_data_bytes); - if (buffer_or_error.is_error()) - return {}; - return buffer_or_error.release_value(); + return m_stream->read_some(bytes); } -Optional ZlibDecompressor::decompress_all(ReadonlyBytes bytes) +ErrorOr ZlibDecompressor::write_some(ReadonlyBytes) { - auto zlib = try_create(bytes); - if (!zlib.has_value()) - return {}; - return zlib->decompress(); + return Error::from_errno(EBADF); } -u32 ZlibDecompressor::checksum() +bool ZlibDecompressor::is_eof() const { - if (!m_checksum) { - auto bytes = m_input_data.slice_from_end(Adler32Size); - m_checksum = bytes.at(0) << 24 | bytes.at(1) << 16 | bytes.at(2) << 8 || bytes.at(3); - } + return m_stream->is_eof(); +} - return m_checksum; +bool ZlibDecompressor::is_open() const +{ + return m_stream->is_open(); +} + +void ZlibDecompressor::close() +{ } ErrorOr> ZlibCompressor::construct(MaybeOwned stream, ZlibCompressionLevel compression_level) diff --git a/Userland/Libraries/LibCompress/Zlib.h b/Userland/Libraries/LibCompress/Zlib.h index 2039973f2b..6a3e0813f3 100644 --- a/Userland/Libraries/LibCompress/Zlib.h +++ b/Userland/Libraries/LibCompress/Zlib.h @@ -44,22 +44,21 @@ struct ZlibHeader { }; static_assert(sizeof(ZlibHeader) == sizeof(u16)); -class ZlibDecompressor { +class ZlibDecompressor : public Stream { public: - Optional decompress(); - u32 checksum(); + static ErrorOr> create(MaybeOwned); - static Optional try_create(ReadonlyBytes data); - static Optional decompress_all(ReadonlyBytes); + virtual ErrorOr read_some(Bytes) override; + virtual ErrorOr write_some(ReadonlyBytes) override; + virtual bool is_eof() const override; + virtual bool is_open() const override; + virtual void close() override; private: - ZlibDecompressor(ZlibHeader, ReadonlyBytes data); + ZlibDecompressor(ZlibHeader, NonnullOwnPtr); ZlibHeader m_header; - - u32 m_checksum { 0 }; - ReadonlyBytes m_input_data; - ReadonlyBytes m_data_bytes; + NonnullOwnPtr m_stream; }; class ZlibCompressor : public Stream { @@ -87,3 +86,8 @@ private: }; } + +template<> +struct AK::Traits : public AK::GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; diff --git a/Userland/Libraries/LibGfx/Font/WOFF/Font.cpp b/Userland/Libraries/LibGfx/Font/WOFF/Font.cpp index 43edf15953..f94abc1c29 100644 --- a/Userland/Libraries/LibGfx/Font/WOFF/Font.cpp +++ b/Userland/Libraries/LibGfx/Font/WOFF/Font.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -120,12 +121,12 @@ ErrorOr> Font::try_load_from_externally_owned_memory(Readonl if (font_buffer_offset + orig_length > font_buffer.size()) return Error::from_string_literal("Uncompressed WOFF table too big"); if (comp_length < orig_length) { - auto decompressed = Compress::ZlibDecompressor::decompress_all(buffer.slice(offset, comp_length)); - if (!decompressed.has_value()) - return Error::from_string_literal("Could not decompress WOFF table"); - if (orig_length != decompressed->size()) + auto compressed_data_stream = make(buffer.slice(offset, comp_length)); + auto decompressor = TRY(Compress::ZlibDecompressor::create(move(compressed_data_stream))); + auto decompressed = TRY(decompressor->read_until_eof()); + if (orig_length != decompressed.size()) return Error::from_string_literal("Invalid decompressed WOFF table length"); - font_buffer.overwrite(font_buffer_offset, decompressed->data(), orig_length); + font_buffer.overwrite(font_buffer_offset, decompressed.data(), orig_length); } else { if (comp_length != orig_length) return Error::from_string_literal("Invalid uncompressed WOFF table length"); diff --git a/Userland/Libraries/LibGfx/ImageFormats/PNGLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/PNGLoader.cpp index a17b00809a..a0084e8096 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/PNGLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/PNGLoader.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -852,12 +853,19 @@ static ErrorOr decode_png_bitmap(PNGLoadingContext& context) if (context.color_type == PNG::ColorType::IndexedColor && context.palette_data.is_empty()) return Error::from_string_literal("PNGImageDecoderPlugin: Didn't see a PLTE chunk for a palletized image, or it was empty."); - auto result = Compress::ZlibDecompressor::decompress_all(context.compressed_data.span()); - if (!result.has_value()) { + auto compressed_data_stream = make(context.compressed_data.span()); + auto decompressor_or_error = Compress::ZlibDecompressor::create(move(compressed_data_stream)); + if (decompressor_or_error.is_error()) { context.state = PNGLoadingContext::State::Error; - return Error::from_string_literal("PNGImageDecoderPlugin: Decompression failed"); + return decompressor_or_error.release_error(); } - auto& decompression_buffer = result.value(); + auto decompressor = decompressor_or_error.release_value(); + auto result_or_error = decompressor->read_until_eof(); + if (result_or_error.is_error()) { + context.state = PNGLoadingContext::State::Error; + return result_or_error.release_error(); + } + auto decompression_buffer = result_or_error.release_value(); context.compressed_data.clear(); context.scanlines.ensure_capacity(context.height); @@ -887,11 +895,9 @@ static ErrorOr> decode_png_animation_frame_bitmap(PNGLoadingConte auto frame_rect = animation_frame.rect(); auto frame_context = context.create_subimage_context(frame_rect.width(), frame_rect.height()); - auto result = Compress::ZlibDecompressor::decompress_all(animation_frame.compressed_data.span()); - if (!result.has_value()) - return Error::from_string_literal("PNGImageDecoderPlugin: Decompression of animation frame failed"); - - auto& decompression_buffer = result.value(); + auto compressed_data_stream = make(animation_frame.compressed_data.span()); + auto decompressor = TRY(Compress::ZlibDecompressor::create(move(compressed_data_stream))); + auto decompression_buffer = TRY(decompressor->read_until_eof()); frame_context.compressed_data.clear(); frame_context.scanlines.ensure_capacity(frame_context.height); @@ -1443,12 +1449,19 @@ ErrorOr> PNGImageDecoderPlugin::icc_data() if (m_context->embedded_icc_profile.has_value()) { if (!m_context->decompressed_icc_profile.has_value()) { - auto result = Compress::ZlibDecompressor::decompress_all(m_context->embedded_icc_profile->compressed_data); - if (!result.has_value()) { + auto compressed_data_stream = make(m_context->embedded_icc_profile->compressed_data); + auto decompressor_or_error = Compress::ZlibDecompressor::create(move(compressed_data_stream)); + if (decompressor_or_error.is_error()) { m_context->embedded_icc_profile.clear(); - return Error::from_string_literal("PNGImageDecoderPlugin: Decompression of ICC profile failed"); + return decompressor_or_error.release_error(); } - m_context->decompressed_icc_profile = move(*result); + auto decompressor = decompressor_or_error.release_value(); + auto result_or_error = decompressor->read_until_eof(); + if (result_or_error.is_error()) { + m_context->embedded_icc_profile.clear(); + return result_or_error.release_error(); + } + m_context->decompressed_icc_profile = result_or_error.release_value(); } return m_context->decompressed_icc_profile.value(); diff --git a/Userland/Libraries/LibHTTP/Job.cpp b/Userland/Libraries/LibHTTP/Job.cpp index e8f9458b0d..a0810fee84 100644 --- a/Userland/Libraries/LibHTTP/Job.cpp +++ b/Userland/Libraries/LibHTTP/Job.cpp @@ -51,13 +51,17 @@ static ErrorOr handle_content_encoding(ByteBuffer const& buf, Deprec // Even though the content encoding is "deflate", it's actually deflate with the zlib wrapper. // https://tools.ietf.org/html/rfc7230#section-4.2.2 - auto uncompressed = Compress::ZlibDecompressor::decompress_all(buf); - if (!uncompressed.has_value()) { + auto memory_stream = make(buf); + auto zlib_decompressor = Compress::ZlibDecompressor::create(move(memory_stream)); + Optional uncompressed; + if (zlib_decompressor.is_error()) { // From the RFC: // "Note: Some non-conformant implementations send the "deflate" // compressed data without the zlib wrapper." dbgln_if(JOB_DEBUG, "Job::handle_content_encoding: ZlibDecompressor::decompress_all() failed. Trying DeflateDecompressor::decompress_all()"); uncompressed = TRY(Compress::DeflateDecompressor::decompress_all(buf)); + } else { + uncompressed = TRY(zlib_decompressor.value()->read_until_eof()); } if constexpr (JOB_DEBUG) {