Skip to content

Commit aeb79a4

Browse files
authored
Merge pull request #24069 from nbhuiyan/fix-24018
Fix vtable slot bounds checking for refined receiver types
2 parents 640d279 + 6ccbb31 commit aeb79a4

3 files changed

Lines changed: 57 additions & 4 deletions

File tree

runtime/compiler/env/PersistentCHTable.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,22 @@ TR_ResolvedMethod *TR_PersistentCHTable::findSingleAbstractImplementer(TR_Opaque
498498
if (TR::Compiler->cls.isInterfaceClass(comp, thisClass))
499499
return 0;
500500

501+
// vftSlot is a vtable offset computed against the class the call was resolved against, whereas
502+
// thisClass is the receiver's (possibly refined) type. For an interface accessor, any
503+
// receiver type that was refined may fall outside thisClass's vtable.
504+
// Skipping this for JITServer mode, as thisClass is a client-side class pointer
505+
// that must not be dereferenced here. The per-class read happens on the client, where it is
506+
// guarded by the equivalent check in TR_J9VMBase::getResolvedVirtualMethod.
507+
if (!comp->isOutOfProcessCompilation()) {
508+
J9Class *j9thisClass = (J9Class *)TR::Compiler->cls.convertClassOffsetToClassPtr(thisClass);
509+
UDATA vTableSlot = (UDATA)comp->fej9()->virtualCallOffsetToVTableSlot(vftSlot);
510+
UDATA vTableNumMethods = J9VTABLE_HEADER_FROM_RAM_CLASS(j9thisClass)->size;
511+
UDATA firstSlot = sizeof(J9Class) + sizeof(J9VTableHeader);
512+
UDATA lastSlot = firstSlot + (0 == vTableNumMethods ? 0 : (vTableNumMethods - 1)) * sizeof(UDATA);
513+
if ((0 == vTableNumMethods) || (vTableSlot < firstSlot) || (vTableSlot > lastSlot))
514+
return 0;
515+
}
516+
501517
TR_ResolvedMethod *implArray[2]; // collect maximum 2 implementers if you can
502518
comp->enterHeuristicRegion();
503519
int32_t implCount

runtime/compiler/env/j9method.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7402,13 +7402,36 @@ TR_OpaqueMethodBlock *TR_J9VMBase::getResolvedVirtualMethod(TR_OpaqueClassBlock
74027402
if (isInterfaceClass(classObject))
74037403
return 0;
74047404

7405-
J9Method *ramMethod = *(J9Method **)((char *)TR::Compiler->cls.convertClassOffsetToClassPtr(classObject)
7406-
+ virtualCallOffsetToVTableSlot(virtualCallOffset));
7405+
J9Class *j9class = (J9Class *)TR::Compiler->cls.convertClassOffsetToClassPtr(classObject);
7406+
UDATA vTableSlot = (UDATA)virtualCallOffsetToVTableSlot(virtualCallOffset);
7407+
7408+
TR::Compilation *comp = _compInfoPT->getCompilation();
7409+
7410+
// virtualCallOffset is a vtable slot computed against the class the call was resolved
7411+
// against, while classObject is the receiver's (possibly refined) type. When those
7412+
// differ, such as when an interface accessor whose vtable slot was taken from one
7413+
// implementor which was then refined to a narrower implementor of the same interface,
7414+
// the slot can be out of range of the classObject's vtable, holding an arbitrary
7415+
// non-J9Method value (e.g. a heap object reference).
7416+
J9VTableHeader *vftHeader = J9VTABLE_HEADER_FROM_RAM_CLASS(j9class);
7417+
UDATA vTableNumMethods = vftHeader->size;
7418+
UDATA firstSlot = sizeof(J9Class) + sizeof(J9VTableHeader);
7419+
UDATA lastSlot = firstSlot + (0 == vTableNumMethods ? 0 : (vTableNumMethods - 1)) * sizeof(UDATA);
7420+
if ((0 == vTableNumMethods) || (vTableSlot < firstSlot) || (vTableSlot > lastSlot)) {
7421+
OMR::Logger *log = comp->log();
7422+
logprintf(comp->getOption(TR_TraceOptDetails), log,
7423+
"getResolvedVirtualMethod: vtable slot %d out of bounds for class %p (vtable holds %d methods, "
7424+
"valid byte range [%d..%d], virtualCallOffset=%d); failing query\n",
7425+
(int32_t)vTableSlot, (void *)j9class, (int32_t)vTableNumMethods, (int32_t)firstSlot, (int32_t)lastSlot,
7426+
virtualCallOffset);
7427+
return 0;
7428+
}
7429+
7430+
J9Method *ramMethod = *(J9Method **)((char *)j9class + vTableSlot);
74077431

74087432
TR_ASSERT(ramMethod, "getResolvedVirtualMethod should always find a ramMethod in the vtable slot");
74097433

74107434
// ramMethod might have been inherited from a superclass
7411-
TR::Compilation *comp = _compInfoPT->getCompilation();
74127435
comp->constProvenanceGraph()->addEdge(classObject, ramMethod);
74137436

74147437
if (ramMethod && (!(_jitConfig->runtimeFlags & J9JIT_RUNTIME_RESOLVE) || ignoreRtResolve)

runtime/compiler/optimizer/J9Inliner.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,21 @@ bool TR_J9VirtualCallSite::findCallSiteForAbstractClass(TR_InlinerBase *inliner)
441441
&& !comp()->getOption(TR_DisableAbstractInlining)
442442
&& (implementer
443443
= chTable->findSingleAbstractImplementer(_receiverClass, _vftSlot, _callerResolvedMethod, comp()))) {
444+
// The (_receiverClass, _vftSlot) pair can be inconsistent when the receiver type
445+
// was refined. Only devirtualize when the implementer actually matches _initialCalleeMethod.
446+
if (_initialCalleeMethod
447+
&& (implementer->nameLength() != _initialCalleeMethod->nameLength()
448+
|| implementer->signatureLength() != _initialCalleeMethod->signatureLength()
449+
|| 0 != strncmp(implementer->nameChars(), _initialCalleeMethod->nameChars(), implementer->nameLength())
450+
|| 0
451+
!= strncmp(implementer->signatureChars(), _initialCalleeMethod->signatureChars(),
452+
implementer->signatureLength()))) {
453+
heuristicTrace(inliner->tracer(),
454+
"Abstract implementer %p does not match intended callee %p (name/signature mismatch); not "
455+
"devirtualizing",
456+
implementer, _initialCalleeMethod);
457+
return false;
458+
}
444459
heuristicTrace(inliner->tracer(), "Found a single Abstract Implementer %p, signature = %s", implementer,
445460
inliner->tracer()->traceSignature(implementer));
446461
TR_VirtualGuardSelection *guard
@@ -1358,4 +1373,3 @@ bool TR_ProfileableCallSite::findProfiledCallTargets(TR_CallStack *callStack, TR
13581373

13591374
return numTargets();
13601375
}
1361-

0 commit comments

Comments
 (0)