mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-22 23:15:20 +00:00
Fix decompression bomb and crash bugs (4 security fixes)
Agent-Logs-Url: https://github.com/adamhathcock/sharpcompress/sessions/7dbc048c-0fb5-46be-bf9a-b5b0b3c3f8bf Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
1e7369a6ee
commit
f7b8225422
@@ -151,7 +151,17 @@ internal partial class StreamingZipHeaderFactory : ZipHeaderFactory
|
||||
}
|
||||
else
|
||||
{
|
||||
headerBytes = reader.ReadUInt32();
|
||||
try
|
||||
{
|
||||
headerBytes = reader.ReadUInt32();
|
||||
}
|
||||
catch (EndOfStreamException ex)
|
||||
{
|
||||
throw new InvalidFormatException(
|
||||
"Unexpected end of stream while reading ZIP archive",
|
||||
ex
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_lastEntryHeader = null;
|
||||
|
||||
@@ -706,6 +706,10 @@ internal partial class CBZip2InputStream : Stream
|
||||
groupPos = BZip2Constants.G_SIZE;
|
||||
}
|
||||
groupPos--;
|
||||
if (groupNo < 0 || groupNo >= selector.Length)
|
||||
{
|
||||
throw new InvalidFormatException("BZip2: group selector out of range");
|
||||
}
|
||||
zt = selector[groupNo];
|
||||
zn = minLens[zt];
|
||||
zvec = BsR(zn);
|
||||
@@ -788,6 +792,10 @@ internal partial class CBZip2InputStream : Stream
|
||||
groupPos = BZip2Constants.G_SIZE;
|
||||
}
|
||||
groupPos--;
|
||||
if (groupNo < 0 || groupNo >= selector.Length)
|
||||
{
|
||||
throw new InvalidFormatException("BZip2: group selector out of range");
|
||||
}
|
||||
zt = selector[groupNo];
|
||||
zn = minLens[zt];
|
||||
zvec = BsR(zn);
|
||||
@@ -862,6 +870,10 @@ internal partial class CBZip2InputStream : Stream
|
||||
BlockOverrun();
|
||||
}
|
||||
|
||||
if (nextSym - 1 < 0 || nextSym - 1 >= yy.Length)
|
||||
{
|
||||
throw new InvalidFormatException("BZip2: symbol out of range");
|
||||
}
|
||||
tmp = yy[nextSym - 1];
|
||||
unzftab[seqToUnseq[tmp]]++;
|
||||
ll8[last] = seqToUnseq[tmp];
|
||||
@@ -898,6 +910,10 @@ internal partial class CBZip2InputStream : Stream
|
||||
groupPos = BZip2Constants.G_SIZE;
|
||||
}
|
||||
groupPos--;
|
||||
if (groupNo < 0 || groupNo >= selector.Length)
|
||||
{
|
||||
throw new InvalidFormatException("BZip2: group selector out of range");
|
||||
}
|
||||
zt = selector[groupNo];
|
||||
zn = minLens[zt];
|
||||
zvec = BsR(zn);
|
||||
|
||||
@@ -244,6 +244,10 @@ internal sealed class HuffmanTree
|
||||
overflowBits--;
|
||||
} while (overflowBits != 0);
|
||||
|
||||
if (index < 0 || index >= array.Length)
|
||||
{
|
||||
throw new InvalidFormatException("Deflate64: invalid Huffman data");
|
||||
}
|
||||
array[index] = (short)ch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ public partial class ReduceStream
|
||||
{
|
||||
if (inByteCount == compressedSize)
|
||||
{
|
||||
_inputExhausted = true;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
@@ -35,6 +36,7 @@ public partial class ReduceStream
|
||||
.ConfigureAwait(false);
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
_inputExhausted = true;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
@@ -117,6 +119,13 @@ public partial class ReduceStream
|
||||
{
|
||||
if (length == 0)
|
||||
{
|
||||
if (_inputExhausted && bitBufferCount <= 0)
|
||||
{
|
||||
throw new InvalidFormatException(
|
||||
"ReduceStream: compressed data exhausted before uncompressed size reached"
|
||||
);
|
||||
}
|
||||
|
||||
byte nextByte = await GetNextByteAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (nextByte != RunLengthCode)
|
||||
{
|
||||
|
||||
@@ -115,16 +115,24 @@ public partial class ReduceStream : Stream
|
||||
|
||||
private int bitBufferCount;
|
||||
private ulong bitBuffer;
|
||||
private bool _inputExhausted;
|
||||
|
||||
private int NEXTBYTE()
|
||||
{
|
||||
if (inByteCount == compressedSize)
|
||||
{
|
||||
_inputExhausted = true;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
inByteCount++;
|
||||
return inStream.ReadByte();
|
||||
int b = inStream.ReadByte();
|
||||
if (b < 0)
|
||||
{
|
||||
_inputExhausted = true;
|
||||
return EOF;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
private void READBITS(int nbits, out byte zdest)
|
||||
@@ -208,6 +216,13 @@ public partial class ReduceStream : Stream
|
||||
{
|
||||
if (length == 0)
|
||||
{
|
||||
if (_inputExhausted && bitBufferCount <= 0)
|
||||
{
|
||||
throw new InvalidFormatException(
|
||||
"ReduceStream: compressed data exhausted before uncompressed size reached"
|
||||
);
|
||||
}
|
||||
|
||||
byte nextByte = GetNextByte();
|
||||
if (nextByte != RunLengthCode)
|
||||
{
|
||||
|
||||
@@ -33,29 +33,13 @@ internal partial class ShrinkStream : Stream
|
||||
return;
|
||||
}
|
||||
|
||||
// Read all compressed data asynchronously
|
||||
var src = new byte[_compressedSize];
|
||||
int bytesRead = 0;
|
||||
int totalBytesRead = 0;
|
||||
|
||||
while (totalBytesRead < (int)_compressedSize)
|
||||
{
|
||||
bytesRead = await inStream
|
||||
.ReadAsync(
|
||||
src,
|
||||
totalBytesRead,
|
||||
(int)_compressedSize - totalBytesRead,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
throw new IncompleteArchiveException(
|
||||
"Unexpected end of stream while reading compressed data"
|
||||
);
|
||||
}
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
// Read actual compressed data from stream rather than pre-allocating based on the
|
||||
// declared compressed size, which may be crafted to cause an OutOfMemoryException.
|
||||
// The stream is already bounded by ReadOnlySubStream in ZipFilePart.
|
||||
using var srcMs = new MemoryStream();
|
||||
await inStream.CopyToAsync(srcMs, cancellationToken).ConfigureAwait(false);
|
||||
var src = srcMs.ToArray();
|
||||
var srcLen = src.Length;
|
||||
|
||||
// Decompress synchronously (CPU-bound operation)
|
||||
var srcUsed = 0;
|
||||
@@ -63,7 +47,7 @@ internal partial class ShrinkStream : Stream
|
||||
|
||||
HwUnshrink.Unshrink(
|
||||
src,
|
||||
(int)_compressedSize,
|
||||
srcLen,
|
||||
out srcUsed,
|
||||
_byteOut,
|
||||
(int)_uncompressedSize,
|
||||
|
||||
@@ -8,7 +8,6 @@ internal partial class ShrinkStream : Stream
|
||||
{
|
||||
private Stream inStream;
|
||||
|
||||
private ulong _compressedSize;
|
||||
private long _uncompressedSize;
|
||||
private byte[] _byteOut;
|
||||
private long _outBytesCount;
|
||||
@@ -24,7 +23,6 @@ internal partial class ShrinkStream : Stream
|
||||
{
|
||||
inStream = stream;
|
||||
|
||||
_compressedSize = (ulong)compressedSize;
|
||||
_uncompressedSize = uncompressedSize;
|
||||
_byteOut = new byte[_uncompressedSize];
|
||||
_outBytesCount = 0L;
|
||||
@@ -55,14 +53,19 @@ internal partial class ShrinkStream : Stream
|
||||
{
|
||||
if (!_decompressed)
|
||||
{
|
||||
var src = new byte[_compressedSize];
|
||||
inStream.ReadExact(src, 0, (int)_compressedSize);
|
||||
// Read actual compressed data from stream rather than pre-allocating based on the
|
||||
// declared compressed size, which may be crafted to cause an OutOfMemoryException.
|
||||
// The stream is already bounded by ReadOnlySubStream in ZipFilePart.
|
||||
using var srcMs = new MemoryStream();
|
||||
inStream.CopyTo(srcMs);
|
||||
var src = srcMs.ToArray();
|
||||
var srcLen = src.Length;
|
||||
var srcUsed = 0;
|
||||
var dstUsed = 0;
|
||||
|
||||
HwUnshrink.Unshrink(
|
||||
src,
|
||||
(int)_compressedSize,
|
||||
srcLen,
|
||||
out srcUsed,
|
||||
_byteOut,
|
||||
(int)_uncompressedSize,
|
||||
|
||||
@@ -268,9 +268,9 @@
|
||||
"net10.0": {
|
||||
"Microsoft.NET.ILLink.Tasks": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.5, )",
|
||||
"resolved": "10.0.5",
|
||||
"contentHash": "A+5ZuQ0f449tM+MQrhf6R9ZX7lYpjk/ODEwLYKrnF6111rtARx8fVsm4YznUnQiKnnXfaXNBqgxmil6RW3L3SA=="
|
||||
"requested": "[10.0.0, )",
|
||||
"resolved": "10.0.0",
|
||||
"contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
@@ -442,9 +442,9 @@
|
||||
"net8.0": {
|
||||
"Microsoft.NET.ILLink.Tasks": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.25, )",
|
||||
"resolved": "8.0.25",
|
||||
"contentHash": "sqX4nmBft05ivqKvUT4nxaN8rT3apCLt9SWFkfRrQPwra1zPwFknQAw1lleuMCKOCLvVmOWwrC2iPSm9RiXZUg=="
|
||||
"requested": "[8.0.22, )",
|
||||
"resolved": "8.0.22",
|
||||
"contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
|
||||
@@ -130,5 +130,50 @@ public class MalformedInputTests
|
||||
"504b03040a0200000e001c0068646c6c6f2e7478745554ac507578000000000000000000000000000000000000000000e80300000000000068030a0000000000147f040020303a360600002e7478745554090003a8c8b6696045ac69f5780b0006ff1d000908180000e8030000000000a4810000109a9a9a8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b9a0000000000000000000000e80300000000000068030a0000009a9a9a504b03440a6fcb486c6c6f2e74ffff"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Reduce_DecompressionBomb_Method2_ThrowsLibraryException()
|
||||
{
|
||||
// 31-byte ZIP using Reduce method 2 with declared uncompressed size far exceeding the
|
||||
// actual compressed data - the decompressor must not generate unbounded output.
|
||||
VerifyMalformedInputThrowsLibraryException(
|
||||
"504b03040a000000020000000200f7ff0500f7ff05ff200600180700000000"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deflate64_HuffmanTree_IndexOutOfRange_ThrowsLibraryException()
|
||||
{
|
||||
// 105-byte ZIP using Deflate64 with invalid Huffman code lengths causing IOOB in CreateTable
|
||||
VerifyMalformedInputThrowsLibraryException(
|
||||
"504b03040a00005409000088c8b669757800009ac8b66975783606000000640028b52ffd047fff"
|
||||
+ "02009a888888888820313735303600303132002030007573746172202000757001307230819b75"
|
||||
+ "72756e7475410a000c2000391eeb061ffe391eeb068f0c0a000c20"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BZip2_GetAndMoveToFrontDecode_IndexOutOfRange_ThrowsLibraryException()
|
||||
{
|
||||
// 93-byte BZip2 stream triggering IOOB deeper in GetAndMoveToFrontDecode
|
||||
VerifyMalformedInputThrowsLibraryException(
|
||||
"425a6839314159265359c1c080e2000001410000100244a00100808b640006000775780b2ef2ed"
|
||||
+ "0001393beb06060606060606060606f9050605060606060f0654090003ffffff7f003403"
|
||||
+ "0a0002001f8b7fff0000000000e98b8b3931"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Zip_ShrinkOOM_CraftedCompressedSize_ThrowsLibraryException()
|
||||
{
|
||||
// 122-byte ZIP with Shrink compression and compressed size set to 0x7FFFFFFF (2 GB).
|
||||
// The library must not attempt to allocate a 2 GB buffer based on the untrusted header.
|
||||
VerifyMalformedInputThrowsLibraryException(
|
||||
"504b03040a0000000100147f6f5c20303a36ffffff7f0600000009001c0068656c6c6f2e747874"
|
||||
+ "5554090003a8c8b6696045ac6975780b01e8303a36060000000600000009001800000001004f2a"
|
||||
+ "2a2a2a0c2000395d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d000004e8303a360600000006000000"
|
||||
+ "0900180000"
|
||||
);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user