Skip to content
Merged
1 change: 1 addition & 0 deletions src/coreclr/dlls/mscorrc/mscorrc.rc
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ BEGIN
BFA_MISSING_DELEGATE_METHOD "Missing definition for required runtime implemented delegate method."
BFA_MULT_TYPE_SAME_NAME "Duplicate type with name '%1' in assembly '%2'."
BFA_INVALID_METHOD_TOKEN "Bad method token."
BFA_BAD_ASYNC_METHOD "Bad use of RuntimeAsync flag."
BFA_ECALLS_MUST_BE_IN_SYS_MOD "ECall methods must be packaged into a system module."
BFA_CANT_GET_CLASSLAYOUT "Could not get classlayout."
BFA_CALLCONV_NOT_LOCAL_SIG "Signature is not IMAGE_CEE_CS_CALLCONV_LOCAL_SIG."
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/dlls/mscorrc/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@
#define BFA_MISSING_DELEGATE_METHOD 0x2030
#define BFA_MULT_TYPE_SAME_NAME 0x2031
#define BFA_INVALID_METHOD_TOKEN 0x2032
#define BFA_BAD_ASYNC_METHOD 0x2033
#define BFA_ECALLS_MUST_BE_IN_SYS_MOD 0x2034
#define BFA_CANT_GET_CLASSLAYOUT 0x2035
#define BFA_CALLCONV_NOT_LOCAL_SIG 0x2036
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4279,6 +4279,11 @@ private uint getJitFlags(ref CORJIT_FLAGS flags, uint sizeInBytes)
ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramGenericMethod, this.MethodBeingCompiled);
}

if (this.MethodBeingCompiled.IsAsync)
{
ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramAsync, this.MethodBeingCompiled);
}

#if READYTORUN
// TODO: enable this check in full AOT
if (Marshaller.IsMarshallingRequired(this.MethodBeingCompiled.Signature, ((MetadataType)this.MethodBeingCompiled.OwningType).Module, this.MethodBeingCompiled.GetUnmanagedCallersOnlyMethodCallingConventions())) // Only blittable arguments
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public enum ExceptionStringID
InvalidProgramGenericMethod,
InvalidProgramNonBlittableTypes,
InvalidProgramMultipleCallConv,
InvalidProgramAsync,

// BadImageFormatException
BadImageFormatGeneric,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@
<data name="FileLoadErrorGeneric" xml:space="preserve">
<value>Failed to load assembly '{0}'</value>
</data>
<data name="InvalidProgramAsync" xml:space="preserve">
<value>UnmanagedCallersOnly attribute specified on async method '{0}'</value>
</data>
<data name="InvalidProgramDefault" xml:space="preserve">
<value>Invalid IL or CLR metadata</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ private static MethodIL TryGetRuntimeImplementedMethodIL(MethodDesc method)

Debug.Assert(method.IsRuntimeImplemented);

if (method.IsAsync)
{
ThrowHelper.ThrowBadImageFormatException();
}

TypeDesc owningType = method.OwningType;

