-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Add CRC32 validation when reading zip archive entries #124766
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -712,4 +712,182 @@ public override async ValueTask DisposeAsync() | |||||||||||||||||||||||||
| await base.DisposeAsync().ConfigureAwait(false); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| internal sealed class CrcValidatingReadStream : Stream | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| private readonly Stream _baseStream; | ||||||||||||||||||||||||||
| private uint _runningCrc; | ||||||||||||||||||||||||||
| private readonly uint _expectedCrc; | ||||||||||||||||||||||||||
| private long _totalBytesRead; | ||||||||||||||||||||||||||
| private readonly long _expectedLength; | ||||||||||||||||||||||||||
| private bool _isDisposed; | ||||||||||||||||||||||||||
| private bool _crcValidated; | ||||||||||||||||||||||||||
| private bool _crcAbandoned; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public CrcValidatingReadStream(Stream baseStream, uint expectedCrc, long expectedLength) | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| _baseStream = baseStream; | ||||||||||||||||||||||||||
| _expectedCrc = expectedCrc; | ||||||||||||||||||||||||||
| _expectedLength = expectedLength; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
+727
to
+732
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public override bool CanRead => !_isDisposed && _baseStream.CanRead; | ||||||||||||||||||||||||||
| public override bool CanSeek => !_isDisposed && _baseStream.CanSeek; | ||||||||||||||||||||||||||
| public override bool CanWrite => false; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public override long Length => _baseStream.Length; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public override long Position | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| get => _baseStream.Position; | ||||||||||||||||||||||||||
| set | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| ThrowIfDisposed(); | ||||||||||||||||||||||||||
| ThrowIfCantSeek(); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| _crcAbandoned = true; | ||||||||||||||||||||||||||
| _baseStream.Position = value; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public override int Read(byte[] buffer, int offset, int count) | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| ThrowIfDisposed(); | ||||||||||||||||||||||||||
| ValidateBufferArguments(buffer, offset, count); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| int bytesRead = _baseStream.Read(buffer, offset, count); | ||||||||||||||||||||||||||
| ProcessBytesRead(buffer.AsSpan(offset, bytesRead)); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return bytesRead; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public override int Read(Span<byte> buffer) | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| ThrowIfDisposed(); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| int bytesRead = _baseStream.Read(buffer); | ||||||||||||||||||||||||||
| ProcessBytesRead(buffer.Slice(0, bytesRead)); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return bytesRead; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public override int ReadByte() | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| byte b = default; | ||||||||||||||||||||||||||
| return Read(new Span<byte>(ref b)) == 1 ? b : -1; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| ThrowIfDisposed(); | ||||||||||||||||||||||||||
| ValidateBufferArguments(buffer, offset, count); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| int bytesRead = await _baseStream.ReadAsync(buffer.AsMemory(offset, count), cancellationToken).ConfigureAwait(false); | ||||||||||||||||||||||||||
| ProcessBytesRead(buffer.AsSpan(offset, bytesRead)); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return bytesRead; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default) | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| ThrowIfDisposed(); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| int bytesRead = await _baseStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); | ||||||||||||||||||||||||||
| ProcessBytesRead(buffer.Span.Slice(0, bytesRead)); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return bytesRead; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| private void ProcessBytesRead(ReadOnlySpan<byte> data) | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| if (data.Length > 0 && !_crcAbandoned) | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| _runningCrc = Crc32Helper.UpdateCrc32(_runningCrc, data); | ||||||||||||||||||||||||||
| _totalBytesRead += data.Length; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (_totalBytesRead >= _expectedLength) | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| ValidateCrc(); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
+808
to
+811
|
||||||||||||||||||||||||||
| if (_totalBytesRead >= _expectedLength) | |
| { | |
| ValidateCrc(); | |
| } | |
| if (_totalBytesRead == _expectedLength) | |
| { | |
| ValidateCrc(); | |
| } | |
| else if (_totalBytesRead > _expectedLength) | |
| { | |
| throw new InvalidDataException(SR.CrcMismatch); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _runningCrc field should be explicitly initialized to 0 in the constructor for clarity, even though the default value for uint is 0. This makes the intent clear that CRC32 calculation starts with an initial value of 0, consistent with the pattern used in CheckSumAndSizeWriteStream (line 503).