mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-04 05:25:00 +00:00
Merge pull request #1024 from adamhathcock/copilot/fix-memory-exhaustion-bug
Fix memory exhaustion in TAR header auto-detection
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -11,6 +11,8 @@ TestResults/
|
||||
packages/*/
|
||||
project.lock.json
|
||||
tests/TestArchives/Scratch
|
||||
tests/TestArchives/*/Scratch
|
||||
tests/TestArchives/*/Scratch2
|
||||
.vs
|
||||
tools
|
||||
.vscode
|
||||
@@ -18,4 +20,3 @@ tools
|
||||
|
||||
.DS_Store
|
||||
*.snupkg
|
||||
/tests/TestArchives/6d23a38c-f064-4ef1-ad89-b942396f53b9/Scratch
|
||||
|
||||
@@ -25,6 +25,10 @@ internal sealed class TarHeader
|
||||
|
||||
internal const int BLOCK_SIZE = 512;
|
||||
|
||||
// Maximum size for long name/link headers to prevent memory exhaustion attacks
|
||||
// This is generous enough for most real-world scenarios (32KB)
|
||||
private const int MAX_LONG_NAME_SIZE = 32768;
|
||||
|
||||
internal void Write(Stream output)
|
||||
{
|
||||
var buffer = new byte[BLOCK_SIZE];
|
||||
@@ -186,6 +190,15 @@ internal sealed class TarHeader
|
||||
private string ReadLongName(BinaryReader reader, byte[] buffer)
|
||||
{
|
||||
var size = ReadSize(buffer);
|
||||
|
||||
// Validate size to prevent memory exhaustion from malformed headers
|
||||
if (size < 0 || size > MAX_LONG_NAME_SIZE)
|
||||
{
|
||||
throw new InvalidFormatException(
|
||||
$"Long name size {size} is invalid or exceeds maximum allowed size of {MAX_LONG_NAME_SIZE} bytes"
|
||||
);
|
||||
}
|
||||
|
||||
var nameLength = (int)size;
|
||||
var nameBytes = reader.ReadBytes(nameLength);
|
||||
var remainingBytesToRead = BLOCK_SIZE - (nameLength % BLOCK_SIZE);
|
||||
|
||||
@@ -254,4 +254,58 @@ public class TarReaderTests : ReaderTests
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void Tar_Malformed_LongName_Excessive_Size()
|
||||
{
|
||||
// Create a malformed TAR header with an excessively large LongName size
|
||||
// This simulates what happens during auto-detection of compressed files
|
||||
var buffer = new byte[512];
|
||||
|
||||
// Set up a basic TAR header structure
|
||||
// Name field (offset 0, 100 bytes) - set to "././@LongLink" which is typical for LongName
|
||||
var nameBytes = System.Text.Encoding.ASCII.GetBytes("././@LongLink");
|
||||
Array.Copy(nameBytes, 0, buffer, 0, nameBytes.Length);
|
||||
|
||||
// Set entry type to LongName (offset 156)
|
||||
buffer[156] = (byte)'L'; // EntryType.LongName
|
||||
|
||||
// Set an excessively large size (offset 124, 12 bytes, octal format)
|
||||
// This simulates a corrupted/misinterpreted size field
|
||||
// Using "77777777777" (octal) = 8589934591 bytes (~8GB)
|
||||
var sizeBytes = System.Text.Encoding.ASCII.GetBytes("77777777777 ");
|
||||
Array.Copy(sizeBytes, 0, buffer, 124, sizeBytes.Length);
|
||||
|
||||
// Calculate and set checksum (offset 148, 8 bytes)
|
||||
// Set checksum field to spaces first
|
||||
for (var i = 148; i < 156; i++)
|
||||
{
|
||||
buffer[i] = (byte)' ';
|
||||
}
|
||||
|
||||
// Calculate checksum
|
||||
var checksum = 0;
|
||||
foreach (var b in buffer)
|
||||
{
|
||||
checksum += b;
|
||||
}
|
||||
|
||||
var checksumStr = Convert.ToString(checksum, 8).PadLeft(6, '0') + "\0 ";
|
||||
var checksumBytes = System.Text.Encoding.ASCII.GetBytes(checksumStr);
|
||||
Array.Copy(checksumBytes, 0, buffer, 148, checksumBytes.Length);
|
||||
|
||||
// Create a stream with this malformed header
|
||||
using var stream = new MemoryStream();
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
stream.Position = 0;
|
||||
|
||||
// Attempt to read this malformed archive
|
||||
// The InvalidFormatException from the validation gets caught and converted to IncompleteArchiveException
|
||||
// The important thing is it doesn't cause OutOfMemoryException
|
||||
Assert.Throws<IncompleteArchiveException>(() =>
|
||||
{
|
||||
using var reader = TarReader.Open(stream);
|
||||
reader.MoveToNextEntry();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user