Handle partial LZip headers and validate dictionary sizes consistently

This commit is contained in:
Adam Hathcock
2026-10-10 15:40:35 +01:00
parent 17f26064f9
commit d01539ae3c
3 changed files with 45 additions and 20 deletions

View File

@@ -129,11 +129,7 @@ public sealed partial class LZipStream
var header = ArrayPool<byte>.Shared.Rent(6);
try
{
var n = await stream.ReadAsync(header, 0, 6, cancellationToken).ConfigureAwait(false);
// TODO: Handle reading only part of the header?
if (n != 6)
if (!await stream.ReadFullyAsync(header, 0, 6, cancellationToken).ConfigureAwait(false))
{
return 0;
}
@@ -148,13 +144,7 @@ public sealed partial class LZipStream
{
return 0;
}
var basePower = header[5] & 0x1F;
var subtractionNumerator = (header[5] & 0xE0) >> 5;
if (basePower < 4 || basePower > 30)
{
return 0;
}
return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4)));
return DecodeDictionarySize(header[5]);
}
finally
{

View File

@@ -253,11 +253,7 @@ public sealed partial class LZipStream : Stream, IFinishable
{
// Read the header
Span<byte> header = stackalloc byte[6];
var n = stream.Read(header);
// TODO: Handle reading only part of the header?
if (n != 6)
if (!stream.ReadFully(header))
{
return 0;
}
@@ -272,9 +268,18 @@ public sealed partial class LZipStream : Stream, IFinishable
{
return 0;
}
var basePower = header[5] & 0x1F;
var subtractionNumerator = (header[5] & 0xE0) >> 5;
return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4)));
return DecodeDictionarySize(header[5]);
}
private static int DecodeDictionarySize(byte encoded)
{
var basePower = encoded & 0x1F;
if (basePower < 12 || basePower > 29)
{
return 0;
}
var size = (1 << basePower) - ((encoded >> 5) * (1 << (basePower - 4)));
return size >= 4096 ? size : 0;
}
// Async methods moved to LZipStream.Async.cs

View File

@@ -0,0 +1,30 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Compressors.LZMA;
using SharpCompress.Test.Mocks;
using Xunit;
namespace SharpCompress.Test.Streams;
public class LZipHeaderReadTests
{
[Theory]
[InlineData(12, 4096)]
[InlineData(29, 536870912)]
[InlineData(11, 0)]
[InlineData(30, 0)]
[InlineData(31, 0)]
[InlineData(44, 0)]
public async Task LZip_Header_Fragmentation_And_Dictionary_Validation(int encoded, int expected)
{
byte[] header = [0x4C, 0x5A, 0x49, 0x50, 1, (byte)encoded];
using var sync = new ShortReadStream(new MemoryStream(header));
using var asyncInput = new ShortReadStream(new MemoryStream(header));
Assert.Equal(expected, LZipStream.ValidateAndReadSize(sync));
Assert.Equal(
expected,
await LZipStream.ValidateAndReadSizeAsync(asyncInput, CancellationToken.None)
);
}
}