-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Fixing a possible null reference error in WebSocket deflate. #62428
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
Conversation
Tagging subscribers to this area: @dotnet/ncl Issue DetailsThere is a possible situation (although I've been unable to reproduce) when a deflate with @QuinnDamerell is there any way you could try and run your code against this build, or this error happens only in production environment? Fixes #62422
|
src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketDeflater.cs
Outdated
Show resolved
Hide resolved
// is going to throw. | ||
needsMoreBuffer = errorCode == ErrorCode.BufError | ||
|| _stream.AvailIn > 0 | ||
|| written == output.Length; |
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.
So we have tests that exercise all of these conditions?
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 first two yes. They are easy to be tested when we provide payload that is incompressible.
The written == output.Length
I couldn't reproduce, but it is my only conclusion on how could an empty Span<byte>
buffer reach UnsafeFlush
.
@stephentoub, if you're interested, I was able to reproduce this, but I had to reduce the memory level with which the deflater is initialized. By default it's 8 (Deflate_DefaultMemLevel) and I changed it to 1 so I can iterate faster with different buffers. In order to change it, modify line 213 to Lines 204 to 215 in 47b9cb5
Now run the following test: [Fact]
public async Task Issue()
{
WebSocketTestStream stream = new();
using WebSocket server = WebSocket.CreateFromStream(stream, new WebSocketCreationOptions
{
IsServer = true,
KeepAliveInterval = TimeSpan.Zero,
DangerousDeflateOptions = new()
});
var blob = new byte[16136];
new Random(0).NextBytes(blob);
await server.SendAsync(blob, WebSocketMessageType.Binary, endOfMessage: true, CancellationToken);
stream.Remote.Clear();
} |
@zlatanov I wonder, if the condition for having an empty span is so specific, why the failure is not easily reproducible? And how does |
The memory level for the deflater represents how much memory it is allowed to use internally before flushing to So for example, if we try to deflate 10_000 bytes of data in a single deflate call, it deflater in most cases will not produce any output unless we explicitly say we want it to. In our case it is essential to avoid flushing and only flush when we're done deflating. However there might be cases when the deflater has reached some limit in its internal buffer and will flush bytes. It might even flush as many bytes as we've given it opportunity to. My original assumption was that the deflater will always return This is why working with I hope this makes sense, let me know if I can help further. |
@ManickaP should we go ahead and proceed merging this into main so it can be also backported for NET 6? @QuinnDamerell reports no more errors with the build that you've provided. @stephentoub I was unable to produce such data that would trigger this behavior so a test could be written. |
Let's merge this. |
Sounds reasonable to me. |
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.
Thanks!
/backport to release/6.0 |
Started backporting to release/6.0: https://github.com/dotnet/runtime/actions/runs/1572912323 |
There is a possible situation (although I've been unable to reproduce) when a deflate with
FlushCode.NoFlush
flush code consumes the entire output buffer but also doesn't return a buffer error to indicate that it needs more output. In this case the WebSocket would happily callFlush
with an emptySpan<byte>
buffer, which when used infixed
statement would result in null pointer.@QuinnDamerell is there any way you could try and run your code against this build, or this error happens only in production environment?
Fixes #62422