Skip to content

Commit e7baf91

Browse files
Fix contract violations for OLE scenario. (#114609)
Rework the logic so the FCall doesn't do any allocations and defers any non-fast scenario to the slow path.
1 parent 267465c commit e7baf91

File tree

2 files changed

+76
-42
lines changed

2 files changed

+76
-42
lines changed

src/coreclr/vm/olecontexthelpers.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ LPVOID SetupOleContext()
3434
{
3535
NOTHROW;
3636
GC_TRIGGERS;
37-
MODE_ANY;
38-
ENTRY_POINT;
37+
MODE_COOPERATIVE;
3938
POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
4039
}
4140
CONTRACT_END;
@@ -48,7 +47,7 @@ LPVOID SetupOleContext()
4847
HRESULT hr = GetCurrentObjCtx(&pObjCtx);
4948
if (hr == S_OK)
5049
{
51-
SOleTlsData* _pData = (SOleTlsData *) ClrTeb::GetOleReservedPtr();
50+
SOleTlsData* _pData = (SOleTlsData *)ClrTeb::GetOleReservedPtr();
5251
if (_pData && _pData->pCurrentCtx == NULL)
5352
{
5453
_pData->pCurrentCtx = pObjCtx; // no release !!!!
@@ -58,7 +57,7 @@ LPVOID SetupOleContext()
5857
// We can't call SafeRelease here since that would transition
5958
// to preemptive GC mode which is bad since SetupOleContext is called
6059
// from places where we can't take a GC.
61-
ULONG cbRef = pObjCtx->Release();
60+
(void)pObjCtx->Release();
6261
}
6362
}
6463
}

src/coreclr/vm/stubhelpers.cpp

Lines changed: 73 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ void StubHelpers::Init()
171171

172172
#ifdef FEATURE_COMINTEROP
173173

174+
// Clears FP state on x86 for compatibility with VB6.
174175
FORCEINLINE static void GetCOMIPFromRCW_ClearFP()
175176
{
176177
LIMITED_METHOD_CONTRACT;
@@ -190,91 +191,112 @@ FORCEINLINE static void GetCOMIPFromRCW_ClearFP()
190191
#endif // TARGET_X86
191192
}
192193

193-
FORCEINLINE static SOleTlsData *GetOrCreateOleTlsData()
194+
FORCEINLINE static SOleTlsData* TryGetOleTlsData()
194195
{
195196
LIMITED_METHOD_CONTRACT;
196197

197-
SOleTlsData *pOleTlsData;
198198
#ifdef TARGET_X86
199199
// This saves 1 memory instruction over NtCurretTeb()->ReservedForOle because
200200
// NtCurrentTeb() reads _TEB.NtTib.Self which is the same as what FS:0 already
201201
// points to.
202-
pOleTlsData = (SOleTlsData *)(ULONG_PTR)__readfsdword(offsetof(TEB, ReservedForOle));
202+
return (SOleTlsData*)(ULONG_PTR)__readfsdword(offsetof(TEB, ReservedForOle));
203203
#else // TARGET_X86
204-
pOleTlsData = (SOleTlsData *)NtCurrentTeb()->ReservedForOle;
204+
return (SOleTlsData*)ClrTeb::GetOleReservedPtr();
205205
#endif // TARGET_X86
206-
if (pOleTlsData == NULL)
206+
}
207+
208+
static SOleTlsData* GetOrCreateOleTlsData()
209+
{
210+
CONTRACTL
207211
{
208-
pOleTlsData = (SOleTlsData *)SetupOleContext();
212+
NOTHROW;
213+
GC_TRIGGERS;
214+
MODE_COOPERATIVE;
209215
}
216+
CONTRACT_END;
217+
218+
SOleTlsData* pOleTlsData = TryGetOleTlsData();
219+
if (pOleTlsData == NULL)
220+
pOleTlsData = (SOleTlsData*)SetupOleContext();
221+
210222
return pOleTlsData;
211223
}
212224

