diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h index dd463d1f7..018b1a922 100644 --- a/libziparchive/include/ziparchive/zip_archive.h +++ b/libziparchive/include/ziparchive/zip_archive.h @@ -265,6 +265,9 @@ class Reader { * Returns 0 on success and negative values on failure, for example if |reader| * cannot supply the right amount of data, or if the number of bytes written to * data does not match |uncompressed_length|. + * + * If |crc_out| is not nullptr, it is set to the crc32 checksum of the + * uncompressed data. */ int32_t Inflate(const Reader& reader, const uint32_t compressed_length, const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out); diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc index 753526316..1298caf0e 100644 --- a/libziparchive/zip_archive.cc +++ b/libziparchive/zip_archive.cc @@ -924,6 +924,7 @@ int32_t Inflate(const Reader& reader, const uint32_t compressed_length, std::unique_ptr zstream_guard(&zstream, zstream_deleter); + const bool compute_crc = (crc_out != nullptr); uint64_t crc = 0; uint32_t remaining_bytes = compressed_length; do { @@ -955,9 +956,8 @@ int32_t Inflate(const Reader& reader, const uint32_t compressed_length, if (zstream.avail_out == 0 || (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) { const size_t write_size = zstream.next_out - &write_buf[0]; if (!writer->Append(&write_buf[0], write_size)) { - // The file might have declared a bogus length. - return kInconsistentInformation; - } else { + return kIoError; + } else if (compute_crc) { crc = crc32(crc, &write_buf[0], write_size); } @@ -974,7 +974,9 @@ int32_t Inflate(const Reader& reader, const uint32_t compressed_length, // it ourselves above because there are no additional gains to be made by // having zlib calculate it for us, since they do it by calling crc32 in // the same manner that we have above. - *crc_out = crc; + if (compute_crc) { + *crc_out = crc; + } if (zstream.total_out != uncompressed_length || remaining_bytes != 0) { ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", zstream.total_out, diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc index 753bd4427..374310b4d 100644 --- a/libziparchive/zip_archive_test.cc +++ b/libziparchive/zip_archive_test.cc @@ -766,6 +766,93 @@ TEST(ziparchive, BrokenLfhSignature) { ASSERT_EQ(-1, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle)); } +class VectorReader : public zip_archive::Reader { + public: + VectorReader(const std::vector& input) : Reader(), input_(input) {} + + bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const { + if ((offset + len) < input_.size()) { + return false; + } + + memcpy(buf, &input_[offset], len); + return true; + } + + private: + const std::vector& input_; +}; + +class VectorWriter : public zip_archive::Writer { + public: + VectorWriter() : Writer() {} + + bool Append(uint8_t* buf, size_t size) { + output_.insert(output_.end(), buf, buf + size); + return true; + } + + std::vector& GetOutput() { return output_; } + + private: + std::vector output_; +}; + +class BadReader : public zip_archive::Reader { + public: + BadReader() : Reader() {} + + bool ReadAtOffset(uint8_t*, size_t, uint32_t) const { return false; } +}; + +class BadWriter : public zip_archive::Writer { + public: + BadWriter() : Writer() {} + + bool Append(uint8_t*, size_t) { return false; } +}; + +TEST(ziparchive, Inflate) { + const uint32_t compressed_length = kATxtContentsCompressed.size(); + const uint32_t uncompressed_length = kATxtContents.size(); + + const VectorReader reader(kATxtContentsCompressed); + { + VectorWriter writer; + uint64_t crc_out = 0; + + int32_t ret = + zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, &crc_out); + ASSERT_EQ(0, ret); + ASSERT_EQ(kATxtContents, writer.GetOutput()); + ASSERT_EQ(0x950821C5u, crc_out); + } + + { + VectorWriter writer; + int32_t ret = + zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr); + ASSERT_EQ(0, ret); + ASSERT_EQ(kATxtContents, writer.GetOutput()); + } + + { + BadWriter writer; + int32_t ret = + zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr); + ASSERT_EQ(kIoError, ret); + } + + { + BadReader reader; + VectorWriter writer; + int32_t ret = + zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr); + ASSERT_EQ(kIoError, ret); + ASSERT_EQ(0u, writer.GetOutput().size()); + } +} + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv);