summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Deymo <deymo@google.com>2018-03-12 20:00:04 +0100
committerAlex Deymo <deymo@google.com>2018-03-12 20:06:27 +0100
commit64d5cd8065d842026b4e0887e8bddc2b89db3a59 (patch)
treecd493a53ca07011ea71ebe7e4d2e7af1f407cbee
parent6277fec4beb150e2ad9773314d1e97c8daf24ec8 (diff)
downloadbsdiff-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.bp1
-rw-r--r--Makefile1
-rw-r--r--bsdiff.gyp1
-rw-r--r--bz2_decompressor.cc7
-rw-r--r--bz2_decompressor_unittest.cc58
5 files changed, 68 insertions, 0 deletions
diff --git a/Android.bp b/Android.bp
index 7390a11..8d2ec0f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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",
diff --git a/Makefile b/Makefile
index e721e7d..7846a4f 100644
--- a/Makefile
+++ b/Makefile
@@ -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 \
diff --git a/bsdiff.gyp b/bsdiff.gyp
index 40bad2a..629b9e0 100644
--- a/bsdiff.gyp
+++ b/bsdiff.gyp
@@ -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