diff options
author | Alex Deymo <deymo@google.com> | 2018-03-12 20:00:04 +0100 |
---|---|---|
committer | Alex Deymo <deymo@google.com> | 2018-03-12 20:06:27 +0100 |
commit | 64d5cd8065d842026b4e0887e8bddc2b89db3a59 (patch) | |
tree | cd493a53ca07011ea71ebe7e4d2e7af1f407cbee | |
parent | 6277fec4beb150e2ad9773314d1e97c8daf24ec8 (diff) | |
download | bsdiff-64d5cd8065d842026b4e0887e8bddc2b89db3a59.tar.gz |
Fix infinite loop when providing a partial input bz2 stream.
The bz2 decompressor expects to read from the input until there's enough
output produced. This patch makes the read fail if we get stuck without
making any progress in the decompression.
Bug: chromium:818174
Test: Added unittest.
Change-Id: Iaee8a2184ec1c5a7dbbe261ec77a39ce247965a3
-rw-r--r-- | Android.bp | 1 | ||||
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | bsdiff.gyp | 1 | ||||
-rw-r--r-- | bz2_decompressor.cc | 7 | ||||
-rw-r--r-- | bz2_decompressor_unittest.cc | 58 |
5 files changed, 68 insertions, 0 deletions
@@ -117,6 +117,7 @@ cc_test { "bsdiff_arguments_unittest.cc", "bsdiff_unittest.cc", "bspatch_unittest.cc", + "bz2_decompressor_unittest.cc", "diff_encoder_unittest.cc", "endsley_patch_writer_unittest.cc", "extents_file_unittest.cc", @@ -68,6 +68,7 @@ bsdiff_common_unittests := \ bsdiff_arguments_unittest.cc \ bsdiff_unittest.cc \ bspatch_unittest.cc \ + bz2_decompressor_unittest.cc \ diff_encoder_unittest.cc \ endsley_patch_writer_unittest.cc \ extents_file_unittest.cc \ @@ -155,6 +155,7 @@ 'bsdiff_arguments_unittest.cc', 'bsdiff_unittest.cc', 'bspatch_unittest.cc', + 'bz2_decompressor_unittest.cc', 'diff_encoder_unittest.cc', 'endsley_patch_writer_unittest.cc', 'extents_file_unittest.cc', diff --git a/bz2_decompressor.cc b/bz2_decompressor.cc index 16a31e7..4504ac5 100644 --- a/bz2_decompressor.cc +++ b/bz2_decompressor.cc @@ -51,6 +51,7 @@ bool BZ2Decompressor::Read(uint8_t* output_data, size_t bytes_to_output) { std::numeric_limits<unsigned int>::max(), bytes_to_output); stream_.avail_out = output_size; + size_t prev_avail_in = stream_.avail_in; int bz2err = BZ2_bzDecompress(&stream_); if (bz2err != BZ_OK && bz2err != BZ_STREAM_END) { LOG(ERROR) << "Failed to decompress " << output_size @@ -58,6 +59,12 @@ bool BZ2Decompressor::Read(uint8_t* output_data, size_t bytes_to_output) { return false; } bytes_to_output -= (output_size - stream_.avail_out); + if (bytes_to_output && prev_avail_in == stream_.avail_in && + output_size == stream_.avail_out) { + LOG(ERROR) << "BZ2Decompressor made no progress, pending " + << bytes_to_output << " bytes to decompress"; + return false; + } } return true; } diff --git a/bz2_decompressor_unittest.cc b/bz2_decompressor_unittest.cc new file mode 100644 index 0000000..6fa819c --- /dev/null +++ b/bz2_decompressor_unittest.cc @@ -0,0 +1,58 @@ +// Copyright 2018 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "bsdiff/bz2_decompressor.h" + +#include <memory> + +#include <gtest/gtest.h> + +namespace { + +// echo -n "Hello!" | bzip2 -9 | hexdump -v -e '" " 11/1 "0x%02x, " "\n"' +constexpr uint8_t kBZ2Hello[] = { + 0x42, 0x5a, 0x68, 0x39, 0x31, 0x41, 0x59, 0x26, 0x53, 0x59, 0x1a, + 0xea, 0x74, 0xba, 0x00, 0x00, 0x00, 0x95, 0x00, 0x20, 0x00, 0x00, + 0x40, 0x02, 0x04, 0xa0, 0x00, 0x21, 0x83, 0x41, 0x9a, 0x02, 0x5c, + 0x2e, 0x2e, 0xe4, 0x8a, 0x70, 0xa1, 0x20, 0x35, 0xd4, 0xe9, 0x74, +}; + +} // namespace + +namespace bsdiff { + +class BZ2DecompressorTest : public testing::Test { + protected: + void SetUp() { + decompressor_.reset(new BZ2Decompressor()); + EXPECT_NE(nullptr, decompressor_.get()); + } + + std::unique_ptr<BZ2Decompressor> decompressor_; +}; + +TEST_F(BZ2DecompressorTest, ReadingFromEmptyFileTest) { + uint8_t data = 0; + EXPECT_TRUE(decompressor_->SetInputData(&data, 0)); + + uint8_t output_data[10]; + EXPECT_FALSE(decompressor_->Read(output_data, sizeof(output_data))); +} + +// Check that we fail to read from a truncated file. +TEST_F(BZ2DecompressorTest, ReadingFromTruncatedFileTest) { + // We feed only half of the compressed file. + EXPECT_TRUE(decompressor_->SetInputData(kBZ2Hello, sizeof(kBZ2Hello) / 2)); + uint8_t output_data[6]; + EXPECT_FALSE(decompressor_->Read(output_data, sizeof(output_data))); +} + +// Check that we fail to read more than it is available in the file. +TEST_F(BZ2DecompressorTest, ReadingMoreThanAvailableTest) { + EXPECT_TRUE(decompressor_->SetInputData(kBZ2Hello, sizeof(kBZ2Hello))); + uint8_t output_data[1000]; + EXPECT_FALSE(decompressor_->Read(output_data, sizeof(output_data))); +} + +} // namespace bsdiff |