mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-11-15 17:02:03 +00:00
Remove unsupported LZMA terminal-error guarantees
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<DataErrorException>(() => Drain(stream, buffer));
|
||||
var position = input.Position;
|
||||
for (var attempt = 0; attempt < 3; attempt++)
|
||||
{
|
||||
Assert.Throws<DataErrorException>(() => stream.Read(buffer, 0, buffer.Length));
|
||||
Assert.Throws<DataErrorException>(() => 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<DataErrorException>(() => 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<DataErrorException>(() =>
|
||||
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<DataErrorException>(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<DataErrorException>(() => 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<Decoder>(stream, "_decoder");
|
||||
var window = GetField<OutWindow>(stream, "_outWindow");
|
||||
var range = GetField<SharpCompress.Compressors.LZMA.RangeCoder.Decoder>(
|
||||
@@ -307,7 +287,6 @@ public class LzmaDecoderSafetyTests
|
||||
leaveOpen: true
|
||||
);
|
||||
Assert.Throws<DataErrorException>(() => stream.ReadByte());
|
||||
Assert.Throws<DataErrorException>(() => stream.Read(new byte[1], 0, 1));
|
||||
Assert.Equal(5, input.Position);
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user