[release/10.0] Fix crash on calling COM interface method on a class where a derived class overriding it was created first#128796
Conversation
When a ComMethodTable for a COM-visible interface is shared between a base class and a derived class (via ImplementsInterfaceWithSameSlotsAsParent), the virtual dispatch in COMToCLRGetObjectAndTarget_Virtual reads the managed vtable slot on the actual object's MethodTable via GetSlotForVirtual. Since temporary entry points are lazily allocated, the parent's vtable slot may still be NULL if the method was never called from managed code, causing an access violation. Fix by eagerly restoring the vtable slot on parent MethodTables during LayOutInterfaceMethodTable, so that GetSlotForVirtual never returns NULL during COM dispatch. Fixes dotnet#127512 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Test that COM-to-CLR dispatch through a shared ComMethodTable correctly resolves virtual method overrides. Uses separate type sets to independently validate both orderings (derived-first and base-first) within the same process, and verifies the correct method implementation is called. Regression test for dotnet#127512 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Tagging subscribers to this area: @dotnet/interop-contrib |
There was a problem hiding this comment.
Pull request overview
Backport to release/10.0 of a fix for a regression introduced by lazy temporary-entrypoint allocation (#101580). When a COM-visible base class and a derived class that overrides one of its virtual methods share a ComMethodTable, creating the derived class first laid out the shared ComMethodTable without ever forcing the base class's vtable slot to be restored. A subsequent COM call on the base instance then dispatched through a NULL slot and crashed. The fix walks parent MethodTables while laying out the interface method table and calls GetRestoredSlot so the base-class slot is materialized.
Changes:
- In
ComMethodTable::LayOutInterfaceMethodTable, for each virtual class method bound to an interface slot, walk up the parent chain and callGetRestoredSlot(slot)for every parent that still defines that virtual. - Adds a new COM interop test (
VirtualMethodOverrideTest) with both derived-first and base-first call orders to guard against the regression.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/comcallablewrapper.cpp | Restore parent vtable slots when laying out a shared ComMethodTable to prevent NULL COM dispatch slots. |
| src/tests/Interop/COM/VirtualMethodOverride/VirtualMethodOverrideTest.cs | New xUnit tests covering both ordering scenarios for COM-visible base/derived classes sharing an interface. |
| src/tests/Interop/COM/VirtualMethodOverride/VirtualMethodOverrideTest.csproj | Test project wiring (process-isolated, NativeAOT-incompatible). |
Copilot's findings
- Files reviewed: 3/3 changed files
- Comments generated: 0
See #127512
When a COM interface is implemented by a class with a virtual method and a derived class overrides it, they share a
ComMethodTable. We then useGetSlotForVirtualto resolve to the correct target for a method. With #101580, the slot for the base class is lazily populated. If the derived class is used first it creates aComMethodTableand lays it out, which makes sure the slots for that class' method table are restored. When base class is then used, it shares the sameComMethodTablewhich is already laid out, so it does not explicitly restore its slots, leaving the slot for the base class method null.This change makes laying out a
ComMethodTablealso restore parent vtable slots. I did this directly against release/10.0, as how we do this dispatch is quite different in .NET 11. It doesn't crash anymore, but we do still have a correctness issue there.cc @dotnet/appmodel @AaronRobinsonMSFT
Example setup:
If
Derivedis created first, callingBase.DoWorkcrashes.