213-
FORCEINLINE static IUnknown *GetCOMIPFromRCW_GetIUnknownFromRCWCache(RCW *pRCW, MethodTable * pItfMT)
225+
FORCEINLINE static void* GetCOMIPFromRCW_GetTarget(IUnknown *pUnk, CLRToCOMCallInfo *pComInfo)
214226
{
215227
LIMITED_METHOD_CONTRACT;
216228

217-
// The code in this helper is the "fast path" that used to be generated directly
218-
// to compiled ML stubs. The idea is to aim for an efficient RCW cache hit.
219-
SOleTlsData * pOleTlsData = GetOrCreateOleTlsData();
229+
LPVOID* lpVtbl = *(LPVOID **)pUnk;
230+
LPVOID tgt = lpVtbl[pComInfo->m_cachedComSlot];
231+
if (tgt != NULL)
232+
GetCOMIPFromRCW_ClearFP();
233+
234+
return tgt;
235+
}
236+
237+
FORCEINLINE static IUnknown* GetCOMIPFromRCW_GetTargetFromRCWCache(SOleTlsData* pOleTlsData, RCW* pRCW, CLRToCOMCallInfo* pComInfo, void** ppTarget)
238+
{
239+
LIMITED_METHOD_CONTRACT;
240+
_ASSERTE(pOleTlsData != NULL);
241+
_ASSERTE(pRCW != NULL);
242+
_ASSERTE(pComInfo != NULL);
243+
_ASSERTE(ppTarget != NULL);
220244

221245
// test for free-threaded after testing for context match to optimize for apartment-bound objects
222246
if (pOleTlsData->pCurrentCtx == pRCW->GetWrapperCtxCookie() || pRCW->IsFreeThreaded())
223247
{
224248
for (int i = 0; i < INTERFACE_ENTRY_CACHE_SIZE; i++)
225249
{
226-
if (pRCW->m_aInterfaceEntries[i].m_pMT == pItfMT)
250+
if (pRCW->m_aInterfaceEntries[i].m_pMT == pComInfo->m_pInterfaceMT)
227251
{
228-
return pRCW->m_aInterfaceEntries[i].m_pUnknown;
252+
IUnknown* pUnk = pRCW->m_aInterfaceEntries[i].m_pUnknown;
253+
if (pUnk != NULL)
254+
{
255+
void* targetMaybe = GetCOMIPFromRCW_GetTarget(pUnk, pComInfo);
256+
if (targetMaybe != NULL)
257+
{
258+
*ppTarget = targetMaybe;
259+
return pUnk;
260+
}
261+
}
229262
}
230263
}
231264
}
232265

233266
return NULL;
234267
}
235268

236-
FORCEINLINE static void *GetCOMIPFromRCW_GetTarget(IUnknown *pUnk, CLRToCOMCallInfo *pComInfo)
237-
{
238-
LIMITED_METHOD_CONTRACT;
239-
240-
LPVOID *lpVtbl = *(LPVOID **)pUnk;
241-
return lpVtbl[pComInfo->m_cachedComSlot];
242-
}
243-
244269
//==================================================================================================================
245270
// The GetCOMIPFromRCW helper exists in four specialized versions to optimize CLR->COM perf. Please be careful when
246271
// changing this code as one of these methods is executed as part of every CLR->COM call so every instruction counts.
247272
//==================================================================================================================
248273

249-
250274
#include <optsmallperfcritical.h>
251275

