Skip to content

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

Merged
merged 3 commits into from
Mar 14, 2025

Conversation

filipnavara
Copy link
Member

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.

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.
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Mar 13, 2025
@filipnavara
Copy link
Member Author

filipnavara commented Mar 13, 2025

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)

@filipnavara filipnavara requested a review from pavelsavara March 13, 2025 07:57
@pavelsavara pavelsavara self-assigned this Mar 13, 2025
@pavelsavara pavelsavara added this to the 10.0.0 milestone Mar 13, 2025
@pavelsavara pavelsavara added arch-wasm WebAssembly architecture os-browser Browser variant of arch-wasm labels Mar 13, 2025
Copy link
Contributor

Tagging subscribers to 'arch-wasm': @lewing
See info in area-owners.md if you want to be subscribed.

@pavelsavara

This comment was marked as duplicate.

@EgorBo
Copy link
Member

EgorBo commented Mar 13, 2025

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:

Assert failure(PID 281072 [0x000449f0], Thread: 376552 [0x5bee8]): FALSE

CORECLR! BlockFreeHandles + 0x158 (0x00007ff8`6dbdff68)
CORECLR! TableFreeBulkPreparedHandles + 0x112 (0x00007ff8`6dbe1812)
CORECLR! TableFullRebalanceCache + 0x1C8 (0x00007ff8`6dc79f98)
CORECLR! TableFreeSingleHandleToCache + 0x178 (0x00007ff8`6dc79d58)
CORECLR! HndDestroyHandle + 0x222 (0x00007ff8`6dbdf1e2)
CORECLR! HndDestroyHandleOfUnknownType + 0xE1 (0x00007ff8`6dbdf321)
CORECLR! MarshalNative::GCHandleInternalFree + 0x126 (0x00007ff8`6d9915f6)
SYSTEM.PRIVATE.CORELIB! <no symbol> + 0x0 (0x00007ff8`6c41127f)
SYSTEM.PRIVATE.CORELIB! <no symbol> + 0x0 (0x00007ff8`6c379dab)
<no module>! <no symbol> + 0x0 (0x00007ff8`0e441e5c)
    File: C:\prj\runtime-main2\src\coreclr\gc\handletablecore.cpp:1899
    Image: C:\prj\runtime-main2\artifacts\bin\coreclr\windows.x64.Checked\CoreRun.exe

When we clone MemoryHandle (via boxing) and call Dispose on both the original struct and its boxed clone, we end up calling GCHandle.Free and IPinable.Unpin twice here.

@jkotas is it something that shouldn't happen or just an UB on double-free?

@huoyaoyuan
Copy link
Member

Double free of GCHandle is faulty. See also #112727

@jkotas
Copy link
Member

jkotas commented Mar 13, 2025

is it something that shouldn't happen or just an UB on double-free?

It is just an UB on double-free.

@pavelsavara
Copy link
Member

This will need 9.0 backport. Net8 doesn't have this issue.

@pavelsavara
Copy link
Member

/ba-g know CI timeouts

@pavelsavara pavelsavara merged commit dfd3cc0 into dotnet:main Mar 14, 2025
89 of 92 checks passed
@pavelsavara
Copy link
Member

/backport to release/9.0-staging

Copy link
Contributor

Started backporting to release/9.0-staging: https://github.com/dotnet/runtime/actions/runs/13860807379

@github-actions github-actions bot locked and limited conversation to collaborators Apr 14, 2025
@filipnavara filipnavara deleted the patch-20 branch April 27, 2025 16:26
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
arch-wasm WebAssembly architecture area-System.Net community-contribution Indicates that the PR has been added by a community member os-browser Browser variant of arch-wasm
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants