diff --git a/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs b/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs index 949fbc97..36929d8a 100644 --- a/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs +++ b/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs @@ -129,11 +129,7 @@ public sealed partial class LZipStream var header = ArrayPool.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 { diff --git a/src/SharpCompress/Compressors/LZMA/LZipStream.cs b/src/SharpCompress/Compressors/LZMA/LZipStream.cs index f2e1abea..7379639d 100644 --- a/src/SharpCompress/Compressors/LZMA/LZipStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LZipStream.cs @@ -253,11 +253,7 @@ public sealed partial class LZipStream : Stream, IFinishable { // Read the header Span 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 diff --git a/tests/SharpCompress.Test/Streams/LZipHeaderReadTests.cs b/tests/SharpCompress.Test/Streams/LZipHeaderReadTests.cs new file mode 100644 index 00000000..7d45b6c9 --- /dev/null +++ b/tests/SharpCompress.Test/Streams/LZipHeaderReadTests.cs @@ -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) + ); + } +}