From 00332c9b7d52d444c4c84af49d542c983a0231ac Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Thu, 30 Mar 2023 12:14:07 +0200 Subject: [PATCH] LibCompress: Move XZ header validation into the read function The constructor is now only concerned with creating the required streams, which means that it no longer fails for XZ streams with invalid headers. Instead, everything is parsed and validated during the first read, preparing us for files with multiple streams. --- Tests/LibCompress/TestXz.cpp | 10 ++++++---- Userland/Libraries/LibCompress/Xz.cpp | 19 +++++++++++-------- Userland/Libraries/LibCompress/Xz.h | 4 ++-- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Tests/LibCompress/TestXz.cpp b/Tests/LibCompress/TestXz.cpp index 8bb6af4f01..ec8dc31f1f 100644 --- a/Tests/LibCompress/TestXz.cpp +++ b/Tests/LibCompress/TestXz.cpp @@ -118,8 +118,9 @@ TEST_CASE(xz_utils_bad_0_header_magic) }; auto stream = MUST(try_make(compressed)); - auto decompressor_or_error = Compress::XzDecompressor::create(move(stream)); - EXPECT(decompressor_or_error.is_error()); + auto decompressor = MUST(Compress::XzDecompressor::create(move(stream))); + auto buffer_or_error = decompressor->read_until_eof(PAGE_SIZE); + EXPECT(buffer_or_error.is_error()); } TEST_CASE(xz_utils_bad_0_nonempty_index) @@ -695,8 +696,9 @@ TEST_CASE(xz_utils_bad_1_stream_flags_2) }; auto stream = MUST(try_make(compressed)); - auto decompressor_or_error = Compress::XzDecompressor::create(move(stream)); - EXPECT(decompressor_or_error.is_error()); + auto decompressor = MUST(Compress::XzDecompressor::create(move(stream))); + auto buffer_or_error = decompressor->read_until_eof(PAGE_SIZE); + EXPECT(buffer_or_error.is_error()); } TEST_CASE(xz_utils_bad_1_stream_flags_3) diff --git a/Userland/Libraries/LibCompress/Xz.cpp b/Userland/Libraries/LibCompress/Xz.cpp index 7fabb841f3..cf996111e4 100644 --- a/Userland/Libraries/LibCompress/Xz.cpp +++ b/Userland/Libraries/LibCompress/Xz.cpp @@ -165,17 +165,13 @@ ErrorOr> XzDecompressor::create(MaybeOwned { auto counting_stream = TRY(try_make(move(stream))); - auto stream_header = TRY(counting_stream->read_value()); - TRY(stream_header.validate()); - - auto decompressor = TRY(adopt_nonnull_own_or_enomem(new (nothrow) XzDecompressor(move(counting_stream), stream_header.flags))); + auto decompressor = TRY(adopt_nonnull_own_or_enomem(new (nothrow) XzDecompressor(move(counting_stream)))); return decompressor; } -XzDecompressor::XzDecompressor(NonnullOwnPtr stream, XzStreamFlags stream_flags) +XzDecompressor::XzDecompressor(NonnullOwnPtr stream) : m_stream(move(stream)) - , m_stream_flags(stream_flags) { } @@ -184,6 +180,13 @@ ErrorOr XzDecompressor::read_some(Bytes bytes) if (m_found_stream_footer) return bytes.trim(0); + if (!m_stream_flags.has_value()) { + auto stream_header = TRY(m_stream->read_value()); + TRY(stream_header.validate()); + + m_stream_flags = stream_header.flags; + } + if (!m_current_block_stream.has_value() || (*m_current_block_stream)->is_eof()) { if (m_current_block_stream.has_value()) { // We have already processed a block, so we weed to clean up trailing data before the next block starts. @@ -212,7 +215,7 @@ ErrorOr XzDecompressor::read_some(Bytes bytes) // indicate a warning or error." // TODO: Block content checks are currently unimplemented as a whole, independent of the check type. // For now, we only make sure to remove the correct amount of bytes from the stream. - switch (m_stream_flags.check_type) { + switch (m_stream_flags->check_type) { case XzStreamCheckType::None: break; case XzStreamCheckType::CRC32: @@ -329,7 +332,7 @@ ErrorOr XzDecompressor::read_some(Bytes bytes) // when parsing the Stream backwards. The decoder MUST compare // the Stream Flags fields in both Stream Header and Stream // Footer, and indicate an error if they are not identical." - if (Bytes { &m_stream_flags, sizeof(m_stream_flags) } != Bytes { &stream_footer.flags, sizeof(stream_footer.flags) }) + if (Bytes { &*m_stream_flags, sizeof(XzStreamFlags) } != Bytes { &stream_footer.flags, sizeof(stream_footer.flags) }) return Error::from_string_literal("XZ stream header flags don't match the stream footer"); m_found_stream_footer = true; diff --git a/Userland/Libraries/LibCompress/Xz.h b/Userland/Libraries/LibCompress/Xz.h index 1e95a7ea86..8decf07799 100644 --- a/Userland/Libraries/LibCompress/Xz.h +++ b/Userland/Libraries/LibCompress/Xz.h @@ -108,10 +108,10 @@ public: virtual void close() override; private: - XzDecompressor(NonnullOwnPtr, XzStreamFlags); + XzDecompressor(NonnullOwnPtr); NonnullOwnPtr m_stream; - XzStreamFlags m_stream_flags; + Optional m_stream_flags; bool m_found_stream_footer { false }; Optional> m_current_block_stream {};