From b047d6860a5f11c2f97d43826844ff8b97c584e1 Mon Sep 17 00:00:00 2001 From: Vadim Syrov Date: Tue, 6 Oct 2026 14:09:08 +0300 Subject: [PATCH] Fix gzip FEXTRA XLEN overflow --- .../Compressors/Deflate/ZlibBaseStream.cs | 4 +- .../GZip/GZipExtraFieldTests.cs | 78 +++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 tests/SharpCompress.Test/GZip/GZipExtraFieldTests.cs diff --git a/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs b/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs index c7ea2a6f..d39eb178 100644 --- a/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs +++ b/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs @@ -755,7 +755,7 @@ internal class ZlibBaseStream : Stream, IStreamStack n = _stream.Read(header.Slice(0, 2)); // 2-byte length field totalBytesRead += n; - var extraLength = (short)(header[0] + header[1] * 256); + var extraLength = header[0] + (header[1] * 256); var extra = new byte[extraLength]; n = _stream.Read(extra, 0, extra.Length); if (n != extraLength) @@ -815,7 +815,7 @@ internal class ZlibBaseStream : Stream, IStreamStack n = await _stream.ReadAsync(header, 0, 2, cancellationToken).ConfigureAwait(false); // 2-byte length field totalBytesRead += n; - var extraLength = (short)(header[0] + header[1] * 256); + var extraLength = header[0] + (header[1] * 256); var extra = new byte[extraLength]; n = await _stream .ReadAsync(extra, 0, extra.Length, cancellationToken) diff --git a/tests/SharpCompress.Test/GZip/GZipExtraFieldTests.cs b/tests/SharpCompress.Test/GZip/GZipExtraFieldTests.cs new file mode 100644 index 00000000..5681af50 --- /dev/null +++ b/tests/SharpCompress.Test/GZip/GZipExtraFieldTests.cs @@ -0,0 +1,78 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using AwesomeAssertions; +using SharpCompress.Test.Mocks; +using Xunit; +using SharpCompressionMode = SharpCompress.Compressors.CompressionMode; +using SystemCompressionMode = System.IO.Compression.CompressionMode; + +namespace SharpCompress.Test.GZip; + +public class GZipExtraFieldTests +{ + private static readonly byte[] EXPECTED_PAYLOAD = [.. "gzip payload"u8]; + + [Theory] + [InlineData(32767)] + [InlineData(32768)] + [InlineData(65535)] + public void GZipStream_Read_WithLargeExtraField_ShouldReturnPayload(int extraLength) + { + using var input = new MemoryStream(CreateGZipWithExtraField(extraLength)); + using var gzip = new SharpCompress.Compressors.Deflate.GZipStream( + input, + SharpCompressionMode.Decompress + ); + using var output = new MemoryStream(); + + gzip.CopyTo(output); + + _ = output.ToArray().Should().Equal(EXPECTED_PAYLOAD); + } + + [Theory] + [InlineData(32767)] + [InlineData(32768)] + [InlineData(65535)] + public async ValueTask GZipStream_ReadAsync_WithLargeExtraField_ShouldReturnPayload( + int extraLength + ) + { + using var rawInput = new MemoryStream(CreateGZipWithExtraField(extraLength)); + await using var input = new AsyncOnlyStream(rawInput, disposeStream: false); + await using var gzip = new SharpCompress.Compressors.Deflate.GZipStream( + input, + SharpCompressionMode.Decompress + ); + using var output = new MemoryStream(); + + await gzip.CopyToAsync(output); + + _ = output.ToArray().Should().Equal(EXPECTED_PAYLOAD); + } + + private static byte[] CreateGZipWithExtraField(int extraLength) + { + using var compressed = new MemoryStream(); + using ( + var gzip = new System.IO.Compression.GZipStream( + compressed, + SystemCompressionMode.Compress, + leaveOpen: true + ) + ) + { + gzip.Write(EXPECTED_PAYLOAD, 0, EXPECTED_PAYLOAD.Length); + } + + var original = compressed.ToArray(); + var result = new byte[original.Length + extraLength + 2]; + Buffer.BlockCopy(original, 0, result, 0, 10); + result[3] |= 0x04; + result[10] = (byte)extraLength; + result[11] = (byte)(extraLength >> 8); + Buffer.BlockCopy(original, 10, result, 12 + extraLength, original.Length - 10); + return result; + } +}