summaryrefslogtreecommitdiff
path: root/src/dlls/mscoree/comcallunmarshal.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/dlls/mscoree/comcallunmarshal.cpp')
-rw-r--r--src/dlls/mscoree/comcallunmarshal.cpp305
1 files changed, 305 insertions, 0 deletions
diff --git a/src/dlls/mscoree/comcallunmarshal.cpp b/src/dlls/mscoree/comcallunmarshal.cpp
new file mode 100644
index 0000000000..d0f9b7ca75
--- /dev/null
+++ b/src/dlls/mscoree/comcallunmarshal.cpp
@@ -0,0 +1,305 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: ComCallUnmarshal.cpp
+//
+
+//
+// Classes used to unmarshal all COM call wrapper IPs.
+//
+
+
+#include "stdafx.h" // Standard header.
+
+#ifdef FEATURE_COMINTEROP
+
+#include "ComCallUnmarshal.h"
+#include <utilcode.h> // Utility helpers.
+
+// For free-threaded marshaling, we must not be spoofed by out-of-process or cross-runtime marshal data.
+// Only unmarshal data that comes from our own runtime.
+extern BYTE g_UnmarshalSecret[sizeof(GUID)];
+extern bool g_fInitedUnmarshalSecret;
+
+STDMETHODIMP ComCallUnmarshal::QueryInterface(REFIID iid, void **ppv)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(ppv, NULL_OK));
+ } CONTRACTL_END;
+
+ if (!ppv)
+ return E_POINTER;
+
+ *ppv = NULL;
+ if (iid == IID_IUnknown)
+ {
+ *ppv = (IUnknown *)this;
+ AddRef();
+ } else if (iid == IID_IMarshal)
+ {
+ *ppv = (IMarshal *)this;
+ AddRef();
+ }
+ return (*ppv != NULL) ? S_OK : E_NOINTERFACE;
+}
+
+STDMETHODIMP_(ULONG) ComCallUnmarshal::AddRef(void)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+ return 2;
+}
+
+STDMETHODIMP_(ULONG) ComCallUnmarshal::Release(void)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+ return 1;
+}
+
+STDMETHODIMP ComCallUnmarshal::GetUnmarshalClass (REFIID riid, void * pv, ULONG dwDestContext,
+ void * pvDestContext, ULONG mshlflags,
+ LPCLSID pclsid)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+ // Marshal side only.
+ _ASSERTE(FALSE);
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP ComCallUnmarshal::GetMarshalSizeMax (REFIID riid, void * pv, ULONG dwDestContext,
+ void * pvDestContext, ULONG mshlflags,
+ ULONG * pSize)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+ // Marshal side only.
+ _ASSERTE(FALSE);
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP ComCallUnmarshal::MarshalInterface (LPSTREAM pStm, REFIID riid, void * pv,
+ ULONG dwDestContext, LPVOID pvDestContext,
+ ULONG mshlflags)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+ // Marshal side only.
+ _ASSERTE(FALSE);
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP ComCallUnmarshal::UnmarshalInterface (LPSTREAM pStm, REFIID riid, void ** ppvObj)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;;
+ STATIC_CONTRACT_MODE_PREEMPTIVE;
+ PRECONDITION(CheckPointer(pStm));
+ PRECONDITION(CheckPointer(ppvObj));
+ } CONTRACTL_END;
+
+ ULONG bytesRead;
+ ULONG mshlflags;
+ HRESULT hr = E_FAIL;
+
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW);
+ // The marshal code added a reference to the object, but we return a
+ // reference to the object as well, so don't change the ref count on the
+ // success path. Need to release on error paths though (if we manage to
+ // retrieve the IP, that is). If the interface was marshalled
+ // TABLESTRONG or TABLEWEAK, there is going to be a ReleaseMarshalData
+ // in the future, so we should AddRef the IP we're about to give out.
+ // Note also that OLE32 requires us to advance the stream pointer even
+ // in failure cases.
+
+ // Read the raw IP out of the marshalling stream.
+ hr = pStm->Read (ppvObj, sizeof (void *), &bytesRead);
+ if (FAILED (hr) || (bytesRead != sizeof (void *)))
+ IfFailGo(RPC_E_INVALID_DATA);
+
+ // And then the marshal flags.
+ hr = pStm->Read (&mshlflags, sizeof (ULONG), &bytesRead);
+ if (FAILED (hr) || (bytesRead != sizeof (ULONG)))
+ IfFailGo(RPC_E_INVALID_DATA);
+
+ // And then verify our secret, to be sure that cross-runtime clients aren't
+ // trying to trick us into mis-interpreting their data as a ppvObj. Note that
+ // it is guaranteed that the secret data is initialized, or else we certainly
+ // haven't written it into this buffer!
+ if (!g_fInitedUnmarshalSecret)
+ IfFailGo(E_UNEXPECTED);
+
+ BYTE secret[sizeof(GUID)];
+
+ hr = pStm->Read(secret, sizeof(secret), &bytesRead);
+ if (FAILED(hr) || (bytesRead != sizeof(secret)))
+ IfFailGo(RPC_E_INVALID_DATA);
+
+ if (memcmp(g_UnmarshalSecret, secret, sizeof(secret)) != 0)
+ IfFailGo(E_UNEXPECTED);
+
+ if (ppvObj && ((mshlflags == MSHLFLAGS_TABLESTRONG) || (mshlflags == MSHLFLAGS_TABLEWEAK)))
+ {
+ // For table access we can just QI for the correct interface (this
+ // will addref the IP, but that's OK since we need to keep an extra
+ // ref on the IP until ReleaseMarshalData is called).
+ hr = ((IUnknown *)*ppvObj)->QueryInterface(riid, ppvObj);
+ }
+ else
+ {
+ // For normal access we QI for the correct interface then release
+ // the old IP.
+ NonVMComHolder<IUnknown> pOldUnk = (IUnknown *)*ppvObj;
+ hr = pOldUnk->QueryInterface(riid, ppvObj);
+ }
+ErrExit:
+ ;
+ END_SO_INTOLERANT_CODE;
+ return hr;
+}
+
+STDMETHODIMP ComCallUnmarshal::ReleaseMarshalData (LPSTREAM pStm)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_PREEMPTIVE;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(pStm));
+ } CONTRACTL_END;
+
+ IUnknown *pUnk;
+ ULONG bytesRead;
+ ULONG mshlflags;
+ HRESULT hr = S_OK;
+
+ if (!pStm)
+ return E_POINTER;
+
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW);
+
+ // Read the raw IP out of the marshalling stream. Do this first since we
+ // need to update the stream pointer even in case of failures.
+ hr = pStm->Read (&pUnk, sizeof (pUnk), &bytesRead);
+ if (FAILED (hr) || (bytesRead != sizeof (pUnk)))
+ IfFailGo(RPC_E_INVALID_DATA);
+
+ // Now read the marshal flags.
+ hr = pStm->Read (&mshlflags, sizeof (mshlflags), &bytesRead);
+ if (FAILED (hr) || (bytesRead != sizeof (mshlflags)))
+ IfFailGo(RPC_E_INVALID_DATA);
+
+ if (!g_fInitedUnmarshalSecret)
+ {
+ IfFailGo(E_UNEXPECTED);
+ }
+
+ BYTE secret[sizeof(GUID)];
+
+ hr = pStm->Read(secret, sizeof(secret), &bytesRead);
+ if (FAILED(hr) || (bytesRead != sizeof(secret)))
+ IfFailGo(RPC_E_INVALID_DATA);
+
+ if (memcmp(g_UnmarshalSecret, secret, sizeof(secret)) != 0)
+ IfFailGo(E_UNEXPECTED);
+
+ pUnk->Release ();
+
+ErrExit:
+ ;
+ END_SO_INTOLERANT_CODE;
+ return hr;
+}
+
+STDMETHODIMP ComCallUnmarshal::DisconnectObject (ULONG dwReserved)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ // Nothing we can (or need to) do here. The client is using a raw IP to
+ // access this server, so the server shouldn't go away until the client
+ // Release()'s it.
+
+ return S_OK;
+}
+
+CComCallUnmarshalFactory::CComCallUnmarshalFactory()
+{
+ WRAPPER_NO_CONTRACT;
+}
+
+STDMETHODIMP CComCallUnmarshalFactory::QueryInterface(REFIID iid, void **ppv)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(ppv));
+ } CONTRACTL_END;
+
+ if (!ppv)
+ return E_POINTER;
+
+ *ppv = NULL;
+ if (iid == IID_IClassFactory || iid == IID_IUnknown) {
+ *ppv = (IClassFactory *)this;
+ AddRef();
+ }
+ return (*ppv != NULL) ? S_OK : E_NOINTERFACE;
+}
+
+STDMETHODIMP_(ULONG) CComCallUnmarshalFactory::AddRef(void)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ return 2;
+}
+
+STDMETHODIMP_(ULONG) CComCallUnmarshalFactory::Release(void)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ return 1;
+}
+
+STDMETHODIMP CComCallUnmarshalFactory::CreateInstance(LPUNKNOWN punkOuter, REFIID iid, LPVOID FAR *ppv)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(ppv));
+ } CONTRACTL_END;
+
+ if (!ppv)
+ return E_POINTER;
+
+ *ppv = NULL;
+
+ if (punkOuter != NULL)
+ return CLASS_E_NOAGGREGATION;
+
+ return m_Unmarshaller.QueryInterface(iid, ppv);
+}
+
+STDMETHODIMP CComCallUnmarshalFactory::LockServer(BOOL fLock)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ return S_OK;
+}
+
+#endif // FEATURE_COMINTEROP