microsoft / cppwinrt

C++/WinRT
MIT License
1.64k stars 236 forks source link

Bug: create_instance CFG violation on ARM64EC due to WINRT_IMPL_LINK #1335

Closed kobykahane closed 1 year ago

kobykahane commented 1 year ago

Version

v2.0.230706.1

Summary

I am unsure whether the following is a cppwinrt bug or a compiler bug.

When compiling a program that targets ARM64EC (i.e., with MSVC 2022 17.6.5, with the ARM64 target compiler, with the /arm64EC compiler switch specified) and that has Control Flow Guard enforcement enabled (/guard:cf) and that program invokes WINRT_IMPL_CoCreateInstance, the program fastfails with a CFG violation:

    ntdll.dll!RtlFailFast2()
    ntdll.dll!#RtlpHandleInvalidUserCallTarget()
    ntdll.dll!#LdrpHandleInvalidUserCallTargetEC()
    repro.exe!winrt::impl::capture_to<IContextCallback,int (__cdecl*)(winrt::guid const &,void *,unsigned int,winrt::guid const &,void * *) noexcept,winrt::guid const &,void * &,unsigned int &>(void * * result, int(*)(const winrt::guid &, void *, unsigned int, const winrt::guid &, void * *) noexcept function, const winrt::guid & <args_0>, void * & <args_1>, unsigned int & <args_2>) Line 2357
    repro.exe!winrt::capture<IContextCallback,int (__cdecl&)(winrt::guid const &,void *,unsigned int,winrt::guid const &,void * *) noexcept,winrt::guid const &,void * &,unsigned int &>(int(*)(const winrt::guid &, void *, unsigned int, const winrt::guid &, void * *) noexcept <args_0>, const winrt::guid & <args_1>, void * & <args_2>, unsigned int & <args_3>) Line 2596
>   repro.exe!winrt::create_instance<IContextCallback>(const winrt::guid & clsid, unsigned int context, void * outer) Line 6625
    repro.exe!main() Line 10

This does not occur if the program targets normal ARM64 instead of ARM64EC and does not occur if the program is built without CFG.

I've managed to narrow this down to WINRT_IMPL_LINK's definition of the alternate name. The following program also crashes in a similar way:

#include <Windows.h>
#include <objbase.h>
#include <ctxtcall.h>

extern "C" HRESULT __stdcall MyCoCreateInstance(
    REFCLSID rclsid, LPUNKNOWN pUnkOuter,
    DWORD dwClsContext, REFIID riid, LPVOID* ppv);

#pragma comment(linker, "/alternatename:#MyCoCreateInstance=#CoCreateInstance")

int main()
{
    CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    IContextCallback* ip;
    auto p = &MyCoCreateInstance;
    p(CLSID_ContextSwitcher, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&ip));

}

(note that there's no significance to the specific COM CLSID and IID being used) However, if instead the alternate name is changed to either:

#pragma comment(linker, "/alternatename:MyCoCreateInstance=#CoCreateInstance")

(i.e., without the leading hash in the first part, but with the hash in prefix for the actual import name) or:

#pragma comment(linker, "/alternatename:MyCoCreateInstance=CoCreateInstance")

(i.e., no hashes at all, identically to what WINRT_IMPL_LINK would normally do on x64) then the program compiles and links successfully, successfully invokes CoCreateInstance through the pointer and does not trigger a CFG violation on ARM64EC.

Reproducible example

#include <Unknwn.h>
#include <ctxtcall.h>
#include <winrt/base.h>

int main()
{
    winrt::init_apartment();
    auto p = winrt::create_instance<IContextCallback>(CLSID_ContextSwitcher);

    return 0;
}

Expected behavior

Programs using winrt::create_instance built for ARM64EC with CFG enabled should not crash with a CFG violation.

Actual behavior

Programs using winrt::create_instance built for ARM64EC with CFG enabled do crash with a CFG violation.

Additional comments

Compiler path:

C:\>where cl
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.36.32532\bin\HostARM64\arm64\cl.exe

Compiler command line for repros:

cl /Zi /EHsc /std:c++20 /permissive- /arm64EC /guard:cf repro.cpp ole32.lib
kennykerr commented 1 year ago

This was originally added in #556 at the request of @mcfi - I believe it is being used by the OS where CFG is enabled, so to the best of my knowledge it is working correctly, provided a compatible compiler and target configuration. It is also possible that you're hitting a compiler bug. Hopefully @mcfi can confirm.

kennykerr commented 1 year ago

Confirmed this is a known bug. The (internal) reference is DevDiv bug 1846207.

kobykahane commented 1 year ago

Thanks @kennykerr!

kennykerr commented 1 year ago

By the way, thanks for the clear repro. That helped triage a similar active bug internally.