From 7a235537e899e545a29e85f460f2907a3f66a51f Mon Sep 17 00:00:00 2001 From: Tete17 Date: Fri, 16 May 2025 00:05:53 +0200 Subject: [PATCH] LibCompress: Error out when encounters and incomplete stream If we find ourselves in a situation where zlib can't make any progress, we don't have any more data to feed in and no output has been produced, we need to raise an error as the compressed data is incomplete. This used to lead to an infinite busy loop where we keep calling zlib to decompressed but is not able. This causes the promise on the read side of the transformer to never fulfill. This gives us at least 24 more WPT tests :) --- Libraries/LibCompress/GenericZlib.cpp | 6 ++++++ Libraries/LibWeb/Compression/DecompressionStream.cpp | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Libraries/LibCompress/GenericZlib.cpp b/Libraries/LibCompress/GenericZlib.cpp index b8ff099744d..1ab1d992225 100644 --- a/Libraries/LibCompress/GenericZlib.cpp +++ b/Libraries/LibCompress/GenericZlib.cpp @@ -76,9 +76,15 @@ ErrorOr GenericZlibDecompressor::read_some(Bytes bytes) } auto ret = inflate(m_zstream, Z_NO_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) return handle_zlib_error(ret); + // We got Z_BUF_ERROR (no progress was possible), no more input, stream is EOF and no output was produced. + // There is no way to get out of this loop, error out. + if (ret == Z_BUF_ERROR && m_zstream->avail_in == 0 && m_stream->is_eof() && bytes.size() == m_zstream->avail_out) + return Error::from_string_literal("No decompression progress on EOF stream"); + if (ret == Z_STREAM_END) { inflateReset(m_zstream); if (m_zstream->avail_in == 0) diff --git a/Libraries/LibWeb/Compression/DecompressionStream.cpp b/Libraries/LibWeb/Compression/DecompressionStream.cpp index 60540e4ebda..13dcf0e45b5 100644 --- a/Libraries/LibWeb/Compression/DecompressionStream.cpp +++ b/Libraries/LibWeb/Compression/DecompressionStream.cpp @@ -153,13 +153,13 @@ WebIDL::ExceptionOr DecompressionStream::decompress_flush_and_enqueue() return TRY(decompressor->read_until_eof()); }); if (maybe_buffer.is_error()) - return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unable to compress flush: {}", maybe_buffer.error())) }; + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unable to decompress flush: {}", maybe_buffer.error())) }; auto buffer = maybe_buffer.release_value(); + // Note: LibCompress already throws an error if we call read_until_eof and no more progress can be made // 2. If the end of the compressed input has not been reached, then throw a TypeError. - if (m_decompressor.visit([](auto const& decompressor) { return !decompressor->is_eof(); })) - return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "End of compressed input has not been reached"sv }; + VERIFY(m_decompressor.visit([](auto const& decompressor) { return decompressor->is_eof(); })); // 3. If buffer is empty, return. if (buffer.is_empty())