Fix COM reentrancy contract violations in ExtensionPoints-related paths#126571
Fix COM reentrancy contract violations in ExtensionPoints-related paths#126571
Conversation
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/68c1bb22-8f29-41a2-86b8-b039f3aa63bc Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com>
|
Tagging subscribers to this area: @dotnet/interop-contrib |
…r and update related calls
There was a problem hiding this comment.
Pull request overview
This PR adjusts CoreCLR COM interop contract handling to accommodate managed re-entrancy (e.g., managed IMallocSpy) during marshaling cleanup and related COM-call paths.
Changes:
- Update
OleVariant::ClearLPSTRArray/ClearLPWSTRArraycleanup paths to tolerateCoTaskMemFreere-entrancy via a contract-violation annotation. - Simplify
CheckParentComVisibilityAPI by removing an unusedBOOL fForIDispatchparameter and updating call sites. - Add a
ThrowsViolationcontract-violation scope inComPreStubWorkeraround the managed exception dispatcher / unwind handler region.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/stubhelpers.cpp | Update call site to new CheckParentComVisibility() signature. |
| src/coreclr/vm/stdinterfaces.cpp | Update IDispatch-related call sites to new CheckParentComVisibility() signature. |
| src/coreclr/vm/olevariant.cpp | Annotate LPSTR/LPWSTR array cleanup to allow managed re-entrancy during CoTaskMemFree. |
| src/coreclr/vm/comcallablewrapper.h | Remove unused fForIDispatch parameter from CheckParentComVisibility* APIs. |
| src/coreclr/vm/comcallablewrapper.cpp | Add contract-violation scope in ComPreStubWorker; update visibility checks to new signature. |
Comments suppressed due to low confidence (1)
src/coreclr/vm/comcallablewrapper.cpp:324
BEGIN_CONTRACT_VIOLATION(ThrowsViolation)introduces a scope that must always reachEND_CONTRACT_VIOLATION. UnderFEATURE_INTERPRETER, the earlyreturn pInterpreterTarget;exits the function without callingEND_CONTRACT_VIOLATION, leaving the violation state active on the thread (and skipping the explicit leave call). Restructure so all returns afterBEGIN_CONTRACT_VIOLATIONleave the scope (e.g., move the interpreter fast-path beforeBEGIN_CONTRACT_VIOLATION, or ensure it executesEND_CONTRACT_VIOLATIONbefore returning).
BEGIN_CONTRACT_VIOLATION(ThrowsViolation);
INSTALL_MANAGED_EXCEPTION_DISPATCHER;
INSTALL_UNWIND_AND_CONTINUE_HANDLER;
#ifdef FEATURE_INTERPRETER
PCODE pInterpreterTarget = pEntryThunk->GetInterpreterTarget();
if (pInterpreterTarget != (PCODE)0)
{
t_MostRecentUMEntryThunkData = pEntryThunk;
return pInterpreterTarget;
}
This comment has been minimized.
This comment has been minimized.
|
@MichalStrehovsky This PR should fix the contract violations you found. |
Sounds good. We'll only be able to test this once #126517 merges since right now the test doesn't run at all. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/coreclr/vm/comcallablewrapper.cpp:324
ComPreStubWorkernow entersBEGIN_CONTRACT_VIOLATION(ThrowsViolation)and installs the managed exception dispatcher / unwind handler, but theFEATURE_INTERPRETERfast path returns early (return pInterpreterTarget;) beforeUNINSTALL_*andEND_CONTRACT_VIOLATION.ContractViolationHolderdoes not auto-leave on scope exit, so this leaves the violation mask (and the try/catch dispatcher scope) unbalanced for the rest of the thread’s execution. Consider moving the interpreter-target early-return before theBEGIN_CONTRACT_VIOLATION/INSTALL_*sequence, or refactor to a single-exit cleanup path that always uninstalls and ends the contract violation before returning.
BEGIN_CONTRACT_VIOLATION(ThrowsViolation);
INSTALL_MANAGED_EXCEPTION_DISPATCHER;
INSTALL_UNWIND_AND_CONTINUE_HANDLER;
#ifdef FEATURE_INTERPRETER
PCODE pInterpreterTarget = pEntryThunk->GetInterpreterTarget();
if (pInterpreterTarget != (PCODE)0)
{
t_MostRecentUMEntryThunkData = pEntryThunk;
return pInterpreterTarget;
}
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/dda7a23f-910f-4f7f-b3f8-4b21b06d6a14 Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/76569a90-7ab4-4a60-ab3d-c1795c141dd9 Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
|
Note This review was generated by Copilot. 🤖 Copilot Code Review — PR #126571Holistic AssessmentMotivation: This PR addresses contract annotation correctness in COM interop code. It fixes three distinct issues: (1) Approach: The approach is appropriate for each change: Summary: ✅ LGTM. All changes are correct, well-motivated, and consistent with established codebase patterns. The dead Detailed Findings✅ Contract Correctness —
|
Note
This PR description was updated with GitHub Copilot assistance.
This PR fixes the
Interop/COM/ExtensionPointsfailure by correcting CoreCLR COM interop contract handling for paths that can legitimately re-enter managed code during COM allocation hooks, and it includes a small cleanup of the related COM visibility helpers.Changes
OleVariant::ClearLPSTRArrayandOleVariant::ClearLPWSTRArrayasNOTHROW, but mark theCoTaskMemFreecleanup paths with a permanentThrowsViolationfor runtime reentrancy, sinceCoTaskMemFreecan invoke a managedIMallocSpyimplementation.ComPreStubWorkersetup around the managed exception dispatcher and unwind handler in aBEGIN_CONTRACT_VIOLATION(ThrowsViolation)/END_CONTRACT_VIOLATIONblock.ComPreStubWorkertoSTATIC_CONTRACT_NOTHROWso it matches the intended HRESULT-returning behavior outside the explicit violation scope.fForIDispatchparameter fromCheckParentComVisibility.CheckParentComVisibilityNoThrowhelper and useHasInvisibleParent()directly at the remaining visibility check.Why this addresses the failure
The failing test exercises COM extension points that can callback into managed code during allocation cleanup and stub initialization. Those paths were operating under overly strict contracts, which made valid reentrant behavior show up as contract violations. This change narrows the violation handling to the specific reentrant regions while keeping the surrounding HRESULT-returning contracts intact. The visibility-helper refactors are supporting cleanups with no intended behavioral change.