Ignore:
Timestamp:
Mar 11, 2018, 2:09:20 PM (7 years ago)
Author:
[email protected]
Message:

Split DirectArguments into JSValueOOB and JSValueStrict parts
https://bugs.webkit.org/show_bug.cgi?id=183458

Reviewed by Yusuke Suzuki.
Source/JavaScriptCore:


Our Spectre plan for JSValue objects is to allow inline JSValue stores and loads guarded by
unmitigated structure checks. This works because objects reachable from JSValues (i.e. JSValue
objects, like String, Symbol, and any descendant of JSObject) will only contain fields that it's OK
to read and write within a Spectre mitigation window. Writes are important, because within the
window, a write could appear to be made speculatively and rolled out later. This means that:

  • JSValue objects cannot have lengths, masks, or anything else inline.


  • JSValue objects cannot have an inline type that is used as part of a Spectre mitigation for a type check, unless that type is in the form of a poison key.


This means that the dynamic poisoning that I previously landed for DirectArguments is wrong. It also
means that it's wrong for DirectArguments to have an inline length.

This changes DirectArguments to use poisoning according to the universal formula:

  • The random accessed portions are out-of-line, pointed to by a poisoned pointer.


  • No inline length.


Surprisingly, this is perf-neutral. It's probably perf-neutral because our compiler optimizations
amortize whatever cost there was.

  • bytecode/AccessCase.cpp:

(JSC::AccessCase::generateWithGuard):

  • dfg/DFGCallCreateDirectArgumentsSlowPathGenerator.h:

(JSC::DFG::CallCreateDirectArgumentsSlowPathGenerator::CallCreateDirectArgumentsSlowPathGenerator):

  • dfg/DFGCallCreateDirectArgumentsWithKnownLengthSlowPathGenerator.h: Added.

(JSC::DFG::CallCreateDirectArgumentsWithKnownLengthSlowPathGenerator::CallCreateDirectArgumentsWithKnownLengthSlowPathGenerator):

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compileGetByValOnDirectArguments):
(JSC::DFG::SpeculativeJIT::compileGetArrayLength):
(JSC::DFG::SpeculativeJIT::compileCreateDirectArguments):
(JSC::DFG::SpeculativeJIT::compileGetFromArguments):
(JSC::DFG::SpeculativeJIT::compilePutToArguments):

  • ftl/FTLAbstractHeapRepository.h:
  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileGetArrayLength):
(JSC::FTL::DFG::LowerDFGToB3::compileGetByVal):
(JSC::FTL::DFG::LowerDFGToB3::compileCreateDirectArguments):
(JSC::FTL::DFG::LowerDFGToB3::compileGetFromArguments):
(JSC::FTL::DFG::LowerDFGToB3::compilePutToArguments):
(JSC::FTL::DFG::LowerDFGToB3::compileCheckSubClass):
(JSC::FTL::DFG::LowerDFGToB3::allocateVariableSizedHeapCell):
(JSC::FTL::DFG::LowerDFGToB3::dynamicPoison): Deleted.
(JSC::FTL::DFG::LowerDFGToB3::dynamicPoisonOnLoadedType): Deleted.
(JSC::FTL::DFG::LowerDFGToB3::dynamicPoisonOnType): Deleted.

  • heap/SecurityKind.h:
  • jit/JITPropertyAccess.cpp:

(JSC::JIT::emit_op_get_from_arguments):
(JSC::JIT::emit_op_put_to_arguments):
(JSC::JIT::emitDirectArgumentsGetByVal):

  • jit/JITPropertyAccess32_64.cpp:

(JSC::JIT::emit_op_get_from_arguments):
(JSC::JIT::emit_op_put_to_arguments):

  • llint/LowLevelInterpreter.asm:
  • llint/LowLevelInterpreter32_64.asm:
  • llint/LowLevelInterpreter64.asm:
  • runtime/DirectArguments.cpp:

(JSC::DirectArguments::DirectArguments):
(JSC::DirectArguments::createUninitialized):
(JSC::DirectArguments::create):
(JSC::DirectArguments::createByCopying):
(JSC::DirectArguments::estimatedSize):
(JSC::DirectArguments::visitChildren):
(JSC::DirectArguments::overrideThings):
(JSC::DirectArguments::copyToArguments):
(JSC::DirectArguments::mappedArgumentsSize):

  • runtime/DirectArguments.h:
  • runtime/JSCPoison.h:
  • runtime/JSLexicalEnvironment.h:
  • runtime/JSSymbolTableObject.h:
  • runtime/VM.cpp:

(JSC::VM::VM):

  • runtime/VM.h:

Source/WTF:

  • wtf/MathExtras.h:

(WTF::dynamicPoison): Deleted.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/DirectArguments.cpp

    r227617 r229518  
    3737const ClassInfo DirectArguments::s_info = { "Arguments", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(DirectArguments) };
    3838
    39 DirectArguments::DirectArguments(VM& vm, Structure* structure, unsigned length, unsigned capacity)
     39DirectArguments::DirectArguments(VM& vm, Structure* structure, WriteBarrier<Unknown>* storage)
    4040    : GenericArguments(vm, structure)
    41     , m_length(length)
    42     , m_minCapacity(capacity)
     41    , m_storage(vm, this, storage)
    4342{
    4443    // When we construct the object from C++ code, we expect the capacity to be at least as large as
    4544    // length. JIT-allocated DirectArguments objects play evil tricks, though.
    46     ASSERT(capacity >= length);
     45    ASSERT(storageHeader(storage).minCapacity >= storageHeader(storage).length);
    4746}
    4847
     
    5049    VM& vm, Structure* structure, unsigned length, unsigned capacity)
    5150{
     51    void* rawStoragePtr = vm.jsValueGigacageAuxiliarySpace.allocateNonVirtual(
     52        vm, storageSize(capacity), nullptr, AllocationFailureMode::Assert);
     53    WriteBarrier<Unknown>* storage = static_cast<WriteBarrier<Unknown>*>(rawStoragePtr) + 1;
     54    storageHeader(storage).length = length;
     55    storageHeader(storage).minCapacity = capacity;
     56   
    5257    DirectArguments* result =
    53         new (NotNull, allocateCell<DirectArguments>(vm.heap, allocationSize(capacity)))
    54         DirectArguments(vm, structure, length, capacity);
     58        new (NotNull, allocateCell<DirectArguments>(vm.heap))
     59        DirectArguments(vm, structure, storage);
    5560    result->finishCreation(vm);
    5661    return result;
     
    6065{
    6166    DirectArguments* result = createUninitialized(vm, structure, length, capacity);
    62    
     67
     68    WriteBarrier<Unknown>* storage = result->storage();
    6369    for (unsigned i = capacity; i--;)
    64         result->storage()[i].clear();
     70        storage[i].clear();
    6571   
    6672    return result;
     
    7682        vm, exec->lexicalGlobalObject()->directArgumentsStructure(), length, capacity);
    7783   
     84    WriteBarrier<Unknown>* storage = result->storage();
    7885    for (unsigned i = capacity; i--;)
    79         result->storage()[i].set(vm, result, exec->getArgumentUnsafe(i));
     86        storage[i].set(vm, result, exec->getArgumentUnsafe(i));
    8087   
    8188    result->callee().set(vm, result, jsCast<JSFunction*>(exec->jsCallee()));
     
    8895    DirectArguments* thisObject = jsCast<DirectArguments*>(cell);
    8996    size_t mappedArgumentsSize = thisObject->m_mappedArguments ? thisObject->mappedArgumentsSize() * sizeof(bool) : 0;
    90     size_t modifiedArgumentsSize = thisObject->m_modifiedArgumentsDescriptor ? thisObject->m_length * sizeof(bool) : 0;
     97    size_t modifiedArgumentsSize = thisObject->m_modifiedArgumentsDescriptor ? thisObject->storageHeader().length * sizeof(bool) : 0;
    9198    return Base::estimatedSize(cell) + mappedArgumentsSize + modifiedArgumentsSize;
    9299}
     
    98105    Base::visitChildren(thisObject, visitor);
    99106
    100     visitor.appendValues(thisObject->storage(), std::max(thisObject->m_length, thisObject->m_minCapacity));
     107    visitor.markAuxiliary(&thisObject->storageHeader());
     108    visitor.appendValues(thisObject->storage(), std::max(thisObject->storageHeader().length, thisObject->storageHeader().minCapacity));
     109   
    101110    visitor.append(thisObject->m_callee);
    102111
    103112    if (thisObject->m_mappedArguments)
    104113        visitor.markAuxiliary(thisObject->m_mappedArguments.get());
     114   
    105115    GenericArguments<DirectArguments>::visitChildren(thisCell, visitor);
    106116}
     
    115125    RELEASE_ASSERT(!m_mappedArguments);
    116126   
    117     putDirect(vm, vm.propertyNames->length, jsNumber(m_length), static_cast<unsigned>(PropertyAttribute::DontEnum));
     127    putDirect(vm, vm.propertyNames->length, jsNumber(storageHeader().length), static_cast<unsigned>(PropertyAttribute::DontEnum));
    118128    putDirect(vm, vm.propertyNames->callee, m_callee.get(), static_cast<unsigned>(PropertyAttribute::DontEnum));
    119129    putDirect(vm, vm.propertyNames->iteratorSymbol, globalObject()->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
     
    122132    bool* overrides = static_cast<bool*>(backingStore);
    123133    m_mappedArguments.set(vm, this, overrides);
    124     for (unsigned i = m_length; i--;)
     134    for (unsigned i = storageHeader().length; i--;)
    125135        overrides[i] = false;
    126136}
     
    141151{
    142152    if (!m_mappedArguments) {
    143         unsigned limit = std::min(length + offset, m_length);
     153        unsigned limit = std::min(length + offset, storageHeader().length);
    144154        unsigned i;
    145155        VirtualRegister start = firstElementDest - offset;
     156        WriteBarrier<Unknown>* storage = this->storage();
    146157        for (i = offset; i < limit; ++i)
    147             exec->r(start + i) = storage()[i].get();
     158            exec->r(start + i) = storage[i].get();
    148159        for (; i < length; ++i)
    149160            exec->r(start + i) = get(exec, i);
     
    159170    // still allocate so that m_mappedArguments is non-null. We use that to indicate that the other properties
    160171    // (length, etc) are overridden.
    161     return WTF::roundUpToMultipleOf<8>(m_length ? m_length : 1);
     172    return WTF::roundUpToMultipleOf<8>(storageHeader().length ? storageHeader().length : 1);
    162173}
    163174
Note: See TracChangeset for help on using the changeset viewer.