Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/coreclr/vm/callstubgenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2500,7 +2500,7 @@ void CallStubGenerator::ComputeCallStub(MetaSig &sig, PCODE *pRoutines, MethodDe
PInvoke::GetCallingConvention_IgnoreErrors(pMD, &unmanagedCallConv, NULL);
hasUnmanagedCallConv = true;
}
else if (pMD != NULL && pMD->HasUnmanagedCallersOnlyAttribute())
else if (pMD != NULL && pMD->HasUnmanagedCallersOnlyAttribute() && !pMD->IsILStub()) // NOTE: IL stubs don't actually have an UnmanagedCallersOnly attribute, even though the HasUnmanagedCallersOnlyAttribute method may return true for them.
{
if (CallConv::TryGetCallingConventionFromUnmanagedCallersOnly(pMD, &unmanagedCallConv))
{
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/vm/comdelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1717,6 +1717,11 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q
if (Nullable::IsNullableType(pMeth->GetMethodTable()))
COMPlusThrow(kNotSupportedException);

// Do not allow static methods with [UnmanagedCallersOnlyAttribute] to be a delegate target.
Copy link
Member

Choose a reason for hiding this comment

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

Most delegate constructor go via optimized path, so this is incomplete.

Our strategy has been to delay this check until the UnmanagedCallersOnly method is actually called and produce a fail-fast with a good error message in that case:

void ReversePInvokeBadTransition()

Copy link
Member Author

Choose a reason for hiding this comment

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

The optimized path already has this check. The question is should the slow path ALSO have the check.

Copy link
Member Author

Choose a reason for hiding this comment

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

The optimized path already has this check. The question is should the slow path ALSO have the check.

Copy link
Member

Choose a reason for hiding this comment

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

Where is this check on the optimized path?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's in the optimized delegate creation path called by the jit. I could replicate that if needed in the interpreter compiler. Search for the comment. I copied it verbatim.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's in the optimized delegate creation path called by the jit. I could replicate that if needed in the interpreter compiler. Search for the comment. I copied it verbatim.

Copy link
Member

Choose a reason for hiding this comment

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

I see it now. I think it is an oversight that we check for it on the fast path. It is inconsistent with the lazy check that we do everywhere else. For example, you can create delegate pointing to UMCO via reflection just fine (it will fail once you try to call it):

public class Program
{
    static void Main()
    {
        typeof(Program).GetMethod("Test").CreateDelegate<Action>();
    }

    [UnmanagedCallersOnly]
    public static void Test() { }
}

// A method marked UnmanagedCallersOnly is special and allowing it to be delegate target will destabilize the runtime.
if (pMeth->HasUnmanagedCallersOnlyAttribute())
COMPlusThrow(kNotSupportedException);

MethodDesc* pDelegateInvoke = COMDelegate::FindDelegateInvokeMethod(pDelMT);

UINT invokeArgCount = MethodDescToNumFixedArgs(pDelegateInvoke);
Expand Down
2 changes: 1 addition & 1 deletion src/tests/Common/CoreCLRTestLibrary/PlatformDetection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public static class PlatformDetection

public static bool IsWindows => OperatingSystem.IsWindows();

public static bool IsBuiltInComEnabled => IsWindows
public static bool IsBuiltInComEnabled => IsWindows && !Utilities.IsCoreClrInterpreter
&& (AppContext.TryGetSwitch("System.Runtime.InteropServices.BuiltInComInterop.IsSupported", out bool isEnabled)
? isEnabled
: true);
Expand Down
Loading