Skip to content

[cominterop] Add coop handle enter/return on native CCW methods #21365

Merged
lambdageek merged 2 commits intomono:mainfrom
lambdageek:fix-ccw-coop-handle-leak
Dec 13, 2021
Merged

[cominterop] Add coop handle enter/return on native CCW methods #21365
lambdageek merged 2 commits intomono:mainfrom
lambdageek:fix-ccw-coop-handle-leak

Conversation

@lambdageek
Copy link
Member

The CCW methods for IUnknown (and in principle IDispatch - except they all have trivial bodies) are native C code in the runtime that may allocate coop handles. Add a coop handle frame around the entire call in order to make sure they're cleaned up and don't retain a reference.

This helps fix managed object leaks with code like:

   var o  = new SomeClass();
   var pUnk = Marshal.GetIUnknownForObject(o);
   int c = Marshal.Release(pUnk);
   o = null;

Which retains a reference to the SomeClass instance that won't be collected until the thread dies, despite cleaning up the IUnknown refcount. The underling ccw addref/release methods leak coop handles on the thread.

This is not an issue when the CCW calls some managed method because there are no coop handles there until some icall (at which point it will set up the coop handle stack properly).

The CCW methods for IUnknown (and in principle IDispatch - except they all have
trivial bodies) are native C code in the runtime that may allocate coop
handles.  Add a coop handle frame around the entire call in order to make sure
they're cleaned up and don't retain a reference.

This helps fix managed object leaks with code like:

```
   var o  = new SomeClass();
   var pUnk = Marshal.GetIUnknownForObject(o);
   int c = Marshal.Release(pUnk);
   o = null;
```

Which retains a reference to the `SomeClass` instance that won't be collected
until the thread dies, despite cleaning up the IUnknown refcount. The underling
ccw addref/release methods leak coop handles on the thread.

This is not an issue when the CCW calls some managed method because there are
no coop handles there until some icall (at which point it will set up the
coop handle stack properly).
@lambdageek lambdageek force-pushed the fix-ccw-coop-handle-leak branch from acc120d to 8de1b6f Compare December 10, 2021 21:11
@lambdageek
Copy link
Member Author

/backport to 2020-02

@github-actions
Copy link
Contributor

@lambdageek
Copy link
Member Author

@monojenkins build failed

@naricc naricc self-requested a review December 13, 2021 17:59
@lambdageek lambdageek merged commit 1734a7d into mono:main Dec 13, 2021
@lambdageek lambdageek deleted the fix-ccw-coop-handle-leak branch December 13, 2021 19:40
lateralusX added a commit to lateralusX/runtime that referenced this pull request Mar 23, 2022
ThomasKuehne pushed a commit to ThomasKuehne/mono that referenced this pull request Mar 23, 2024
…#21365)

* [tests] Add CCW GetIUnknownForObject leak test

* [cominterop] Add coop handle enter/return on native CCW methods

The CCW methods for IUnknown (and in principle IDispatch - except they all have
trivial bodies) are native C code in the runtime that may allocate coop
handles.  Add a coop handle frame around the entire call in order to make sure
they're cleaned up and don't retain a reference.

This helps fix managed object leaks with code like:

```
   var o  = new SomeClass();
   var pUnk = Marshal.GetIUnknownForObject(o);
   int c = Marshal.Release(pUnk);
   o = null;
```

Which retains a reference to the `SomeClass` instance that won't be collected
until the thread dies, despite cleaning up the IUnknown refcount. The underling
ccw addref/release methods leak coop handles on the thread.

This is not an issue when the CCW calls some managed method because there are
no coop handles there until some icall (at which point it will set up the
coop handle stack properly).
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.

3 participants