-
Notifications
You must be signed in to change notification settings - Fork 5k
Fix double dispose of GCHandle in BrowserWebSocket #113464
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
MemoryHandle is a struct, so passing it into an IDisposable parameter creates a box. This causes two copies of GCHandle to exist which are no longer synchronized and can be double disposed. This happened when passing pinBuffer into CancellationHelper in ReceiveAsyncCore and SendAsyncCore. In both cases the caller does the Dispose itself inside a finally, so there's no need to pass the pinBuffer to CancellationHelper at all.
This has been reported downstream in NativeAOT-LLVM where the double-free of the GCHandle causes an assert (in Debug) or a corruption. (/cc @yowl for the original report) |
Tagging subscribers to 'arch-wasm': @lewing |
This comment was marked as duplicate.
This comment was marked as duplicate.
A minimal repro for the issue Filip was tracing: using System.Buffers;
ThreadPool.QueueUserWorkItem(_ => GC.Collect());
for (int i = 0; i < 100; i++)
{
Memory<byte> bufferMemory = new byte[100];
MemoryHandle mh = bufferMemory.Pin();
IDisposable d = mh;
d.Dispose();
mh.Dispose();
} Fails for me on the Checked runtime with:
When we clone @jkotas is it something that shouldn't happen or just an UB on double-free? |
Double free of GCHandle is faulty. See also #112727 |
It is just an UB on double-free. |
This will need 9.0 backport. Net8 doesn't have this issue. |
/ba-g know CI timeouts |
/backport to release/9.0-staging |
Started backporting to release/9.0-staging: https://github.com/dotnet/runtime/actions/runs/13860807379 |
MemoryHandle is a struct, so passing it into an IDisposable parameter creates a box. This causes two copies of GCHandle to exist which are no longer synchronized and can be double disposed.
This happened when passing pinBuffer into CancellationHelper in ReceiveAsyncCore and SendAsyncCore. In both cases the caller does the Dispose itself inside a finally, so there's no need to pass the pinBuffer to CancellationHelper at all.