mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-11-15 17:02:03 +00:00
Report invalid LZMA2 chunk properties as a data error
An LZMA2 chunk header properties byte of 225 or more cannot occur in valid data, but it reached Decoder.SetDecoderProperties and surfaced as InvalidParamException. EncryptedDecoderStream only maps DataErrorException to CryptographicException, so a wrong 7z password occasionally leaked the internal codec exception, making SevenZipWriter_Encrypted_WrongPasswordFails flaky. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -146,11 +146,8 @@ public partial class LzmaStream
|
||||
await _inputStream!
|
||||
.ReadExactAsync(headerBuffer, 0, 1, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
Properties[0] = headerBuffer[0];
|
||||
_inputPosition++;
|
||||
|
||||
_decoder ??= new Decoder();
|
||||
_decoder.SetDecoderProperties(Properties);
|
||||
SetChunkProperties(headerBuffer[0]);
|
||||
}
|
||||
else if (_needProps)
|
||||
{
|
||||
|
||||
@@ -547,6 +547,21 @@ public partial class LzmaStream : Stream, IStreamStack, IAsyncDisposable
|
||||
return value;
|
||||
}
|
||||
|
||||
private void SetChunkProperties(byte properties)
|
||||
{
|
||||
// A properties byte encodes (pb * 5 + lp) * 9 + lc with pb <= 4; a larger value is
|
||||
// corrupt stream data, not an invalid caller argument.
|
||||
if (properties >= 9 * 5 * 5)
|
||||
{
|
||||
throw new DataErrorException();
|
||||
}
|
||||
Properties[0] = properties;
|
||||
|
||||
// Properties initialization resets every probability and match-state field.
|
||||
_decoder ??= new Decoder();
|
||||
_decoder.SetDecoderProperties(Properties);
|
||||
}
|
||||
|
||||
private void DecodeChunkHeader()
|
||||
{
|
||||
var control = ReadChunkHeaderByte();
|
||||
@@ -585,11 +600,7 @@ public partial class LzmaStream : Stream, IStreamStack, IAsyncDisposable
|
||||
if (control >= 0xC0)
|
||||
{
|
||||
_needProps = false;
|
||||
Properties[0] = (byte)ReadChunkHeaderByte();
|
||||
|
||||
// Properties initialization resets every probability and match-state field.
|
||||
_decoder ??= new Decoder();
|
||||
_decoder.SetDecoderProperties(Properties);
|
||||
SetChunkProperties((byte)ReadChunkHeaderByte());
|
||||
}
|
||||
else if (_needProps)
|
||||
{
|
||||
|
||||
@@ -78,6 +78,34 @@ public class LzmaDecoderSafetyTests
|
||||
});
|
||||
}
|
||||
|
||||
// LZMA2 chunk with dictionary and properties reset whose properties byte encodes pb > 4.
|
||||
private static readonly byte[] InvalidChunkProperties = { 0xE0, 0, 0, 0, 4, 0xFF, 0, 0, 0, 0 };
|
||||
|
||||
[Fact]
|
||||
public void Lzma2InvalidChunkPropertiesThrowsDataError()
|
||||
{
|
||||
using var input = new MemoryStream(InvalidChunkProperties);
|
||||
using var stream = LzmaStream.Create(new byte[] { 0 }, input, leaveOpen: true);
|
||||
Assert.Throws<DataErrorException>(() => stream.ReadByte());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AsyncLzma2InvalidChunkPropertiesThrowsDataError()
|
||||
{
|
||||
using var input = new MemoryStream(InvalidChunkProperties);
|
||||
using var stream = await LzmaStream.CreateAsync(
|
||||
new byte[] { 0 },
|
||||
input,
|
||||
-1,
|
||||
-1,
|
||||
leaveOpen: true
|
||||
);
|
||||
var buffer = new byte[16];
|
||||
await Assert.ThrowsAsync<DataErrorException>(() =>
|
||||
stream.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None)
|
||||
);
|
||||
}
|
||||
|
||||
#if !LEGACY_DOTNET
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
|
||||
Reference in New Issue
Block a user