mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-11-15 17:02:03 +00:00
Consume both GZip header CRC bytes and reject truncated fields
This commit is contained in:
@@ -108,8 +108,11 @@ internal sealed partial class GZipFilePart
|
||||
}
|
||||
if ((header[3] & 0x02) == 0x02)
|
||||
{
|
||||
var buf = new byte[1];
|
||||
_ = await _stream.ReadAsync(buf, 0, 1, cancellationToken).ConfigureAwait(false); // CRC16, ignore
|
||||
var headerCrc = new byte[2];
|
||||
if (!await _stream.ReadFullyAsync(headerCrc, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
throw new ZlibException("Unexpected end-of-file reading GZIP header CRC.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -133,7 +133,11 @@ internal sealed partial class GZipFilePart : FilePart
|
||||
}
|
||||
if ((header[3] & 0x02) == 0x02)
|
||||
{
|
||||
_stream.ReadByte(); // CRC16, ignore
|
||||
Span<byte> headerCrc = stackalloc byte[2];
|
||||
if (!_stream.ReadFully(headerCrc))
|
||||
{
|
||||
throw new ZlibException("Unexpected end-of-file reading GZIP header CRC.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -778,7 +778,12 @@ internal class ZlibBaseStream : Stream, IStreamStack
|
||||
}
|
||||
if ((header[3] & 0x02) == 0x02)
|
||||
{
|
||||
Read(_buf1, 0, 1); // CRC16, ignore
|
||||
Span<byte> headerCrc = stackalloc byte[2];
|
||||
if (!_stream.ReadFully(headerCrc))
|
||||
{
|
||||
throw new ZlibException("Unexpected end-of-file reading GZIP header CRC.");
|
||||
}
|
||||
totalBytesRead += 2;
|
||||
}
|
||||
|
||||
return totalBytesRead;
|
||||
@@ -848,7 +853,12 @@ internal class ZlibBaseStream : Stream, IStreamStack
|
||||
}
|
||||
if ((header[3] & 0x02) == 0x02)
|
||||
{
|
||||
await _stream.ReadAsync(_buf1, 0, 1, cancellationToken).ConfigureAwait(false); // CRC16, ignore
|
||||
var headerCrc = new byte[2];
|
||||
if (!await _stream.ReadFullyAsync(headerCrc, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
throw new ZlibException("Unexpected end-of-file reading GZIP header CRC.");
|
||||
}
|
||||
totalBytesRead += 2;
|
||||
}
|
||||
|
||||
return totalBytesRead;
|
||||
|
||||
@@ -21,6 +21,82 @@ public class GZipExtraFieldTests
|
||||
{
|
||||
private static readonly byte[] EXPECTED_PAYLOAD = [.. "gzip payload"u8];
|
||||
|
||||
[Theory]
|
||||
[InlineData("Stream", false)]
|
||||
[InlineData("Stream", true)]
|
||||
[InlineData("Archive", false)]
|
||||
[InlineData("Archive", true)]
|
||||
[InlineData("Reader", false)]
|
||||
[InlineData("Reader", true)]
|
||||
public async Task GZip_Rejects_Truncated_Header_Crc(string api, bool async)
|
||||
{
|
||||
byte[] bytes = [0x1F, 0x8B, 8, 2, 0, 0, 0, 0, 0, 3, 0];
|
||||
using var input = new ShortReadStream(new MemoryStream(bytes));
|
||||
await Assert.ThrowsAsync<ZlibException>(() =>
|
||||
ReadPayload(input, api, async, CancellationToken.None)
|
||||
);
|
||||
Assert.False(input.IsDisposed);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public async Task GZip_Header_Crc_Field_Is_Two_Bytes(bool async)
|
||||
{
|
||||
var original = CreateGZipWithExtraField(0, null);
|
||||
original[3] |= 2;
|
||||
var headerCrc = SharpCompress.Crypto.Crc32Stream.Compute(original.AsSpan(0, 12).ToArray());
|
||||
byte[] bytes =
|
||||
[
|
||||
.. original.AsSpan(0, 12),
|
||||
(byte)headerCrc,
|
||||
(byte)(headerCrc >> 8),
|
||||
.. original.AsSpan(12),
|
||||
];
|
||||
using var input = new ShortReadStream(new MemoryStream(bytes));
|
||||
using var output = new MemoryStream();
|
||||
if (async)
|
||||
{
|
||||
await using var gzip = new SharpCompress.Compressors.Deflate.GZipStream(
|
||||
input,
|
||||
SharpCompressionMode.Decompress,
|
||||
CompressionLevel.Default,
|
||||
Encoding.UTF8,
|
||||
leaveOpen: true
|
||||
);
|
||||
await gzip.CopyToAsync(output, 81920, CancellationToken.None);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var gzip = new SharpCompress.Compressors.Deflate.GZipStream(
|
||||
input,
|
||||
SharpCompressionMode.Decompress,
|
||||
CompressionLevel.Default,
|
||||
Encoding.UTF8,
|
||||
leaveOpen: true
|
||||
);
|
||||
gzip.CopyTo(output);
|
||||
}
|
||||
Assert.Equal(EXPECTED_PAYLOAD, output.ToArray());
|
||||
using var archiveInput = new ShortReadStream(new MemoryStream(bytes));
|
||||
if (async)
|
||||
{
|
||||
await using var archive = await GZipArchive.OpenAsyncArchive(archiveInput);
|
||||
var entry = await archive.EntriesAsync.FirstAsync();
|
||||
using var entryStream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
||||
output.SetLength(0);
|
||||
await entryStream.CopyToAsync(output, 81920, CancellationToken.None);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var archive = GZipArchive.OpenArchive(archiveInput);
|
||||
using var entryStream = archive.Entries.Single().OpenEntryStream();
|
||||
output.SetLength(0);
|
||||
entryStream.CopyTo(output);
|
||||
}
|
||||
Assert.Equal(EXPECTED_PAYLOAD, output.ToArray());
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> FragmentedExtraFields()
|
||||
{
|
||||
foreach (var api in new[] { "Stream", "Reader", "Archive" })
|
||||
|
||||
Reference in New Issue
Block a user