Skip to content

[wasm][coreclr] Fix prestub of methods with IL helper stubs#124873

Open
radekdoulik wants to merge 1 commit intodotnet:mainfrom
radekdoulik:clr-wasm-prestub-il-helpers
Open

[wasm][coreclr] Fix prestub of methods with IL helper stubs#124873
radekdoulik wants to merge 1 commit intodotnet:mainfrom
radekdoulik:clr-wasm-prestub-il-helpers

Conversation

@radekdoulik
Copy link
Member

Fix #121955

@radekdoulik radekdoulik added this to the Future milestone Feb 25, 2026
@radekdoulik radekdoulik added the arch-wasm WebAssembly architecture label Feb 25, 2026
Copilot AI review requested due to automatic review settings February 25, 2026 21:44
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a WASM/CoreCLR interpreter prestub issue where methods implemented via managed IL helper stubs could end up publishing an entrypoint that maps to the helper MethodDesc, breaking MethodDesc lookups and access checks (manifesting as MethodAccessException, e.g. around delegate construction).

Changes:

  • Update MethodDesc::DoPrestub (FCall + managed-impl + portable entrypoints) to publish this method’s own PortableEntryPoint and attach interpreter data to it, instead of reusing the helper’s portable entrypoint.
  • Enable interpreter callsite access-check callouts on WASM by removing the #ifndef TARGET_WASM guard.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/coreclr/vm/prestub.cpp Publishes the current method’s portable entrypoint and sets interpreter data for managed FCall helper-stub scenarios.
src/coreclr/interpreter/compiler.cpp Enables emitting callsite access-check callouts on WASM by removing a WASM-only compile-time exclusion.

Comment on lines 2317 to +2328
SetInterpreterCode((InterpByteCodeStart*)ilStubInterpData);

// Use this method's own PortableEntryPoint rather than the helper's.
// Setting pCode to the helper's PortableEntryPoint would write a foreign
// MethodDesc pointer into this method's slot, breaking MethodDesc lookups
// and access checks (e.g. private Delegate.DelegateConstruct exposed on
// delegate ctor slots causes MethodAccessException).
PCODE entryPoint = GetPortableEntryPoint();
if (ilStubInterpData != NULL)
{
PortableEntryPoint::SetInterpreterData(entryPoint, (PCODE)(TADDR)ilStubInterpData);
}
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the managed-FCall + FEATURE_PORTABLE_ENTRYPOINTS path, pCode is now set to this method’s PortableEntryPoint unconditionally, but PortableEntryPoint::SetInterpreterData is only called when helperMD->GetInterpreterCode() is non-null. If the helper ends up with native/AOT code (so GetInterpreterCode() stays null), the returned entryPoint will have neither interpreter data nor actual code set, which can result in repeatedly re-entering prestub / inability to execute the method. Consider handling the non-interpreter case by setting this method’s PortableEntryPoint actual code to the helper’s actual code (e.g., if PortableEntryPoint::HasNativeEntryPoint(pCode) then SetActualCode(entryPoint, (PCODE)PortableEntryPoint::GetActualCode(pCode))), and/or asserting that one of interpreter/native is always available before publishing pCode.

Suggested change
SetInterpreterCode((InterpByteCodeStart*)ilStubInterpData);
// Use this method's own PortableEntryPoint rather than the helper's.
// Setting pCode to the helper's PortableEntryPoint would write a foreign
// MethodDesc pointer into this method's slot, breaking MethodDesc lookups
// and access checks (e.g. private Delegate.DelegateConstruct exposed on
// delegate ctor slots causes MethodAccessException).
PCODE entryPoint = GetPortableEntryPoint();
if (ilStubInterpData != NULL)
{
PortableEntryPoint::SetInterpreterData(entryPoint, (PCODE)(TADDR)ilStubInterpData);
}
if (ilStubInterpData != NULL)
{
SetInterpreterCode((InterpByteCodeStart*)ilStubInterpData);
}
// Use this method's own PortableEntryPoint rather than the helper's.
// Setting pCode to the helper's PortableEntryPoint would write a foreign
// MethodDesc pointer into this method's slot, breaking MethodDesc lookups
// and access checks (e.g. private Delegate.DelegateConstruct exposed on
// delegate ctor slots causes MethodAccessException).
PCODE helperEntryPoint = pCode;
PCODE entryPoint = GetPortableEntryPoint();
if (ilStubInterpData != NULL)
{
PortableEntryPoint::SetInterpreterData(entryPoint, (PCODE)(TADDR)ilStubInterpData);
}
else if (PortableEntryPoint::HasNativeEntryPoint(helperEntryPoint))
{
PortableEntryPoint::SetActualCode(entryPoint, (PCODE)PortableEntryPoint::GetActualCode(helperEntryPoint));
}
else
{
// Managed FCall helpers must have either interpreter or native entrypoint
_ASSERTE(!"Managed FCall helper has neither interpreter nor native entrypoint");
}

Copilot uses AI. Check for mistakes.
Copy link
Member Author

@radekdoulik radekdoulik Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should have IL stub, so it should not be native code entrypoint.

we should handle other scenario though, once #124868 lands. to handle case where the helper method's portable entrypoint gets reset (for example by tiering once we have codegen) and force prestub on it again to force recompilation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will let the CI run finish to make sure it passes. then I should add an assert at least or handle the case described above

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-VM-coreclr

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[wasm][coreclr] Attempt to access method System.Delegate.DelegateConstruct results in exception

2 participants