if (owningType.IsDelegate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public static UnmanagedCallingConventions GetPInvokeMethodCallingConventions(thi
{
Debug.Assert(method.IsPInvoke);

UnmanagedCallingConventions result;
UnmanagedCallingConventions result = 0;

if (method is Internal.IL.Stubs.PInvokeTargetNativeMethod pinvokeTarget)
method = pinvokeTarget.Target;
Expand All @@ -116,9 +116,9 @@ public static UnmanagedCallingConventions GetPInvokeMethodCallingConventions(thi
&& (int)MethodSignatureFlags.UnmanagedCallingConventionThisCall == (int)UnmanagedCallingConventions.Thiscall);
result = (UnmanagedCallingConventions)unmanagedCallConv;
}
else
else if (method is EcmaMethod ecmaMethod)
{
CustomAttributeValue<TypeDesc>? unmanagedCallConvAttribute = ((EcmaMethod)method).GetDecodedCustomAttribute("System.Runtime.InteropServices", "UnmanagedCallConvAttribute");
CustomAttributeValue<TypeDesc>? unmanagedCallConvAttribute = ecmaMethod.GetDecodedCustomAttribute("System.Runtime.InteropServices", "UnmanagedCallConvAttribute");
if (unmanagedCallConvAttribute != null)
{
result = GetUnmanagedCallingConventionFromAttribute(unmanagedCallConvAttribute.Value, method.Context);
Expand All @@ -127,10 +127,10 @@ public static UnmanagedCallingConventions GetPInvokeMethodCallingConventions(thi
{
result = GetPlatformDefaultUnmanagedCallingConvention(method.Context);
}
}

if (method.HasCustomAttribute("System.Runtime.InteropServices", "SuppressGCTransitionAttribute"))
result |= UnmanagedCallingConventions.IsSuppressGcTransition;
if (method.HasCustomAttribute("System.Runtime.InteropServices", "SuppressGCTransitionAttribute"))
result |= UnmanagedCallingConventions.IsSuppressGcTransition;
}

return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ public PInvokeILProvider(PInvokeILEmitterConfiguration pInvokeILEmitterConfigura

public override MethodIL GetMethodIL(MethodDesc method)
{
if (method.IsAsync)
{
ThrowHelper.ThrowBadImageFormatException();
}
return PInvokeILEmitter.EmitIL(method, _pInvokeILEmitterConfiguration, _interopStateManager);
}

Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/vm/comdelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2045,6 +2045,10 @@ void COMDelegate::ThrowIfInvalidUnmanagedCallersOnlyUsage(MethodDesc* pMD)
if (pMD->HasClassOrMethodInstantiation())
EX_THROW(EEResourceException, (kInvalidProgramException, W("InvalidProgram_GenericMethod")));

// No async methods
if (pMD->IsAsyncMethod())
EX_THROW(EEResourceException, (kInvalidProgramException, W("InvalidProgram_AsyncMethod")));

// Arguments - Scenarios involving UnmanagedCallersOnly are handled during the jit.
bool unmanagedCallersOnlyRequiresMarshalling = false;
if (PInvoke::MarshalingRequired(pMD, NULL, NULL, NULL, unmanagedCallersOnlyRequiresMarshalling))
Expand Down
33 changes: 15 additions & 18 deletions src/coreclr/vm/dllimport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ StubSigDesc::StubSigDesc(MethodDesc *pMD)
GC_NOTRIGGER;
SUPPORTS_DAC;
PRECONDITION(pMD != NULL);
PRECONDITION(!pMD->IsAsyncMethod());
}
CONTRACTL_END;

Expand All @@ -108,9 +109,6 @@ StubSigDesc::StubSigDesc(MethodDesc *pMD)
m_sig = pMD->GetSignature();
m_pModule = pMD->GetModule(); // Used for token resolution.

// TODO: (async) revisit and examine if this needs to be supported somehow
_ASSERTE(!pMD->IsAsyncMethod());

m_tkMethodDef = pMD->GetMemberDef();
SigTypeContext::InitTypeContext(pMD, &m_typeContext);
m_pMetadataModule = pMD->GetModule();
Expand Down Expand Up @@ -1131,7 +1129,6 @@ class ILStubState : public StubState
DWORD dwToken = 0;
if (pTargetMD)
{
// TODO: (async) revisit and examine if this needs to be supported somehow
_ASSERTE(!pTargetMD->IsAsyncVariantMethod());
dwToken = pTargetMD->GetMemberDef();
}
Expand Down Expand Up @@ -2764,6 +2761,10 @@ void PInvokeStaticSigInfo::DllImportInit(

PRECONDITION(CheckPointer(pMD));

// P/Invoke methods should never be marked as async.
// This should be blocked in the class loader.
PRECONDITION(!pMD->IsAsyncMethod());

// These preconditions to prevent multithreaded regression
// where pMD->m_szLibName was passed in directly, cleared
// by this API, then accessed on another thread before being reset here.
Expand All @@ -2779,9 +2780,6 @@ void PInvokeStaticSigInfo::DllImportInit(
IMDInternalImport *pInternalImport = pMD->GetMDImport();
CorPinvokeMap mappingFlags = pmMaxValue;
mdModuleRef modref = mdModuleRefNil;
// TODO: (async) revisit and examine if this needs to be supported somehow
if (pMD->IsAsyncMethod())
ThrowHR(COR_E_NOTSUPPORTED);

if (FAILED(pInternalImport->GetPinvokeMap(pMD->GetMemberDef(), (DWORD*)&mappingFlags, ppEntryPointName, &modref)))
{
Expand Down Expand Up @@ -3053,16 +3051,15 @@ namespace
STANDARD_VM_CHECK;
PRECONDITION(pMD != NULL);
PRECONDITION(pMD->IsPInvoke());
PRECONDITION(!pMD->IsAsyncMethod());
PRECONDITION(callConv != NULL);
}
CONTRACTL_END;

CorInfoCallConvExtension callConvLocal;
IMDInternalImport* pInternalImport = pMD->GetMDImport();
CorPinvokeMap mappingFlags = pmMaxValue;
// TODO: (async) revisit and examine if this needs to be supported somehow
if (pMD->IsAsyncMethod())
ThrowHR(COR_E_NOTSUPPORTED);


HRESULT hr = pInternalImport->GetPinvokeMap(pMD->GetMemberDef(), (DWORD*)&mappingFlags, NULL /*pszImportName*/, NULL /*pmrImportDLL*/);
if (FAILED(hr))
Expand Down Expand Up @@ -3249,6 +3246,12 @@ BOOL PInvoke::MarshalingRequired(
{
STANDARD_VM_CHECK;
PRECONDITION(pMD != NULL || (!sigPointer.IsNull() && pModule != NULL));

// We should never see an async method here.
// Delegate Invoke methods should never be async.
// Async P/Invokes are not supported.
// Async UnmanagedCallersOnly methods are not supported.
PRECONDITION(pMD == NULL || !pMD->IsAsyncMethod());
}
CONTRACTL_END;

Expand Down Expand Up @@ -3324,10 +3327,6 @@ BOOL PInvoke::MarshalingRequired(
mdMethodDef methodToken = mdMethodDefNil;
if (pMD != NULL)
{
// TODO: (async) revisit and examine if this needs to be supported somehow
if (pMD->IsAsyncMethod())
ThrowHR(COR_E_NOTSUPPORTED);

methodToken = pMD->GetMemberDef();
}
CollateParamTokens(pMDImport, methodToken, numArgs - 1, pParamTokenArray);
Expand Down Expand Up @@ -6080,12 +6079,10 @@ static void GetILStubForCalli(VASigCookie* pVASigCookie, MethodDesc* pMD)

if (pMD != NULL)
{
_ASSERTE(pMD->IsPInvoke());
_ASSERTE(!pMD->IsAsyncMethod());
PInvokeStaticSigInfo sigInfo(pMD);

// TODO: (async) revisit and examine if this needs to be supported somehow
if (pMD->IsAsyncMethod())
ThrowHR(COR_E_NOTSUPPORTED);

md = pMD->GetMemberDef();
nlFlags = sigInfo.GetLinkFlags();
nlType = sigInfo.GetCharSet();
Expand Down
26 changes: 20 additions & 6 deletions src/coreclr/vm/methodtablebuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3183,6 +3183,11 @@ MethodTableBuilder::EnumerateClassMethods()
CONSISTENCY_CHECK(hr == S_OK);
type = mcPInvoke;
}

if (IsMiAsync(dwImplFlags))
{
BuildMethodTableThrowException(BFA_BAD_ASYNC_METHOD);
}
}
else if (IsMiRuntime(dwImplFlags))
{
Expand Down Expand Up @@ -3227,6 +3232,11 @@ MethodTableBuilder::EnumerateClassMethods()
}

delegateMethodsSeen |= newDelegateMethodSeen;

if (IsMiAsync(dwImplFlags))
{
BuildMethodTableThrowException(BFA_BAD_ASYNC_METHOD);
}
}
else if (hasGenericMethodArgs)
{
Expand Down Expand Up @@ -3254,6 +3264,12 @@ MethodTableBuilder::EnumerateClassMethods()
// pointer-sized field pointing to COM interop data which are
// allocated lazily when/if the MD actually gets used for interop.
type = mcComInterop;

// The interface method itself should never be marked as a runtime-async method.
if (IsMiAsync(dwImplFlags))
{
BuildMethodTableThrowException(BFA_BAD_ASYNC_METHOD);
}
}
else
#endif // !FEATURE_COMINTEROP
Expand Down Expand Up @@ -3432,15 +3448,13 @@ MethodTableBuilder::EnumerateClassMethods()
}

MethodClassification asyncVariantType = type;
#ifdef FEATURE_COMINTEROP
if (type == mcComInterop)
if (type != mcIL && type != mcInstantiated)
{
// For COM interop methods,
// we don't want to treat the async variant as a COM Interop method
// (as it isn't, it's a transient IL method).
// Don't treat the async variant of special method kinds as
// the special method kind.
// The async variant methods are always IL methods with a transient implementation.
asyncVariantType = mcIL;
}
#endif // FEATURE_COMINTEROP

Signature newMemberSig(pNewMemberSignature, cAsyncThunkMemberSignature);
pNewMethod = new (GetStackingAllocator()) bmtMDMethod(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3107,6 +3107,9 @@
<data name="NotSupported_NonReflectedType" xml:space="preserve">
<value>Not supported in a non-reflected type.</value>
</data>
<data name="InvalidProgram_AsyncMethod" xml:space="preserve">
<value>Async methods with UnmanagedCallersOnlyAttribute are invalid.</value>
</data>
<data name="InvalidProgram_NonStaticMethod" xml:space="preserve">
<value>Non-static methods with UnmanagedCallersOnlyAttribute are invalid.</value>
</data>
Expand Down
23 changes: 21 additions & 2 deletions src/tests/Interop/COM/RuntimeAsync/RuntimeAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,36 @@ static async Task TestAsyncMethod(ITaskComServer_AsDispatchOnly obj)
}
}
}

[Fact]
public static void TaskReturningPInvokeWithComMarshalling()
{
Task originalTask = new(() => {});
Task result = RunTask(originalTask);

originalTask.Start();

result.GetAwaiter().GetResult();

static async Task RunTask(Task task)
{
await RuntimeAsyncNative.PassThroughTask(task);
}
}
}

public static class RuntimeAsyncNative
{
[DllImport("RuntimeAsyncNative")]
[DllImport(nameof(RuntimeAsyncNative))]
[return: MarshalAs(UnmanagedType.U1)]
public static extern bool ValidateSlotLayoutForDefaultInterface([MarshalAs(UnmanagedType.Interface)] object comObject, int expectedIntValue, float expectedFloatValue);

[DllImport("RuntimeAsyncNative")]
[DllImport(nameof(RuntimeAsyncNative))]
[return: MarshalAs(UnmanagedType.U1)]
public static extern bool ValidateSlotLayoutForInterface([MarshalAs(UnmanagedType.Interface)] object comObject, float expectedFloatValue);

[DllImport(nameof(RuntimeAsyncNative))]
public static extern Task PassThroughTask(Task task);
}

[ComVisible(true)]
Expand Down
6 changes: 6 additions & 0 deletions src/tests/Interop/COM/RuntimeAsync/RuntimeAsyncNative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,9 @@ extern "C" DLL_EXPORT bool STDMETHODCALLTYPE ValidateSlotLayoutForInterface(IUnk

return true;
}

extern "C" DLL_EXPORT IUnknown* STDMETHODCALLTYPE PassThroughTask(IUnknown* pUnk)
{
pUnk->AddRef();
return pUnk;
}
38 changes: 38 additions & 0 deletions src/tests/Interop/UnmanagedCallersOnly/InvalidCallbacks.il
Original file line number Diff line number Diff line change
Expand Up @@ -305,3 +305,41 @@
IL_0001: ret
}
}

.class public auto ansi beforefieldinit InvalidCSharp.RuntimeAsyncUnmanagedCallersOnly
extends [System.Runtime]System.Object
{
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
.maxstack 8
ldarg.0
call instance void [System.Runtime]System.Object::.ctor()
ret
}

.method public hidebysig static
class [System.Runtime]System.Threading.Tasks.Task AsyncMethodReturningTask (
) cil managed async
{
.custom instance void [System.Runtime.InteropServices]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = (
01 00 00 00
)
.maxstack 8

IL_0001: ret
}

.method public hidebysig static
int32 AsyncMethodWithBlittableReturnType (
) cil managed async
{
.custom instance void [System.Runtime.InteropServices]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = (
01 00 00 00
)
.maxstack 8

IL_0000: ldc.i4.0
IL_0001: ret
}
}
Loading
Loading