252-
// This helper can handle any CLR->COM call, it supports hosting,
253-
// and clears FP state on x86 for compatibility with VB6.
254-
FCIMPL3(IUnknown*, StubHelpers::GetCOMIPFromRCW, Object* pSrcUNSAFE, MethodDesc* pMD, void **ppTarget)
276+
// This helper can handle any CLR->COM call.
277+
FCIMPL3(IUnknown*, StubHelpers::GetCOMIPFromRCW, Object* pSrcUNSAFE, MethodDesc* pMD, void** ppTarget)
255278
{
256279
CONTRACTL
257280
{
258281
FCALL_CHECK;
259-
PRECONDITION(pMD->IsCLRToCOMCall() || pMD->IsEEImpl());
282+
PRECONDITION(pSrcUNSAFE != NULL);
283+
PRECONDITION(pMD != NULL && (pMD->IsCLRToCOMCall() || pMD->IsEEImpl()));
284+
PRECONDITION(ppTarget != NULL);
260285
}
261286
CONTRACTL_END;
262287

288+
// See the slow path as well, StubHelpers_GetCOMIPFromRCWSlow. The first part of that
289+
// function is identical to this one, but it handles the case where the OLE TLS
290+
// data hasn't been created yet.
263291
OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE);
264-
CLRToCOMCallInfo *pComInfo = CLRToCOMCallInfo::FromMethodDesc(pMD);
265-
RCW *pRCW = pSrc->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW();
292+
CLRToCOMCallInfo* pComInfo = CLRToCOMCallInfo::FromMethodDesc(pMD);
293+
RCW* pRCW = pSrc->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW();
266294
if (pRCW != NULL)
267295
{
268-
IUnknown * pUnk = GetCOMIPFromRCW_GetIUnknownFromRCWCache(pRCW, pComInfo->m_pInterfaceMT);
269-
if (pUnk != NULL)
270-
{
271-
*ppTarget = GetCOMIPFromRCW_GetTarget(pUnk, pComInfo);
272-
if (*ppTarget != NULL)
273-
{
274-
GetCOMIPFromRCW_ClearFP();
275-
return pUnk;
276-
}
277-
}
296+
// This is the "fast path" for compiled ML stubs. The idea is to aim for an efficient RCW cache hit.
297+
SOleTlsData* pOleTlsData = TryGetOleTlsData();
298+
if (pOleTlsData != NULL)
299+
return GetCOMIPFromRCW_GetTargetFromRCWCache(pOleTlsData, pRCW, pComInfo, ppTarget);
278300
}
279301
return NULL;
280302
}
@@ -296,12 +318,25 @@ extern "C" IUnknown* QCALLTYPE StubHelpers_GetCOMIPFromRCWSlow(QCall::ObjectHand
296318
OBJECTREF objRef = pSrc.Get();
297319
GCPROTECT_BEGIN(objRef);
298320

321+
// This snippet exists to enable OLE TLS data creation that isn't possible on the fast path.
322+
// It is practically identical to the StubHelpers::GetCOMIPFromRCW FCALL, but in the event the OLE TLS
323+
// data on this thread hasn't occurred yet, we will create it. Since this is the slow path, trying the
324+
// cache again isn't a problem.
325+
SOleTlsData* pOleTlsData = GetOrCreateOleTlsData(); // Ensure OLE TLS data is created.
299326
CLRToCOMCallInfo* pComInfo = CLRToCOMCallInfo::FromMethodDesc(pMD);
327+
RCW* pRCW = objRef->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW();
328+
if (pRCW != NULL)
329+
{
330+
IUnknown* pUnk = GetCOMIPFromRCW_GetTargetFromRCWCache(pOleTlsData, pRCW, pComInfo, ppTarget);
331+
if (pUnk != NULL)
332+
return pUnk;
333+
}
334+
335+
// Still not in the cache and we've ensured the OLE TLS data was created.
300336
SafeComHolder<IUnknown> pRetUnk = ComObject::GetComIPFromRCWThrowing(&objRef, pComInfo->m_pInterfaceMT);
301337
*ppTarget = GetCOMIPFromRCW_GetTarget(pRetUnk, pComInfo);
302338
_ASSERTE(*ppTarget != NULL);
303339

304-
GetCOMIPFromRCW_ClearFP();
305340
pIntf = pRetUnk.Extract();
306341

307342
GCPROTECT_END();

0 commit comments

Comments
 (0)