From 446cb384be263fc2f9afecd59f996b7bf9ee2dd0 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 25 Feb 2026 22:37:26 +0100 Subject: [PATCH 1/4] [wasm][coreclr] Fix prestub of methods with IL helper stubs Fix https://github.com/dotnet/runtime/issues/121955 --- src/coreclr/interpreter/compiler.cpp | 3 --- src/coreclr/vm/prestub.cpp | 12 ++++++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index b5924423564ee6..c48bc08ac7d2fb 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -4097,8 +4097,6 @@ void InterpCompiler::EmitCanAccessCallout(CORINFO_RESOLVED_TOKEN *pResolvedToken void InterpCompiler::EmitCallsiteCallout(CorInfoIsAccessAllowedResult accessAllowed, CORINFO_HELPER_DESC* calloutDesc) { -// WASM-TODO: https://github.com/dotnet/runtime/issues/121955 -#ifndef TARGET_WASM if (accessAllowed == CORINFO_ACCESS_ILLEGAL) { int32_t svars[CORINFO_ACCESS_ALLOWED_MAX_ARGS]; @@ -4169,7 +4167,6 @@ void InterpCompiler::EmitCallsiteCallout(CorInfoIsAccessAllowedResult accessAllo } m_pLastNewIns->data[0] = GetDataForHelperFtn(calloutDesc->helperNum); } -#endif // !TARGET_WASM } static OpcodePeepElement peepRuntimeAsyncCall[] = { diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 9b4b886981a82c..bbf768ba549e20 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -2315,6 +2315,18 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT, CallerGCMode callerGCMo (void)helperMD->DoPrestub(NULL /* MethodTable */, CallerGCMode::Coop); void* ilStubInterpData = helperMD->GetInterpreterCode(); 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); + } + pCode = entryPoint; } #else // !FEATURE_PORTABLE_ENTRYPOINTS // FCalls are always wrapped in a precode to enable mapping of the entrypoint back to MethodDesc From 4045eedb7c4b1b09bb2b0efc1f60a3d6153d99d0 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Thu, 26 Feb 2026 15:03:02 +0100 Subject: [PATCH 2/4] Suggestion from review Co-authored-by: Jan Kotas --- src/coreclr/vm/prestub.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index bbf768ba549e20..295c879b87ee67 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -2317,10 +2317,7 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT, CallerGCMode callerGCMo 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). + // It is required to maintain 1:1 mapping between MethodDesc and its entrypoint. PCODE entryPoint = GetPortableEntryPoint(); if (ilStubInterpData != NULL) { From e633c68cba9e29b1c515f50430b886ee5d097648 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Thu, 26 Feb 2026 15:19:20 +0100 Subject: [PATCH 3/4] Feedback --- src/coreclr/vm/prestub.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 295c879b87ee67..2200d4d2c0b3c1 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -2314,15 +2314,13 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT, CallerGCMode callerGCMo if (helperMD->ShouldCallPrestub()) (void)helperMD->DoPrestub(NULL /* MethodTable */, CallerGCMode::Coop); void* ilStubInterpData = helperMD->GetInterpreterCode(); + _ASSERTE(ilStubInterpData != NULL); SetInterpreterCode((InterpByteCodeStart*)ilStubInterpData); // Use this method's own PortableEntryPoint rather than the helper's. // It is required to maintain 1:1 mapping between MethodDesc and its entrypoint. PCODE entryPoint = GetPortableEntryPoint(); - if (ilStubInterpData != NULL) - { - PortableEntryPoint::SetInterpreterData(entryPoint, (PCODE)(TADDR)ilStubInterpData); - } + PortableEntryPoint::SetInterpreterData(entryPoint, (PCODE)(TADDR)ilStubInterpData); pCode = entryPoint; } #else // !FEATURE_PORTABLE_ENTRYPOINTS From 3d23fcf983d4bab99b6435428729154a11d33cde Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 26 Feb 2026 19:32:51 -0800 Subject: [PATCH 4/4] Update src/coreclr/vm/prestub.cpp --- src/coreclr/vm/prestub.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 2200d4d2c0b3c1..4ee558fa1bee08 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -2314,6 +2314,7 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT, CallerGCMode callerGCMo if (helperMD->ShouldCallPrestub()) (void)helperMD->DoPrestub(NULL /* MethodTable */, CallerGCMode::Coop); void* ilStubInterpData = helperMD->GetInterpreterCode(); + // WASM-TODO: update this when we will have codegen _ASSERTE(ilStubInterpData != NULL); SetInterpreterCode((InterpByteCodeStart*)ilStubInterpData);