Skip to content

Commit f7b310c

Browse files
davidwrightonCopilotjanvorli
authored
[clr-interp] allow modification of the this pointer in functions which use it as the generics context (#119554)
* [clr-interp] allow modification of the this pointer in functions which use it as the generics context - When the this pointer is used as the generics context, if some code modifies the this pointer, do not allow that modification to affect the generics behavior of the function - Implement this by making a shadow copy of the this pointer in this situation - To avoid doing this ALL of the time, I've added a scheme where we can restart the entire method compilation by setting a flag which describes the current set of retry rules. * Update src/coreclr/interpreter/compiler.cpp Co-authored-by: Copilot <[email protected]> * Update src/coreclr/interpreter/compiler.cpp Co-authored-by: Copilot <[email protected]> * Update src/coreclr/interpreter/compiler.cpp Co-authored-by: Copilot <[email protected]> * Add null check and throw in the logic for late bound resolve a generic exception * Use instruction injection instead of compiler restart for the mov handling * Update src/coreclr/interpreter/compiler.cpp Co-authored-by: Copilot <[email protected]> * Update compiler.cpp Address feedback from @kg * Update src/coreclr/interpreter/compiler.cpp Co-authored-by: Jan Vorlicek <[email protected]> --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: Jan Vorlicek <[email protected]>
1 parent f0712f1 commit f7b310c

File tree

3 files changed

+70
-2
lines changed

3 files changed

+70
-2
lines changed

src/coreclr/interpreter/compiler.cpp

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,15 @@ void InterpCompiler::BuildGCInfo(InterpMethod *pInterpMethod)
12851285
? (GcSlotFlags)GC_SLOT_UNTRACKED
12861286
: (GcSlotFlags)(GC_SLOT_INTERIOR | GC_SLOT_PINNED);
12871287

1288+
// The 'this' pointer may have a shadow copy, or it may not. If it does not have a shadow copy, handle accordingly below.
1289+
if (m_shadowCopyOfThisPointerHasVar && !m_shadowCopyOfThisPointerActuallyNeeded)
1290+
{
1291+
// Don't report the original this pointer var, we'll report the shadow copy instead
1292+
// which is forced to have the same offset
1293+
if (i == 0)
1294+
continue;
1295+
}
1296+
12881297
switch (pVar->interpType) {
12891298
case InterpTypeO:
12901299
break;
@@ -1670,6 +1679,12 @@ void InterpCompiler::CreateILVars()
16701679
{
16711680
bool hasThis = m_methodInfo->args.hasThis();
16721681
bool hasParamArg = m_methodInfo->args.hasTypeArg();
1682+
bool hasThisPointerShadowCopyAsParamIndex = false;
1683+
if (((m_methodInfo->options & CORINFO_GENERICS_CTXT_MASK) == CORINFO_GENERICS_CTXT_FROM_THIS))
1684+
{
1685+
hasThisPointerShadowCopyAsParamIndex = true;
1686+
m_shadowCopyOfThisPointerHasVar = true;
1687+
}
16731688
int paramArgIndex = hasParamArg ? hasThis ? 1 : 0 : INT_MAX;
16741689
int32_t offset;
16751690
int numArgs = hasThis + m_methodInfo->args.numArgs;
@@ -1679,7 +1694,7 @@ void InterpCompiler::CreateILVars()
16791694
// add some starting extra space for new vars
16801695
m_varsCapacity = m_numILVars + m_methodInfo->EHcount + 64;
16811696
m_pVars = (InterpVar*)AllocTemporary0(m_varsCapacity * sizeof (InterpVar));
1682-
m_varsSize = m_numILVars + hasParamArg;
1697+
m_varsSize = m_numILVars + hasParamArg + (hasThisPointerShadowCopyAsParamIndex ? 1 : 0);
16831698

16841699
offset = 0;
16851700

@@ -1700,7 +1715,13 @@ void InterpCompiler::CreateILVars()
17001715
{
17011716
CORINFO_CLASS_HANDLE argClass = m_compHnd->getMethodClass(m_methodInfo->ftn);
17021717
InterpType interpType = m_compHnd->isValueClass(argClass) ? InterpTypeByRef : InterpTypeO;
1703-
CreateNextLocalVar(0, argClass, interpType, &offset);
1718+
if (hasThisPointerShadowCopyAsParamIndex)
1719+
{
1720+
assert(interpType == InterpTypeO);
1721+
assert(!hasParamArg); // We don't support both a param arg and a this pointer shadow copy
1722+
m_paramArgIndex = m_varsSize - 1; // The param arg is stored after the IL locals in the m_pVars array
1723+
}
1724+
CreateNextLocalVar(hasThisPointerShadowCopyAsParamIndex ? m_paramArgIndex : 0, argClass, interpType, &offset);
17041725
argIndexOffset++;
17051726
}
17061727

@@ -1742,6 +1763,14 @@ void InterpCompiler::CreateILVars()
17421763
index++;
17431764
}
17441765

1766+
if (hasThisPointerShadowCopyAsParamIndex)
1767+
{
1768+
// This is the allocation of memory space for the shadow copy of the this pointer, operations like starg 0, and ldarga 0 will operate on this copy
1769+
CORINFO_CLASS_HANDLE argClass = m_compHnd->getMethodClass(m_methodInfo->ftn);
1770+
CreateNextLocalVar(0, argClass, InterpTypeO, &offset);
1771+
index++; // We need to account for the shadow copy in the variable index
1772+
}
1773+
17451774
offset = ALIGN_UP_TO(offset, INTERP_STACK_ALIGNMENT);
17461775
m_ILLocalsSize = offset - m_ILLocalsOffset;
17471776

@@ -2189,6 +2218,12 @@ void InterpCompiler::EmitLoadVar(int32_t var)
21892218

21902219
void InterpCompiler::EmitStoreVar(int32_t var)
21912220
{
2221+
// Check for the unusual case of storing to the this pointer variable within a generic method which uses the this pointer as a generic context arg
2222+
if (var == 0 && m_shadowCopyOfThisPointerHasVar)
2223+
{
2224+
m_shadowCopyOfThisPointerActuallyNeeded = true;
2225+
}
2226+
21922227
InterpType interpType = m_pVars[var].interpType;
21932228
CHECK_STACK(1);
21942229

@@ -3840,6 +3875,12 @@ void InterpCompiler::EmitStaticFieldAccess(InterpType interpFieldType, CORINFO_F
38403875

38413876
void InterpCompiler::EmitLdLocA(int32_t var)
38423877
{
3878+
// Check for the unusual case of taking the address of the this pointer variable within a generic method which uses the this pointer as a generic context arg
3879+
if (var == 0 && m_shadowCopyOfThisPointerHasVar)
3880+
{
3881+
m_shadowCopyOfThisPointerActuallyNeeded = true;
3882+
}
3883+
38433884
if (m_pCBB->clauseType == BBClauseFilter)
38443885
{
38453886
AddIns(INTOP_LOAD_FRAMEVAR);
@@ -6698,6 +6739,27 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
66986739
goto retry_emit;
66996740
}
67006741

6742+
6743+
if (m_shadowCopyOfThisPointerActuallyNeeded)
6744+
{
6745+
InterpInst *pFirstIns = FirstRealIns(m_pEntryBB);
6746+
InterpInst *pMovInst = InsertIns(pFirstIns, INTOP_MOV_P);
6747+
// If this flag is set, then we need to handle the case where an IL instruction may change the
6748+
// value of the this pointer during the execution of the method. We do that by operating on a
6749+
// local copy of the this pointer instead of the original this pointer for most operations.
6750+
6751+
// When this mitigation is enabled, the param arg is the actual input this pointer,
6752+
// and the arg 0 is the local copy of the this pointer.
6753+
pMovInst->SetSVar(getParamArgIndex());
6754+
pMovInst->SetDVar(0);
6755+
pMovInst->ilOffset = pFirstIns->ilOffset; // Match the IL offset of the first real instruction
6756+
}
6757+
else if (m_shadowCopyOfThisPointerHasVar)
6758+
{
6759+
m_pVars[0].offset = 0; // Reset the this pointer offset to 0, since we won't actually need the shadow copy of the this pointer
6760+
// This will result in an unused local var slot on the stack, and two "var"s which point to the same location.
6761+
}
6762+
67016763
UnlinkUnreachableBBlocks();
67026764
}
67036765

src/coreclr/interpreter/compiler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,8 @@ class InterpCompiler
674674
// For each catch or filter clause, we create a variable that holds the exception object.
675675
// This is the index of the first such variable.
676676
int32_t m_clauseVarsIndex = 0;
677+
bool m_shadowCopyOfThisPointerActuallyNeeded = false;
678+
bool m_shadowCopyOfThisPointerHasVar = false;
677679

678680
int32_t CreateVarExplicit(InterpType interpType, CORINFO_CLASS_HANDLE clsHnd, int size);
679681

src/coreclr/vm/codeman.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3398,6 +3398,10 @@ TypeHandle InterpreterJitManager::ResolveEHClause(EE_ILEXCEPTION_CLAUSE* pEHClau
33983398
_ASSERTE(paramContextType == GENERIC_PARAM_CONTEXT_THIS);
33993399
GCX_COOP();
34003400
declaringType = (TypeHandle)dac_cast<PTR_MethodTable>(pCf->GetExactGenericArgsToken());
3401+
if (declaringType.IsNull())
3402+
{
3403+
COMPlusThrow(kNullReferenceException, W("NullReference_This"));
3404+
}
34013405
}
34023406

34033407
_ASSERTE(!declaringType.IsNull());

0 commit comments

Comments
 (0)