Changeset 218868 in webkit


Ignore:
Timestamp:
Jun 27, 2017 11:42:13 PM (7 years ago)
Author:
jfbastien@apple.com
Message:

WebAssembly: running out of executable memory should throw OoM
https://bugs.webkit.org/show_bug.cgi?id=171537
<rdar://problem/32963338>

Reviewed by Saam Barati.

JSTests:

  • wasm.yaml:
  • wasm/lowExecutableMemory/executable-memory-oom.js: Added.

(const.invoke):
(failCount.0.catch):
(failCount.0.module.undefined.catch):

  • wasm/lowExecutableMemory/exports-oom.js: Added.

(const.type):
(const.params):
(const.randomProgram):
(failCount.0.catch):
(failCount.0.module.undefined.catch):

  • wasm/lowExecutableMemory/imports-oom.js: Added.

(const.type):
(const.params):
(const.randomProgram):
(f.imports.push):
(failCount.0.catch):
(failCount.0.module.undefined.catch):

Source/JavaScriptCore:

Both on first compile with BBQ as well as on tier-up with OMG,
running out of X memory shouldn't cause the entire program to
terminate. An exception will do when compiling initial code (since
we don't have any other fallback at the moment), and refusal to
tier up will do as well (it'll just be slower).

This is useful because programs which generate huge amounts of
code simply look like crashes, which developers report to
us. Getting a JavaScript exception instead is much clearer.

  • jit/ExecutableAllocator.cpp:

(JSC::ExecutableAllocator::allocate):

  • llint/LLIntSlowPaths.cpp:

(JSC::LLInt::shouldJIT):

  • runtime/Options.h:
  • wasm/WasmBBQPlan.cpp:

(JSC::Wasm::BBQPlan::prepare):
(JSC::Wasm::BBQPlan::complete):

  • wasm/WasmBinding.cpp:

(JSC::Wasm::wasmToJs):
(JSC::Wasm::wasmToWasm):

  • wasm/WasmBinding.h:
  • wasm/WasmOMGPlan.cpp:

(JSC::Wasm::OMGPlan::work):

  • wasm/js/JSWebAssemblyCodeBlock.cpp:

(JSC::JSWebAssemblyCodeBlock::JSWebAssemblyCodeBlock):

  • wasm/js/JSWebAssemblyCodeBlock.h:
  • wasm/js/JSWebAssemblyInstance.cpp:

(JSC::JSWebAssemblyInstance::finalizeCreation):

Tools:

  • Scripts/run-jsc-stress-tests: add a configuration which runs the

tests under limited executable memory and avoids non-WebAssembly
code generation so that we more reliably run out of executable
memory in WebAssembly.

Location:
trunk
Files:
4 added
15 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r218861 r218868  
     12017-06-27  JF Bastien  <jfbastien@apple.com>
     2
     3        WebAssembly: running out of executable memory should throw OoM
     4        https://bugs.webkit.org/show_bug.cgi?id=171537
     5        <rdar://problem/32963338>
     6
     7        Reviewed by Saam Barati.
     8
     9        * wasm.yaml:
     10        * wasm/lowExecutableMemory/executable-memory-oom.js: Added.
     11        (const.invoke):
     12        (failCount.0.catch):
     13        (failCount.0.module.undefined.catch):
     14        * wasm/lowExecutableMemory/exports-oom.js: Added.
     15        (const.type):
     16        (const.params):
     17        (const.randomProgram):
     18        (failCount.0.catch):
     19        (failCount.0.module.undefined.catch):
     20        * wasm/lowExecutableMemory/imports-oom.js: Added.
     21        (const.type):
     22        (const.params):
     23        (const.randomProgram):
     24        (f.imports.push):
     25        (failCount.0.catch):
     26        (failCount.0.module.undefined.catch):
     27
    1282017-06-27  Caio Lima  <ticaiolima@gmail.com>
    229
  • trunk/JSTests/wasm.yaml

    r218216 r218868  
    3434- path: wasm/stress
    3535  cmd: runWebAssembly unless parseRunCommands
     36- path: wasm/lowExecutableMemory
     37  cmd: runWebAssemblyLowExecutableMemory unless parseRunCommands
    3638
    3739- path: wasm/spec-tests/address.wast.js
  • trunk/Source/JavaScriptCore/ChangeLog

    r218867 r218868  
     12017-06-27  JF Bastien  <jfbastien@apple.com>
     2
     3        WebAssembly: running out of executable memory should throw OoM
     4        https://bugs.webkit.org/show_bug.cgi?id=171537
     5        <rdar://problem/32963338>
     6
     7        Reviewed by Saam Barati.
     8
     9        Both on first compile with BBQ as well as on tier-up with OMG,
     10        running out of X memory shouldn't cause the entire program to
     11        terminate. An exception will do when compiling initial code (since
     12        we don't have any other fallback at the moment), and refusal to
     13        tier up will do as well (it'll just be slower).
     14
     15        This is useful because programs which generate huge amounts of
     16        code simply look like crashes, which developers report to
     17        us. Getting a JavaScript exception instead is much clearer.
     18
     19        * jit/ExecutableAllocator.cpp:
     20        (JSC::ExecutableAllocator::allocate):
     21        * llint/LLIntSlowPaths.cpp:
     22        (JSC::LLInt::shouldJIT):
     23        * runtime/Options.h:
     24        * wasm/WasmBBQPlan.cpp:
     25        (JSC::Wasm::BBQPlan::prepare):
     26        (JSC::Wasm::BBQPlan::complete):
     27        * wasm/WasmBinding.cpp:
     28        (JSC::Wasm::wasmToJs):
     29        (JSC::Wasm::wasmToWasm):
     30        * wasm/WasmBinding.h:
     31        * wasm/WasmOMGPlan.cpp:
     32        (JSC::Wasm::OMGPlan::work):
     33        * wasm/js/JSWebAssemblyCodeBlock.cpp:
     34        (JSC::JSWebAssemblyCodeBlock::JSWebAssemblyCodeBlock):
     35        * wasm/js/JSWebAssemblyCodeBlock.h:
     36        * wasm/js/JSWebAssemblyInstance.cpp:
     37        (JSC::JSWebAssemblyInstance::finalizeCreation):
     38
    1392017-06-27  Saam Barati  <sbarati@apple.com>
    240
  • trunk/Source/JavaScriptCore/jit/ExecutableAllocator.cpp

    r218867 r218868  
    413413        size_t bytesAvailable = static_cast<size_t>(
    414414            statistics.bytesReserved * (1 - executablePoolReservationFraction));
    415         if (bytesAllocated > bytesAvailable)
     415        if (bytesAllocated > bytesAvailable) {
     416            if (Options::logExecutableAllocation())
     417                dataLog("Allocation failed because bytes allocated ", bytesAllocated,  " > ", bytesAvailable, " bytes available.\n");
    416418            return nullptr;
     419        }
    417420    }
    418421   
  • trunk/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp

    r218794 r218868  
    319319        return false;
    320320
    321     // You can modify this to turn off JITting without rebuilding the world.
    322     return exec->vm().canUseJIT();
     321    return exec->vm().canUseJIT() && Options::useBaselineJIT();
    323322}
    324323
  • trunk/Source/JavaScriptCore/runtime/Options.h

    r218861 r218868  
    113113    \
    114114    v(bool, useLLInt,  true, Normal, "allows the LLINT to be used if true") \
    115     v(bool, useJIT,    true, Normal, "allows the baseline JIT to be used if true") \
     115    v(bool, useJIT,    true, Normal, "allows the executable pages to be allocated for JIT and thunks if true") \
     116    v(bool, useBaselineJIT, true, Normal, "allows the baseline JIT to be used if true") \
    116117    v(bool, useDFGJIT, true, Normal, "allows the DFG JIT to be used if true") \
    117118    v(bool, useRegExpJIT, true, Normal, "allows the RegExp JIT to be used if true") \
  • trunk/Source/JavaScriptCore/wasm/WasmBBQPlan.cpp

    r218619 r218868  
    176176        unsigned importFunctionIndex = m_wasmToWasmExitStubs.size();
    177177        dataLogLnIf(verbose, "Processing import function number ", importFunctionIndex, ": ", makeString(import->module), ": ", makeString(import->field));
    178         m_wasmToWasmExitStubs.uncheckedAppend(wasmToWasm(importFunctionIndex));
     178        auto binding = wasmToWasm(importFunctionIndex);
     179        if (UNLIKELY(!binding)) {
     180            switch (binding.error()) {
     181            case BindingFailure::OutOfMemory:
     182                return fail(holdLock(m_lock), makeString("Out of executable memory at import ", String::number(importIndex)));
     183            }
     184            RELEASE_ASSERT_NOT_REACHED();
     185        }
     186        m_wasmToWasmExitStubs.uncheckedAppend(binding.value());
    179187    }
    180188
     
    289297    dataLogLnIf(verbose, "Starting Completion");
    290298
    291     if (m_state == State::Compiled) {
     299    if (!failed() && m_state == State::Compiled) {
    292300        for (uint32_t functionIndex = 0; functionIndex < m_moduleInformation->functionLocationInBinary.size(); functionIndex++) {
    293301            CompilationContext& context = m_compilationContexts[functionIndex];
    294302            SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
    295303            {
    296                 LinkBuffer linkBuffer(*context.wasmEntrypointJIT, nullptr);
     304                LinkBuffer linkBuffer(*context.wasmEntrypointJIT, nullptr, JITCompilationCanFail);
     305                if (UNLIKELY(linkBuffer.didFailToAllocate())) {
     306                    Base::fail(locker, makeString("Out of executable memory in function at index ", String::number(functionIndex)));
     307                    return;
     308                }
     309
    297310                m_wasmInternalFunctions[functionIndex]->entrypoint.compilation = std::make_unique<B3::Compilation>(
    298311                    FINALIZE_CODE(linkBuffer, ("WebAssembly function[%i] %s", functionIndex, SignatureInformation::get(signatureIndex).toString().ascii().data())),
     
    301314
    302315            if (auto jsToWasmInternalFunction = m_jsToWasmInternalFunctions.get(functionIndex)) {
    303                 LinkBuffer linkBuffer(*context.jsEntrypointJIT, nullptr);
     316                LinkBuffer linkBuffer(*context.jsEntrypointJIT, nullptr, JITCompilationCanFail);
     317                if (UNLIKELY(linkBuffer.didFailToAllocate())) {
     318                    Base::fail(locker, makeString("Out of executable memory in function entrypoint at index ", String::number(functionIndex)));
     319                    return;
     320                }
     321
    304322                jsToWasmInternalFunction->entrypoint.compilation = std::make_unique<B3::Compilation>(
    305323                    FINALIZE_CODE(linkBuffer, ("JavaScript->WebAssembly entrypoint[%i] %s", functionIndex, SignatureInformation::get(signatureIndex).toString().ascii().data())),
  • trunk/Source/JavaScriptCore/wasm/WasmBinding.cpp

    r218866 r218868  
    5252}
    5353
    54 MacroAssemblerCodeRef wasmToJs(VM* vm, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex signatureIndex, unsigned importIndex)
     54Expected<MacroAssemblerCodeRef, BindingFailure> wasmToJs(VM* vm, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex signatureIndex, unsigned importIndex)
    5555{
    5656    // FIXME: This function doesn't properly abstract away the calling convention.
     
    120120            };
    121121
    122             LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
     122            LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID, JITCompilationCanFail);
     123            if (UNLIKELY(linkBuffer.didFailToAllocate()))
     124                return makeUnexpected(BindingFailure::OutOfMemory);
     125
    123126            linkBuffer.link(call, throwBadI64);
    124127            return FINALIZE_CODE(linkBuffer, ("WebAssembly->JavaScript invalid i64 use in import[%i]", importIndex));
     
    304307        jit.ret();
    305308
    306         LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
     309        LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID, JITCompilationCanFail);
     310        if (UNLIKELY(linkBuffer.didFailToAllocate()))
     311            return makeUnexpected(BindingFailure::OutOfMemory);
     312
    307313        linkBuffer.link(call, callFunc);
    308314        linkBuffer.link(exceptionCall, doUnwinding);
     
    601607    }
    602608
    603     LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID);
     609    LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID, JITCompilationCanFail);
     610    if (UNLIKELY(patchBuffer.didFailToAllocate()))
     611        return makeUnexpected(BindingFailure::OutOfMemory);
     612
    604613    patchBuffer.link(slowCall, FunctionPtr(vm->getCTIStub(linkCallThunkGenerator).code().executableAddress()));
    605614    CodeLocationLabel callReturnLocation(patchBuffer.locationOfNearCall(slowCall));
     
    611620}
    612621
    613 MacroAssemblerCodeRef wasmToWasm(unsigned importIndex)
     622Expected<MacroAssemblerCodeRef, BindingFailure> wasmToWasm(unsigned importIndex)
    614623{
    615624    const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
     
    654663    jit.jump(scratch);
    655664
    656     LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID);
     665    LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID, JITCompilationCanFail);
     666    if (UNLIKELY(patchBuffer.didFailToAllocate()))
     667        return makeUnexpected(BindingFailure::OutOfMemory);
     668
    657669    return FINALIZE_CODE(patchBuffer, ("WebAssembly->WebAssembly import[%i]", importIndex));
    658670}
  • trunk/Source/JavaScriptCore/wasm/WasmBinding.h

    r215103 r218868  
    3232#include "WasmFormat.h"
    3333#include <wtf/Bag.h>
     34#include <wtf/Expected.h>
    3435
    3536namespace JSC {
     
    3940namespace Wasm {
    4041
    41 MacroAssemblerCodeRef wasmToWasm(unsigned importIndex);
    42 MacroAssemblerCodeRef wasmToJs(VM*, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex, unsigned importIndex);
     42enum class BindingFailure {
     43    OutOfMemory,
     44};
     45
     46Expected<MacroAssemblerCodeRef, BindingFailure> wasmToWasm(unsigned importIndex);
     47Expected<MacroAssemblerCodeRef, BindingFailure> wasmToJs(VM*, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex, unsigned importIndex);
    4348
    4449} } // namespace JSC::Wasm
  • trunk/Source/JavaScriptCore/wasm/WasmOMGPlan.cpp

    r218866 r218868  
    8989
    9090    Entrypoint omgEntrypoint;
    91     LinkBuffer linkBuffer(*context.wasmEntrypointJIT, nullptr);
     91    LinkBuffer linkBuffer(*context.wasmEntrypointJIT, nullptr, JITCompilationCanFail);
     92    if (UNLIKELY(linkBuffer.didFailToAllocate())) {
     93        Base::fail(holdLock(m_lock), makeString("Out of executable memory while tiering up function at index ", String::number(m_functionIndex)));
     94        return;
     95    }
     96
    9297    omgEntrypoint.compilation = std::make_unique<B3::Compilation>(
    9398        FINALIZE_CODE(linkBuffer, ("WebAssembly OMG function[%i] %s", m_functionIndex, SignatureInformation::get(signatureIndex).toString().ascii().data())),
  • trunk/Source/JavaScriptCore/wasm/js/JSWebAssemblyCodeBlock.cpp

    r217108 r218868  
    5959    for (unsigned importIndex = 0; importIndex < m_codeBlock->functionImportCount(); ++importIndex) {
    6060        Wasm::SignatureIndex signatureIndex = moduleInformation.importFunctionSignatureIndices.at(importIndex);
    61         m_wasmToJSExitStubs.uncheckedAppend(Wasm::wasmToJs(&vm, m_callLinkInfos, signatureIndex, importIndex));
     61        auto binding = Wasm::wasmToJs(&vm, m_callLinkInfos, signatureIndex, importIndex);
     62        if (UNLIKELY(!binding)) {
     63            switch (binding.error()) {
     64            case Wasm::BindingFailure::OutOfMemory:
     65                m_errorMessage = ASCIILiteral("Out of executable memory");
     66                return;
     67            }
     68            RELEASE_ASSERT_NOT_REACHED();
     69        }
     70        m_wasmToJSExitStubs.uncheckedAppend(binding.value());
    6271        importWasmToJSStub(importIndex) = m_wasmToJSExitStubs[importIndex].code().executableAddress();
    6372    }
  • trunk/Source/JavaScriptCore/wasm/js/JSWebAssemblyCodeBlock.h

    r218794 r218868  
    7676    Wasm::Callee& jsEntrypointCalleeFromFunctionIndexSpace(unsigned functionIndexSpace)
    7777    {
     78        ASSERT(runnable());
    7879        return m_codeBlock->jsEntrypointCalleeFromFunctionIndexSpace(functionIndexSpace);
    7980    }
    8081    Wasm::WasmEntrypointLoadLocation wasmEntrypointLoadLocationFromFunctionIndexSpace(unsigned functionIndexSpace)
    8182    {
     83        ASSERT(runnable());
    8284        return m_codeBlock->wasmEntrypointLoadLocationFromFunctionIndexSpace(functionIndexSpace);
    8385    }
     
    8587    Wasm::WasmEntrypointLoadLocation wasmToJsCallStubForImport(unsigned importIndex)
    8688    {
     89        ASSERT(runnable());
    8790        return &importWasmToJSStub(importIndex);
    8891    }
     
    9699
    97100    void clearJSCallICs(VM&);
     101
     102    bool runnable() const { return !m_errorMessage; }
     103
     104    String errorMessage()
     105    {
     106        ASSERT(!runnable());
     107        return m_errorMessage;
     108    }
    98109
    99110private:
     
    128139    UnconditionalFinalizer m_unconditionalFinalizer;
    129140    Bag<CallLinkInfo> m_callLinkInfos;
     141    String m_errorMessage;
    130142};
    131143
  • trunk/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp

    r217921 r218868  
    113113    } else {
    114114        codeBlock = JSWebAssemblyCodeBlock::create(vm, wasmCodeBlock.copyRef(), m_module.get());
     115        if (UNLIKELY(!codeBlock->runnable())) {
     116            throwException(exec, scope, JSWebAssemblyLinkError::create(exec, vm, globalObject()->WebAssemblyLinkErrorStructure(), codeBlock->errorMessage()));
     117            return;
     118        }
    115119        m_codeBlock.set(vm, this, codeBlock);
    116120        module()->setCodeBlock(vm, memoryMode(), codeBlock);
  • trunk/Tools/ChangeLog

    r218855 r218868  
     12017-06-27  JF Bastien  <jfbastien@apple.com>
     2
     3        WebAssembly: running out of executable memory should throw OoM
     4        https://bugs.webkit.org/show_bug.cgi?id=171537
     5        <rdar://problem/32963338>
     6
     7        Reviewed by Saam Barati.
     8
     9        * Scripts/run-jsc-stress-tests: add a configuration which runs the
     10        tests under limited executable memory and avoids non-WebAssembly
     11        code generation so that we more reliably run out of executable
     12        memory in WebAssembly.
     13
    1142017-06-27  Wenson Hsieh  <wenson_hsieh@apple.com>
    215
  • trunk/Tools/Scripts/run-jsc-stress-tests

    r218784 r218868  
    12351235        return
    12361236    end
    1237 
    12381237    return if !$jitTests
    12391238    return if !$isFTLPlatform
     
    12471246
    12481247    runWithOutputHandler("default-wasm", noisyOutputHandler, "../spec-harness.js", *FTL_OPTIONS)
    1249     runWithOutputHandler("wasm-no-cjit-yes-tls-context", noisyOutputHandler, "../spec-harness.js",  "--useFastTLSForWasmContext=true", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
    1250     runWithOutputHandler("wasm-eager-jettison", noisyOutputHandler, "../spec-harness.js", "--forceCodeBlockToJettisonDueToOldAge=true", *FTL_OPTIONS)
    1251     runWithOutputHandler("wasm-no-call-ic", noisyOutputHandler, "../spec-harness.js", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
    1252     runWithOutputHandler("wasm-no-tls-context", noisyOutputHandler, "../spec-harness.js", "--useFastTLSForWasmContext=false", *FTL_OPTIONS)
     1248    if !$quickMode
     1249      runWithOutputHandler("wasm-no-cjit-yes-tls-context", noisyOutputHandler, "../spec-harness.js",  "--useFastTLSForWasmContext=true", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
     1250      runWithOutputHandler("wasm-eager-jettison", noisyOutputHandler, "../spec-harness.js", "--forceCodeBlockToJettisonDueToOldAge=true", *FTL_OPTIONS)
     1251      runWithOutputHandler("wasm-no-call-ic", noisyOutputHandler, "../spec-harness.js", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
     1252      runWithOutputHandler("wasm-no-tls-context", noisyOutputHandler, "../spec-harness.js", "--useFastTLSForWasmContext=false", *FTL_OPTIONS)
     1253    end
     1254end
     1255
     1256def runWebAssemblyLowExecutableMemory(*optionalTestSpecificOptions)
     1257    return if !$jitTests
     1258    return if !$isFTLPlatform
     1259    modules = Dir[WASMTESTS_PATH + "*.js"].map { |f| File.basename(f) }
     1260    prepareExtraAbsoluteFiles(WASMTESTS_PATH, ["wasm.json"])
     1261    prepareExtraRelativeFiles(modules.map { |f| "../" + f }, $collection)
     1262    # Only let WebAssembly get executable memory.
     1263    run("default-wasm", "--useConcurrentGC=0" , "--useConcurrentJIT=0", "--jitMemoryReservationSize=15000", "--useBaselineJIT=0", "--useDFGJIT=0", "--useFTLJIT=0", "-m")
    12531264end
    12541265
Note: See TracChangeset for help on using the changeset viewer.