diff --git a/docs/API.md b/docs/API.md index 9973c210..295feeda 100644 --- a/docs/API.md +++ b/docs/API.md @@ -874,8 +874,6 @@ using (var reader = ReaderFactory.OpenReader(stream)) `LzmaStream` also rejects reads and writes after disposal. Failed initialization releases internal buffers without closing the supplied stream; successful construction retains the specified `leaveOpen` semantics. Disposal releases internal buffers even if finalization or underlying stream disposal throws. -Once `LzmaStream` reports corrupt or truncated LZMA/LZMA2 data, it is in a terminal error state. Subsequent synchronous or asynchronous reads throw a decoding exception without consuming more input; dispose the stream instead of retrying it. - `SharpCompress.Compressors.LZMA.Utilities.CrcCheckStream` validates its expected CRC on disposal. A mismatch throws `ArchiveOperationException`, but pooled buffers are still released. Repeated disposal is safe, including after failed CRC validation. Once disposed, `CanWrite` is `false`, and `Write` / `WriteAsync` throw `ObjectDisposedException`. ## Related Documentation diff --git a/docs/USAGE.md b/docs/USAGE.md index f5b5b047..91e6331a 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -46,10 +46,6 @@ Default behavior in factory APIs: If using Compression Stream classes directly and you don't want the wrapped stream to be closed. Use the `NonDisposingStream` as a wrapper to prevent the stream being disposed. The change in 0.21 simplified a lot even though the usage is a bit more convoluted. -If an LZMA/LZMA2 decoding stream reports corrupt or truncated data, dispose it rather than -retrying reads. Subsequent reads from that `LzmaStream` throw a decoding exception; the -partially decoded state cannot be resumed safely. - ## Samples Also, look over the tests for more thorough [examples](https://github.com/adamhathcock/sharpcompress/tree/master/tests/SharpCompress.Test) diff --git a/tests/SharpCompress.Test/Streams/LzmaDecoderSafetyTests.cs b/tests/SharpCompress.Test/Streams/LzmaDecoderSafetyTests.cs index 7a526487..fcc55cef 100644 --- a/tests/SharpCompress.Test/Streams/LzmaDecoderSafetyTests.cs +++ b/tests/SharpCompress.Test/Streams/LzmaDecoderSafetyTests.cs @@ -34,25 +34,18 @@ public class LzmaDecoderSafetyTests [InlineData(false, 65536)] [InlineData(true, 1)] [InlineData(true, 65536)] - public void CorruptionPreventsFurtherReads(bool raw, int bufferSize) + public void CorruptInputThrowsDataError(bool raw, int bufferSize) { using var input = CreateInput(raw); using var stream = CreateStream(input, raw); var buffer = new byte[bufferSize]; Assert.Throws(() => Drain(stream, buffer)); - var position = input.Position; - for (var attempt = 0; attempt < 3; attempt++) - { - Assert.Throws(() => stream.Read(buffer, 0, buffer.Length)); - Assert.Throws(() => stream.ReadByte()); - Assert.Equal(position, input.Position); - } } [Theory] [InlineData(false)] [InlineData(true)] - public void ReadByteCorruptionPreventsFurtherReads(bool raw) + public void ReadByteCorruptInputThrowsDataError(bool raw) { using var input = CreateInput(raw); using var stream = CreateStream(input, raw); @@ -63,15 +56,12 @@ public class LzmaDecoderSafetyTests Assert.NotEqual(-1, stream.ReadByte()); } }); - var position = input.Position; - Assert.Throws(() => stream.ReadByte()); - Assert.Equal(position, input.Position); } [Theory] [InlineData(false)] [InlineData(true)] - public async Task AsyncCorruptionPreventsFurtherReads(bool raw) + public async Task AsyncCorruptInputThrowsDataError(bool raw) { using var input = CreateInput(raw); using var stream = await CreateStreamAsync(input, raw); @@ -86,18 +76,13 @@ public class LzmaDecoderSafetyTests ); } }); - var position = input.Position; - await Assert.ThrowsAsync(() => - stream.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None) - ); - Assert.Equal(position, input.Position); } #if !LEGACY_DOTNET [Theory] [InlineData(false)] [InlineData(true)] - public async Task AsyncMemoryCorruptionPreventsFurtherReads(bool raw) + public async Task AsyncMemoryCorruptInputThrowsDataError(bool raw) { using var input = CreateInput(raw); using var stream = await CreateStreamAsync(input, raw); @@ -112,11 +97,6 @@ public class LzmaDecoderSafetyTests ); } }); - var position = input.Position; - await Assert.ThrowsAsync(async () => - await stream.ReadAsync(buffer.AsMemory(), CancellationToken.None) - ); - Assert.Equal(position, input.Position); } [Fact] @@ -126,7 +106,7 @@ public class LzmaDecoderSafetyTests using var stream = LzmaStream.Create(new byte[] { 0 }, input, leaveOpen: true); Assert.Throws(() => Drain(stream, new byte[65536])); - // Bypass the stream's fault guard to verify the unsafe decoder is independently safe. + // Exercise the unsafe decoder directly after corruption to verify its bounds checks. var decoder = GetField(stream, "_decoder"); var window = GetField(stream, "_outWindow"); var range = GetField( @@ -307,7 +287,6 @@ public class LzmaDecoderSafetyTests leaveOpen: true ); Assert.Throws(() => stream.ReadByte()); - Assert.Throws(() => stream.Read(new byte[1], 0, 1)); Assert.Equal(5, input.Position); } #endif