From a35ce40d7784b408ac0c4108e77bf897bd3df90a Mon Sep 17 00:00:00 2001 From: Adam Reeve Date: Mon, 16 Apr 2018 14:41:00 +0100 Subject: [PATCH 1/2] Fix flushing a GZipOutputStream When flushing a GZipOutputStream, ensure we deflate all data in the input buffer and write it to the underlying stream before we flush the underlying stream. --- .../GZip/GzipInputStream.cs | 4 +- .../Streams/DeflaterOutputStream.cs | 10 +++- .../GZip/GZipTests.cs | 55 +++++++++++++++++++ 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs b/src/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs index 06d602681..b672930f8 100644 --- a/src/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs +++ b/src/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs @@ -102,7 +102,6 @@ public override int Read(byte[] buffer, int offset, int count) // A compressed block could potentially be empty, so we need to loop until we reach EOF or // we find data. while (true) { - // If we haven't read the header for this block, read it if (!readGZIPHeader) { @@ -133,6 +132,9 @@ public override int Read(byte[] buffer, int offset, int count) // If this is the end of stream, read the footer if (inf.IsFinished) { ReadFooter(); + } else if (inf.RemainingInput == 0) { + // If the stream is not finished but we have no more data to read, don't keep looping forever + throw new GZipException("Unexpected EOF"); } if (bytesRead > 0) { diff --git a/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs b/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs index ad49c3f63..f0ba509b8 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs @@ -224,7 +224,13 @@ protected void InitializeAESPassword(ZipEntry entry, string rawPassword, /// protected void Deflate() { - while (!deflater_.IsNeedingInput) { + Deflate(false); + } + + private void Deflate(bool flushing) + { + while (flushing || !deflater_.IsNeedingInput) + { int deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length); if (deflateCount <= 0) { @@ -346,7 +352,7 @@ public override int Read(byte[] buffer, int offset, int count) public override void Flush() { deflater_.Flush(); - Deflate(); + Deflate(true); baseOutputStream_.Flush(); } diff --git a/test/ICSharpCode.SharpZipLib.Tests/GZip/GZipTests.cs b/test/ICSharpCode.SharpZipLib.Tests/GZip/GZipTests.cs index c5c97fc6f..e0271c11f 100644 --- a/test/ICSharpCode.SharpZipLib.Tests/GZip/GZipTests.cs +++ b/test/ICSharpCode.SharpZipLib.Tests/GZip/GZipTests.cs @@ -278,6 +278,61 @@ public void TrailingGarbage() } } + /// + /// Test that if we flush a GZip output stream then all data that has been written + /// is flushed through to the underlying stream and can be successfully read back + /// even if the stream is not yet finished. + /// + [Test] + [Category("GZip")] + public void FlushToUnderlyingStream() + { + var ms = new MemoryStream(); + var outStream = new GZipOutputStream(ms); + + byte[] buf = new byte[100000]; + var rnd = new Random(); + rnd.NextBytes(buf); + + outStream.Write(buf, 0, buf.Length); + // Flush output stream but don't finish it yet + outStream.Flush(); + + ms.Seek(0, SeekOrigin.Begin); + + var inStream = new GZipInputStream(ms); + byte[] buf2 = new byte[buf.Length]; + int currentIndex = 0; + int count = buf2.Length; + + while (true) + { + try + { + int numRead = inStream.Read(buf2, currentIndex, count); + if (numRead <= 0) + { + break; + } + currentIndex += numRead; + count -= numRead; + } + catch (GZipException) + { + // We should get an unexpected EOF exception once we've read all + // data as the stream isn't yet finished. + break; + } + } + + Assert.AreEqual(0, count); + + for (int i = 0; i < buf.Length; ++i) + { + Assert.AreEqual(buf2[i], buf[i]); + } + } + [Test] [Category("GZip")] [Category("Long Running")] From f9ba6e94aa0548cb35d61312337f86829cf30d83 Mon Sep 17 00:00:00 2001 From: Adam Reeve Date: Mon, 16 Apr 2018 14:56:44 +0100 Subject: [PATCH 2/2] Fix infinite loop when flushing DeflaterOutputStream with no compression DeflaterEngine.DeflateStored would always write more output even if there was no more input data to write, resulting in an infinite loop. --- src/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterEngine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterEngine.cs b/src/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterEngine.cs index b8eedc3d0..2f8f4664d 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterEngine.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterEngine.cs @@ -574,7 +574,7 @@ bool DeflateStored(bool flush, bool finish) huffman.FlushStoredBlock(window, blockStart, storedLength, lastBlock); blockStart += storedLength; - return !lastBlock; + return !(lastBlock || storedLength == 0); } return true; }