Skip to content

Conversation

@rzikm
Copy link
Member

@rzikm rzikm commented Nov 19, 2025

Fixes #121710.

Customer Impact

Certain valid code patterns using DeflateStream and GZipStream may produce invalid bytes when compressing empty input. The invalid output then cannot be correctly decompressed by 3rd party tools.

Workarounds are either not using async path or ensuring FlushAsync is not called by user code if nothing was written to the stream.

  • Customer reported
  • Found internally

Regression

  • Yes
  • No

Behavior differs w.r.t. .NET 8.

The bug has been fixed for .NET 11 by #118570

Testing

Tested scenario reported by customer. Additional validation is provided by CI

Risk

Low, fix is small and the root cause is well understood.

@rzikm rzikm requested review from a team and Copilot November 19, 2025 16:14
Copilot finished reviewing on behalf of rzikm November 19, 2025 16:15
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a regression in DeflateStream.FlushAsync that produces invalid compressed bytes when the method is called without writing any data first. The fix prevents flushing the deflater when no bytes have been written, while still flushing the underlying stream.

Key changes:

  • Wrap deflater flush logic in a _wroteBytes check to prevent invalid output on empty streams
  • Underlying stream flush remains unconditional to preserve expected flush behavior

Comment on lines +223 to +240
if (_wroteBytes)
{
int compressedBytes;
flushSuccessful = _deflater.Flush(_buffer, out compressedBytes);
if (flushSuccessful)
// Compress any bytes left:
await WriteDeflaterOutputAsync(cancellationToken).ConfigureAwait(false);

// Pull out any bytes left inside deflater:
bool flushSuccessful;
do
{
await _stream.WriteAsync(new ReadOnlyMemory<byte>(_buffer, 0, compressedBytes), cancellationToken).ConfigureAwait(false);
}
Debug.Assert(flushSuccessful == (compressedBytes > 0));
} while (flushSuccessful);
int compressedBytes;
flushSuccessful = _deflater.Flush(_buffer, out compressedBytes);
if (flushSuccessful)
{
await _stream.WriteAsync(new ReadOnlyMemory<byte>(_buffer, 0, compressedBytes), cancellationToken).ConfigureAwait(false);
}
Debug.Assert(flushSuccessful == (compressedBytes > 0));
} while (flushSuccessful);
}
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The entire deflater flush block is indented one level deeper, but the comment on line 225 still says 'Compress any bytes left' which may be slightly misleading when _wroteBytes is false and no compression occurs. Consider updating the comment to clarify the conditional nature, such as '// Compress any bytes left if data was written:'

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant