⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 291815 in webkit


Ignore:
Timestamp:
Mar 24, 2022, 2:41:42 PM (4 years ago)
Author:
ysuzuki@apple.com
Message:

[JSC] JSRemoteFunction thunk should materialize code-pointer
https://bugs.webkit.org/show_bug.cgi?id=238313

Reviewed by Mark Lam.

When invoking a JSRemoteFunction, we must first wrap the arguments passed to it.
The wrapping operation may trigger a GC, and GC can jettison JIT code. As a result,
even though we know that the target JSFunction has JIT code that we want to execute,
the JIT code may be jettisoned (while wrapping the arguments for it) before we get
to the call. This resulted in occasional crashes on the JSTests/stress/shadow-realm-evaluate.js test.

This patch fixes this by doing a null check on the JIT code just before calling it,
and if null (i.e. the JIT code has been jettisoned), re-materializing the JIT code
first before making the call.

  • jit/JITOperations.cpp:

(JSC::JSC_DEFINE_JIT_OPERATION):

  • jit/JITOperations.h:
  • jit/ThunkGenerators.cpp:

(JSC::remoteFunctionCallGenerator):

Location:
trunk/Source/JavaScriptCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r291785 r291815  
     12022-03-24  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] JSRemoteFunction thunk should materialize code-pointer
     4        https://bugs.webkit.org/show_bug.cgi?id=238313
     5
     6        Reviewed by Mark Lam.
     7
     8        When invoking a JSRemoteFunction, we must first wrap the arguments passed to it.
     9        The wrapping operation may trigger a GC, and GC can jettison JIT code. As a result,
     10        even though we know that the target JSFunction has JIT code that we want to execute,
     11        the JIT code may be jettisoned (while wrapping the arguments for it) before we get
     12        to the call. This resulted in occasional crashes on the JSTests/stress/shadow-realm-evaluate.js test.
     13
     14        This patch fixes this by doing a null check on the JIT code just before calling it,
     15        and if null (i.e. the JIT code has been jettisoned), re-materializing the JIT code
     16        first before making the call.
     17
     18        * jit/JITOperations.cpp:
     19        (JSC::JSC_DEFINE_JIT_OPERATION):
     20        * jit/JITOperations.h:
     21        * jit/ThunkGenerators.cpp:
     22        (JSC::remoteFunctionCallGenerator):
     23
    1242022-03-23  Geza Lore  <glore@igalia.com>
    225
  • trunk/Source/JavaScriptCore/jit/JITOperations.cpp

    r291779 r291815  
    157157
    158158    RELEASE_AND_RETURN(scope, JSValue::encode(getWrappedValue(globalObject, globalObject, JSValue::decode(encodedValue))));
     159}
     160
     161JSC_DEFINE_JIT_OPERATION(operationMaterializeRemoteFunctionTargetCode, void*, (JSRemoteFunction* callee))
     162{
     163    JSGlobalObject* globalObject = callee->globalObject();
     164    VM& vm = globalObject->vm();
     165
     166    CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
     167    JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
     168    auto throwScope = DECLARE_THROW_SCOPE(vm);
     169
     170    ASSERT(isRemoteFunction(vm, callee));
     171
     172    auto* targetFunction = jsCast<JSFunction*>(callee->targetFunction()); // We call this function only when JSRemoteFunction's target is JSFunction.
     173    ExecutableBase* executable = targetFunction->executable();
     174
     175    // Force the executable to cache its arity entrypoint.
     176    {
     177        DeferTraps deferTraps(vm); // We can't jettison any code until after we link the call.
     178        if (!executable->isHostFunction()) {
     179            JSScope* scope = targetFunction->scopeUnchecked();
     180            FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable);
     181            CodeBlock* codeBlockSlot = nullptr;
     182            functionExecutable->prepareForExecution<FunctionExecutable>(vm, targetFunction, scope, CodeForCall, codeBlockSlot);
     183            RETURN_IF_EXCEPTION(throwScope, nullptr);
     184        }
     185        return executable->entrypointFor(CodeForCall, MustCheckArity).executableAddress();
     186    }
    159187}
    160188
  • trunk/Source/JavaScriptCore/jit/JITOperations.h

    r290768 r291815  
    162162JSC_DECLARE_JIT_OPERATION(operationGetWrappedValueForCaller, EncodedJSValue, (JSRemoteFunction*, EncodedJSValue));
    163163JSC_DECLARE_JIT_OPERATION(operationGetWrappedValueForTarget, EncodedJSValue, (JSRemoteFunction*, EncodedJSValue));
     164JSC_DECLARE_JIT_OPERATION(operationMaterializeRemoteFunctionTargetCode, void*, (JSRemoteFunction*));
    164165JSC_DECLARE_JIT_OPERATION(operationThrowRemoteFunctionException, EncodedJSValue, (JSRemoteFunction*));
    165166
  • trunk/Source/JavaScriptCore/jit/ThunkGenerators.cpp

    r291785 r291815  
    15031503        jit.storePtr(GPRInfo::regT1, jit.addressFor(loopIndex));
    15041504
     1505        jit.setupArguments<decltype(operationGetWrappedValueForTarget)>(GPRInfo::regT0, valueRegs);
    15051506        jit.prepareCallOperation(vm);
    1506         jit.setupArguments<decltype(operationGetWrappedValueForTarget)>(GPRInfo::regT0, valueRegs);
    15071507        jit.move(CCallHelpers::TrustedImmPtr(tagCFunction<OperationPtrTag>(operationGetWrappedValueForTarget)), GPRInfo::nonArgGPR0);
    15081508        emitPointerValidation(jit, GPRInfo::nonArgGPR0, OperationPtrTag);
     
    15251525    jit.storeCell(GPRInfo::regT2, CCallHelpers::calleeFrameSlot(CallFrameSlot::callee));
    15261526
    1527     jit.loadPtr(CCallHelpers::Address(GPRInfo::regT2, JSFunction::offsetOfExecutableOrRareData()), GPRInfo::regT0);
    1528     auto hasExecutable = jit.branchTestPtr(CCallHelpers::Zero, GPRInfo::regT0, CCallHelpers::TrustedImm32(JSFunction::rareDataTag));
    1529     jit.loadPtr(CCallHelpers::Address(GPRInfo::regT0, FunctionRareData::offsetOfExecutable() - JSFunction::rareDataTag), GPRInfo::regT0);
     1527    jit.loadPtr(CCallHelpers::Address(GPRInfo::regT2, JSFunction::offsetOfExecutableOrRareData()), GPRInfo::regT1);
     1528    auto hasExecutable = jit.branchTestPtr(CCallHelpers::Zero, GPRInfo::regT1, CCallHelpers::TrustedImm32(JSFunction::rareDataTag));
     1529    jit.loadPtr(CCallHelpers::Address(GPRInfo::regT1, FunctionRareData::offsetOfExecutable() - JSFunction::rareDataTag), GPRInfo::regT1);
    15301530    hasExecutable.link(&jit);
    15311531
    15321532    jit.loadPtr(
    15331533        CCallHelpers::Address(
    1534             GPRInfo::regT0, ExecutableBase::offsetOfJITCodeWithArityCheckFor(CodeForCall)),
    1535         GPRInfo::regT0);
    1536 
     1534            GPRInfo::regT1, ExecutableBase::offsetOfJITCodeWithArityCheckFor(CodeForCall)),
     1535        GPRInfo::regT1);
     1536    auto codeExists = jit.branchTestPtr(CCallHelpers::NonZero, GPRInfo::regT1);
     1537
     1538    // The calls to operationGetWrappedValueForTarget above may GC, and any GC can potentially jettison the JIT code in the target JSFunction.
     1539    // If we find that the JIT code is null (i.e. has been jettisoned), then we need to re-materialize it for the call below. Note that we know
     1540    // that operationMaterializeRemoteFunctionTargetCode should be able to re-materialize the JIT code (except for any OOME) because we only
     1541    // went down this code path after we found a non-null JIT code (in the noCode check) above i.e. it should be possible to materialize the JIT code.
     1542    jit.setupArguments<decltype(operationMaterializeRemoteFunctionTargetCode)>(GPRInfo::regT0);
     1543    jit.prepareCallOperation(vm);
     1544    jit.move(CCallHelpers::TrustedImmPtr(tagCFunction<OperationPtrTag>(operationMaterializeRemoteFunctionTargetCode)), GPRInfo::nonArgGPR0);
     1545    emitPointerValidation(jit, GPRInfo::nonArgGPR0, OperationPtrTag);
     1546    jit.call(GPRInfo::nonArgGPR0, OperationPtrTag);
     1547    exceptionChecks.append(jit.emitJumpIfException(vm));
     1548    jit.move(GPRInfo::returnValueGPR, GPRInfo::regT1);
     1549
     1550    codeExists.link(&jit);
    15371551    // Based on the check above, we should be good with this. On ARM64, emitPointerValidation will do this.
    15381552#if ASSERT_ENABLED && !CPU(ARM64E)
    15391553    {
    1540         CCallHelpers::Jump checkNotNull = jit.branchTestPtr(CCallHelpers::NonZero, GPRInfo::regT0);
     1554        CCallHelpers::Jump checkNotNull = jit.branchTestPtr(CCallHelpers::NonZero, GPRInfo::regT1);
    15411555        jit.abortWithReason(TGInvalidPointer);
    15421556        checkNotNull.link(&jit);
     
    15441558#endif
    15451559
    1546     emitPointerValidation(jit, GPRInfo::regT0, JSEntryPtrTag);
    1547     jit.call(GPRInfo::regT0, JSEntryPtrTag);
     1560    emitPointerValidation(jit, GPRInfo::regT1, JSEntryPtrTag);
     1561    jit.call(GPRInfo::regT1, JSEntryPtrTag);
    15481562
    15491563    // Wrap return value
     
    15581572    resultIsPrimitive.append(jit.branchIfNotObject(resultRegs.payloadGPR()));
    15591573
    1560     jit.move(CCallHelpers::TrustedImmPtr(tagCFunction<OperationPtrTag>(operationGetWrappedValueForCaller)), GPRInfo::nonArgGPR0);
    1561     emitPointerValidation(jit, GPRInfo::nonArgGPR0, OperationPtrTag);
    1562 
    15631574    jit.loadCell(CCallHelpers::addressFor(CallFrameSlot::callee), GPRInfo::regT2);
    15641575    jit.setupArguments<decltype(operationGetWrappedValueForCaller)>(GPRInfo::regT2, resultRegs);
    15651576    jit.prepareCallOperation(vm);
     1577    jit.move(CCallHelpers::TrustedImmPtr(tagCFunction<OperationPtrTag>(operationGetWrappedValueForCaller)), GPRInfo::nonArgGPR0);
     1578    emitPointerValidation(jit, GPRInfo::nonArgGPR0, OperationPtrTag);
    15661579    jit.call(GPRInfo::nonArgGPR0, OperationPtrTag);
    15671580    exceptionChecks.append(jit.emitJumpIfException(vm));
Note: See TracChangeset for help on using the changeset viewer.