Diff
Modified: trunk/JSTests/ChangeLog (218867 => 218868)
--- trunk/JSTests/ChangeLog 2017-06-28 06:23:23 UTC (rev 218867)
+++ trunk/JSTests/ChangeLog 2017-06-28 06:42:13 UTC (rev 218868)
@@ -1,3 +1,30 @@
+2017-06-27 JF Bastien <jfbast...@apple.com>
+
+ 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.
+
+ * 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):
+
2017-06-27 Caio Lima <ticaiol...@gmail.com>
[ESnext] Implement Object Rest - Implementing Object Rest Destructuring
Added: trunk/JSTests/wasm/lowExecutableMemory/executable-memory-oom.js (0 => 218868)
--- trunk/JSTests/wasm/lowExecutableMemory/executable-memory-oom.js (rev 0)
+++ trunk/JSTests/wasm/lowExecutableMemory/executable-memory-oom.js 2017-06-28 06:42:13 UTC (rev 218868)
@@ -0,0 +1,121 @@
+import * as assert from '../assert.js'
+import Builder from '../Builder.js'
+
+const verbose = false;
+const maxInstructionCount = 500;
+const instancesTotal = 8;
+const invocationsTotal = 8;
+const tierUpCalls = 20000; // Call enough to trigger tier up and get it to compile.
+
+// This test starts running with a few bytes of executable memory available. Try
+// to create and instantiate a module which will fail to fit.
+
+const randomProgram = instructionCount => {
+ let b = new Builder()
+ .Type().End()
+ .Function().End()
+ .Export()
+ .Function("foo")
+ .Function("bar")
+ .End()
+ .Code()
+ .Function("foo", { params: [], ret: "f32" })
+ .F32Const(2.0)
+ .Return()
+ .End()
+ .Function("bar", { params: ["f32", "f32"], ret: "f32" })
+ .GetLocal(0);
+
+ // Insert a bunch of dependent instructions in a single basic block so that
+ // our compiler won't be able to strength-reduce.
+ const actions = [
+ b => b.GetLocal(0).F32Sub(),
+ b => b.GetLocal(1).F32Sub(),
+ b => b.GetLocal(0).F32Add(),
+ b => b.GetLocal(1).F32Add(),
+ b => b.GetLocal(0).F32Mul(),
+ b => b.GetLocal(1).F32Mul(),
+ ];
+
+ while (--instructionCount)
+ b = actions[(Math.random() * actions.length) | 0](b);
+
+ b = b.Return().End().End();
+
+ return b.WebAssembly().get();
+}
+
+let failCount = 0;
+let callCount = 0;
+let instances = [];
+
+const invoke = (instance, count) => {
+ if (verbose)
+ print(`Invoking`);
+ for (let i = 0; i < count; ++i)
+ assert.eq(instance.exports["foo"](), 2.0);
+ for (let i = 0; i < count; ++i)
+ instance.exports["bar"](2.0, 6.0);
+ ++callCount;
+};
+
+while (failCount === 0) {
+ const instructionCount = (Math.random() * maxInstructionCount + 1) | 0;
+
+ if (verbose)
+ print(`Trying module with ${instructionCount} instructions.`);
+
+ const buf = randomProgram(instructionCount);
+ let module;
+
+ try {
+ module = new WebAssembly.Module(buf);
+ } catch (e) {
+ if (e instanceof WebAssembly.CompileError) {
+ if (verbose)
+ print(`Caught: ${e}`);
+ ++failCount;
+ }
+ else
+ throw new Error(`Expected a WebAssembly.CompileError, got ${e}`);
+ }
+
+ if (module !== undefined) {
+ if (verbose)
+ print(`Creating instance`);
+
+ let instance;
+ try {
+ instance = new WebAssembly.Instance(module);
+ } catch (e) {
+ if (e instanceof WebAssembly.LinkError) {
+ if (verbose)
+ print(`Caught: ${e}`);
+ ++failCount;
+ }
+ else
+ throw new Error(`Expected a WebAssembly.LinkError, got ${e}`);
+ }
+
+ if (instance !== undefined) {
+ instances.push(instance);
+ invoke(instance, 1);
+ }
+ }
+}
+
+if (callCount === 0)
+ throw new Error(`Expected to be able to allocate a WebAssembly module, instantiate it, and call its exports at least once`);
+
+// Make sure we can still call all the instances we create, even after going
+// OOM. This will try to force tier-up as well, which should fail.
+
+if (verbose)
+ print(`Invoking all previously created instances`);
+
+for (let instance of instances)
+ invoke(instance, tierUpCalls);
+
+// Do it twice to revisit what should have gotten tiered up.
+for (let instance of instances)
+ invoke(instance, 1);
Added: trunk/JSTests/wasm/lowExecutableMemory/exports-oom.js (0 => 218868)
--- trunk/JSTests/wasm/lowExecutableMemory/exports-oom.js (rev 0)
+++ trunk/JSTests/wasm/lowExecutableMemory/exports-oom.js 2017-06-28 06:42:13 UTC (rev 218868)
@@ -0,0 +1,107 @@
+import * as assert from '../assert.js'
+import Builder from '../Builder.js'
+
+const verbose = false;
+const numFunctions = 2;
+const maxParams = 128;
+
+// This test starts running with a few bytes of executable memory available. Try
+// to create and instantiate modules which have way more exports than anything
+// else. Hopefully they'll fail when trying to instantiate their entrypoints.
+
+const type = () => {
+ const types = ["i32", "f32", "f64"]; // Can't export i64.
+ return types[(Math.random() * types.length) | 0];
+};
+
+const params = () => {
+ let p = [];
+ let count = (Math.random() * maxParams) | 0;
+ while (count--)
+ p.push(type());
+ return p;
+};
+
+const randomProgram = () => {
+ let b = new Builder()
+ .Type().End()
+ .Function().End()
+ .Export();
+ for (let f = 0; f < numFunctions; ++f)
+ b = b.Function(`f${f}`);
+ b = b.End().Code();
+ for (let f = 0; f < numFunctions; ++f)
+ b = b.Function(`f${f}`, { params: params() }).Return().End();
+ b = b.End();
+ return b.WebAssembly().get();
+}
+
+let failCount = 0;
+let callCount = 0;
+let instances = [];
+
+const invoke = instance => {
+ let result = 0;
+ for (let f = 0; f < numFunctions; ++f) {
+ const name = `f${f}`;
+ if (verbose)
+ print(`Invoking ${name}`);
+ result += instance.exports[name]();
+ ++callCount;
+ }
+ return result;
+};
+
+while (failCount === 0) {
+ if (verbose)
+ print(`Trying...`);
+
+ const buf = randomProgram();
+ let module;
+
+ try {
+ module = new WebAssembly.Module(buf);
+ } catch (e) {
+ if (e instanceof WebAssembly.CompileError) {
+ if (verbose)
+ print(`Caught: ${e}`);
+ ++failCount;
+ }
+ else
+ throw new Error(`Expected a WebAssembly.CompileError, got ${e}`);
+ }
+
+ if (module !== undefined) {
+ if (verbose)
+ print(`Creating instance`);
+
+ let instance;
+ try {
+ instance = new WebAssembly.Instance(module);
+ } catch (e) {
+ if (e instanceof WebAssembly.LinkError) {
+ if (verbose)
+ print(`Caught: ${e}`);
+ ++failCount;
+ }
+ else
+ throw new Error(`Expected a WebAssembly.LinkError, got ${e}`);
+ }
+
+ if (instance !== undefined) {
+ instances.push(instance);
+ invoke(instance);
+ }
+ }
+}
+
+if (callCount === 0)
+ throw new Error(`Expected to be able to allocate a WebAssembly module, instantiate it, and call its exports at least once`);
+
+// Make sure we can still call all the instances we create, even after going OOM.
+
+if (verbose)
+ print(`Invoking all previously created instances`);
+
+for (let instance of instances)
+ invoke(instance);
Added: trunk/JSTests/wasm/lowExecutableMemory/imports-oom.js (0 => 218868)
--- trunk/JSTests/wasm/lowExecutableMemory/imports-oom.js (rev 0)
+++ trunk/JSTests/wasm/lowExecutableMemory/imports-oom.js 2017-06-28 06:42:13 UTC (rev 218868)
@@ -0,0 +1,124 @@
+import * as assert from '../assert.js'
+import Builder from '../Builder.js'
+
+const verbose = false;
+const numFunctions = 2;
+const maxParams = 32;
+
+// This test starts running with a few bytes of executable memory available. Try
+// to create and instantiate modules which have way more imports than anything
+// else. Hopefully they'll fail when trying to instantiate their entrypoints.
+
+const type = () => {
+ const types = ["i32", "f32", "f64"];
+ return types[(Math.random() * types.length) | 0];
+};
+
+const params = () => {
+ let p = [];
+ let count = (Math.random() * maxParams) | 0;
+ while (count--)
+ p.push(type());
+ return p;
+};
+
+const randomProgram = () => {
+ let b = new Builder()
+ .Type().End()
+ .Import();
+ const ps = params();
+ for (let f = 0; f < numFunctions; ++f)
+ b = b.Function("imp", `${f}`, { params: ps });
+ b = b.End()
+ .Function().End()
+ .Export();
+ for (let f = 0; f < numFunctions; ++f)
+ b = b.Function(`call${f}`);
+ b = b.End()
+ .Code();
+ for (let f = 0; f < numFunctions; ++f) {
+ b = b.Function(`call${f}`, { params: ps });
+ for (let p = 0; p < ps.length; ++p)
+ b = b.GetLocal(p);
+ b = b.Call(f).End();
+ }
+ b = b.End();
+ return b.WebAssembly().get();
+}
+
+let failCount = 0;
+let callCount = 0;
+let instances = [];
+
+let imports = [];
+for (let f = 0; f < numFunctions; ++f)
+ imports.push((...args) => {
+ if (verbose)
+ print(`Invoked ${f} with: ${args}`);
+ ++callCount;
+ });
+
+const invoke = instance => {
+ let result = 0;
+ for (let f = 0; f < numFunctions; ++f) {
+ const name = `call${f}`;
+ if (verbose)
+ print(`Invoking ${name}`);
+ result += instance.exports[name]();
+ }
+ return result;
+};
+
+while (failCount === 0) {
+ if (verbose)
+ print(`Trying...`);
+
+ const buf = randomProgram();
+ let module;
+
+ try {
+ module = new WebAssembly.Module(buf);
+ } catch (e) {
+ if (e instanceof WebAssembly.CompileError) {
+ if (verbose)
+ print(`Caught: ${e}`);
+ ++failCount;
+ }
+ else
+ throw new Error(`Expected a WebAssembly.CompileError, got ${e}`);
+ }
+
+ if (module !== undefined) {
+ if (verbose)
+ print(`Creating instance`);
+
+ let instance;
+ try {
+ instance = new WebAssembly.Instance(module, { imp: imports });
+ } catch (e) {
+ if (e instanceof WebAssembly.LinkError) {
+ if (verbose)
+ print(`Caught: ${e}`);
+ ++failCount;
+ }
+ else
+ throw new Error(`Expected a WebAssembly.LinkError, got ${e}`);
+ }
+
+ if (instance !== undefined) {
+ instances.push(instance);
+ invoke(instance);
+ }
+ }
+}
+
+if (callCount === 0)
+ throw new Error(`Expected to be able to allocate a WebAssembly module, instantiate it, and call its exports at least once`);
+
+// Make sure we can still call all the instances we create, even after going OOM.
+
+if (verbose)
+ print(`Invoking all previously created instances`);
+
+for (let instance of instances)
+ invoke(instance);
Modified: trunk/JSTests/wasm.yaml (218867 => 218868)
--- trunk/JSTests/wasm.yaml 2017-06-28 06:23:23 UTC (rev 218867)
+++ trunk/JSTests/wasm.yaml 2017-06-28 06:42:13 UTC (rev 218868)
@@ -33,6 +33,8 @@
cmd: runWebAssembly unless parseRunCommands
- path: wasm/stress
cmd: runWebAssembly unless parseRunCommands
+- path: wasm/lowExecutableMemory
+ cmd: runWebAssemblyLowExecutableMemory unless parseRunCommands
- path: wasm/spec-tests/address.wast.js
cmd: runWebAssemblySpecTest :normal
Modified: trunk/Source/_javascript_Core/ChangeLog (218867 => 218868)
--- trunk/Source/_javascript_Core/ChangeLog 2017-06-28 06:23:23 UTC (rev 218867)
+++ trunk/Source/_javascript_Core/ChangeLog 2017-06-28 06:42:13 UTC (rev 218868)
@@ -1,3 +1,41 @@
+2017-06-27 JF Bastien <jfbast...@apple.com>
+
+ 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.
+
+ 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):
+
2017-06-27 Saam Barati <sbar...@apple.com>
JITStubRoutine::passesFilter should use isJITPC
Modified: trunk/Source/_javascript_Core/jit/ExecutableAllocator.cpp (218867 => 218868)
--- trunk/Source/_javascript_Core/jit/ExecutableAllocator.cpp 2017-06-28 06:23:23 UTC (rev 218867)
+++ trunk/Source/_javascript_Core/jit/ExecutableAllocator.cpp 2017-06-28 06:42:13 UTC (rev 218868)
@@ -412,8 +412,11 @@
size_t bytesAllocated = statistics.bytesAllocated + sizeInBytes;
size_t bytesAvailable = static_cast<size_t>(
statistics.bytesReserved * (1 - executablePoolReservationFraction));
- if (bytesAllocated > bytesAvailable)
+ if (bytesAllocated > bytesAvailable) {
+ if (Options::logExecutableAllocation())
+ dataLog("Allocation failed because bytes allocated ", bytesAllocated, " > ", bytesAvailable, " bytes available.\n");
return nullptr;
+ }
}
RefPtr<ExecutableMemoryHandle> result = allocator->allocate(sizeInBytes, ownerUID);
Modified: trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp (218867 => 218868)
--- trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp 2017-06-28 06:23:23 UTC (rev 218867)
+++ trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp 2017-06-28 06:42:13 UTC (rev 218868)
@@ -318,8 +318,7 @@
|| !ensureGlobalJITWhitelist().contains(codeBlock))
return false;
- // You can modify this to turn off JITting without rebuilding the world.
- return exec->vm().canUseJIT();
+ return exec->vm().canUseJIT() && Options::useBaselineJIT();
}
// Returns true if we should try to OSR.
Modified: trunk/Source/_javascript_Core/runtime/Options.h (218867 => 218868)
--- trunk/Source/_javascript_Core/runtime/Options.h 2017-06-28 06:23:23 UTC (rev 218867)
+++ trunk/Source/_javascript_Core/runtime/Options.h 2017-06-28 06:42:13 UTC (rev 218868)
@@ -112,7 +112,8 @@
v(optionString, configFile, nullptr, Normal, "file to configure JSC options and logging location") \
\
v(bool, useLLInt, true, Normal, "allows the LLINT to be used if true") \
- v(bool, useJIT, true, Normal, "allows the baseline JIT to be used if true") \
+ v(bool, useJIT, true, Normal, "allows the executable pages to be allocated for JIT and thunks if true") \
+ v(bool, useBaselineJIT, true, Normal, "allows the baseline JIT to be used if true") \
v(bool, useDFGJIT, true, Normal, "allows the DFG JIT to be used if true") \
v(bool, useRegExpJIT, true, Normal, "allows the RegExp JIT to be used if true") \
v(bool, useDOMJIT, true, Normal, "allows the DOMJIT to be used if true") \
Modified: trunk/Source/_javascript_Core/wasm/WasmBBQPlan.cpp (218867 => 218868)
--- trunk/Source/_javascript_Core/wasm/WasmBBQPlan.cpp 2017-06-28 06:23:23 UTC (rev 218867)
+++ trunk/Source/_javascript_Core/wasm/WasmBBQPlan.cpp 2017-06-28 06:42:13 UTC (rev 218868)
@@ -175,7 +175,15 @@
continue;
unsigned importFunctionIndex = m_wasmToWasmExitStubs.size();
dataLogLnIf(verbose, "Processing import function number ", importFunctionIndex, ": ", makeString(import->module), ": ", makeString(import->field));
- m_wasmToWasmExitStubs.uncheckedAppend(wasmToWasm(importFunctionIndex));
+ auto binding = wasmToWasm(importFunctionIndex);
+ if (UNLIKELY(!binding)) {
+ switch (binding.error()) {
+ case BindingFailure::OutOfMemory:
+ return fail(holdLock(m_lock), makeString("Out of executable memory at import ", String::number(importIndex)));
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ m_wasmToWasmExitStubs.uncheckedAppend(binding.value());
}
const uint32_t importFunctionCount = m_moduleInformation->importFunctionCount();
@@ -288,12 +296,17 @@
ASSERT(m_state != State::Compiled || m_currentIndex >= m_moduleInformation->functionLocationInBinary.size());
dataLogLnIf(verbose, "Starting Completion");
- if (m_state == State::Compiled) {
+ if (!failed() && m_state == State::Compiled) {
for (uint32_t functionIndex = 0; functionIndex < m_moduleInformation->functionLocationInBinary.size(); functionIndex++) {
CompilationContext& context = m_compilationContexts[functionIndex];
SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
{
- LinkBuffer linkBuffer(*context.wasmEntrypointJIT, nullptr);
+ LinkBuffer linkBuffer(*context.wasmEntrypointJIT, nullptr, JITCompilationCanFail);
+ if (UNLIKELY(linkBuffer.didFailToAllocate())) {
+ Base::fail(locker, makeString("Out of executable memory in function at index ", String::number(functionIndex)));
+ return;
+ }
+
m_wasmInternalFunctions[functionIndex]->entrypoint.compilation = std::make_unique<B3::Compilation>(
FINALIZE_CODE(linkBuffer, ("WebAssembly function[%i] %s", functionIndex, SignatureInformation::get(signatureIndex).toString().ascii().data())),
WTFMove(context.wasmEntrypointByproducts));
@@ -300,7 +313,12 @@
}
if (auto jsToWasmInternalFunction = m_jsToWasmInternalFunctions.get(functionIndex)) {
- LinkBuffer linkBuffer(*context.jsEntrypointJIT, nullptr);
+ LinkBuffer linkBuffer(*context.jsEntrypointJIT, nullptr, JITCompilationCanFail);
+ if (UNLIKELY(linkBuffer.didFailToAllocate())) {
+ Base::fail(locker, makeString("Out of executable memory in function entrypoint at index ", String::number(functionIndex)));
+ return;
+ }
+
jsToWasmInternalFunction->entrypoint.compilation = std::make_unique<B3::Compilation>(
FINALIZE_CODE(linkBuffer, ("_javascript_->WebAssembly entrypoint[%i] %s", functionIndex, SignatureInformation::get(signatureIndex).toString().ascii().data())),
WTFMove(context.jsEntrypointByproducts));
Modified: trunk/Source/_javascript_Core/wasm/WasmBinding.cpp (218867 => 218868)
--- trunk/Source/_javascript_Core/wasm/WasmBinding.cpp 2017-06-28 06:23:23 UTC (rev 218867)
+++ trunk/Source/_javascript_Core/wasm/WasmBinding.cpp 2017-06-28 06:42:13 UTC (rev 218868)
@@ -51,7 +51,7 @@
jit.loadPtr(JIT::Address(result, JSWebAssemblyInstance::offsetOfImportFunction(importIndex)), result);
}
-MacroAssemblerCodeRef wasmToJs(VM* vm, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex signatureIndex, unsigned importIndex)
+Expected<MacroAssemblerCodeRef, BindingFailure> wasmToJs(VM* vm, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex signatureIndex, unsigned importIndex)
{
// FIXME: This function doesn't properly abstract away the calling convention.
// It'd be super easy to do so: https://bugs.webkit.org/show_bug.cgi?id=169401
@@ -119,7 +119,10 @@
ASSERT(!!vm->callFrameForCatch);
};
- LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
+ LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID, JITCompilationCanFail);
+ if (UNLIKELY(linkBuffer.didFailToAllocate()))
+ return makeUnexpected(BindingFailure::OutOfMemory);
+
linkBuffer.link(call, throwBadI64);
return FINALIZE_CODE(linkBuffer, ("WebAssembly->_javascript_ invalid i64 use in import[%i]", importIndex));
}
@@ -303,7 +306,10 @@
jit.emitFunctionEpilogue();
jit.ret();
- LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
+ LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID, JITCompilationCanFail);
+ if (UNLIKELY(linkBuffer.didFailToAllocate()))
+ return makeUnexpected(BindingFailure::OutOfMemory);
+
linkBuffer.link(call, callFunc);
linkBuffer.link(exceptionCall, doUnwinding);
@@ -600,7 +606,10 @@
});
}
- LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID);
+ LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID, JITCompilationCanFail);
+ if (UNLIKELY(patchBuffer.didFailToAllocate()))
+ return makeUnexpected(BindingFailure::OutOfMemory);
+
patchBuffer.link(slowCall, FunctionPtr(vm->getCTIStub(linkCallThunkGenerator).code().executableAddress()));
CodeLocationLabel callReturnLocation(patchBuffer.locationOfNearCall(slowCall));
CodeLocationLabel hotPathBegin(patchBuffer.locationOf(targetToCheck));
@@ -610,7 +619,7 @@
return FINALIZE_CODE(patchBuffer, ("WebAssembly->_javascript_ import[%i] %s", importIndex, signature.toString().ascii().data()));
}
-MacroAssemblerCodeRef wasmToWasm(unsigned importIndex)
+Expected<MacroAssemblerCodeRef, BindingFailure> wasmToWasm(unsigned importIndex)
{
const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
JIT jit;
@@ -653,7 +662,10 @@
jit.loadPtr(scratch, scratch);
jit.jump(scratch);
- LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID);
+ LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID, JITCompilationCanFail);
+ if (UNLIKELY(patchBuffer.didFailToAllocate()))
+ return makeUnexpected(BindingFailure::OutOfMemory);
+
return FINALIZE_CODE(patchBuffer, ("WebAssembly->WebAssembly import[%i]", importIndex));
}
Modified: trunk/Source/_javascript_Core/wasm/WasmBinding.h (218867 => 218868)
--- trunk/Source/_javascript_Core/wasm/WasmBinding.h 2017-06-28 06:23:23 UTC (rev 218867)
+++ trunk/Source/_javascript_Core/wasm/WasmBinding.h 2017-06-28 06:42:13 UTC (rev 218868)
@@ -31,6 +31,7 @@
#include "VM.h"
#include "WasmFormat.h"
#include <wtf/Bag.h>
+#include <wtf/Expected.h>
namespace JSC {
@@ -38,9 +39,13 @@
namespace Wasm {
-MacroAssemblerCodeRef wasmToWasm(unsigned importIndex);
-MacroAssemblerCodeRef wasmToJs(VM*, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex, unsigned importIndex);
+enum class BindingFailure {
+ OutOfMemory,
+};
+Expected<MacroAssemblerCodeRef, BindingFailure> wasmToWasm(unsigned importIndex);
+Expected<MacroAssemblerCodeRef, BindingFailure> wasmToJs(VM*, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex, unsigned importIndex);
+
} } // namespace JSC::Wasm
#endif // ENABLE(WEBASSEMBLY)
Modified: trunk/Source/_javascript_Core/wasm/WasmOMGPlan.cpp (218867 => 218868)
--- trunk/Source/_javascript_Core/wasm/WasmOMGPlan.cpp 2017-06-28 06:23:23 UTC (rev 218867)
+++ trunk/Source/_javascript_Core/wasm/WasmOMGPlan.cpp 2017-06-28 06:42:13 UTC (rev 218868)
@@ -88,7 +88,12 @@
}
Entrypoint omgEntrypoint;
- LinkBuffer linkBuffer(*context.wasmEntrypointJIT, nullptr);
+ LinkBuffer linkBuffer(*context.wasmEntrypointJIT, nullptr, JITCompilationCanFail);
+ if (UNLIKELY(linkBuffer.didFailToAllocate())) {
+ Base::fail(holdLock(m_lock), makeString("Out of executable memory while tiering up function at index ", String::number(m_functionIndex)));
+ return;
+ }
+
omgEntrypoint.compilation = std::make_unique<B3::Compilation>(
FINALIZE_CODE(linkBuffer, ("WebAssembly OMG function[%i] %s", m_functionIndex, SignatureInformation::get(signatureIndex).toString().ascii().data())),
WTFMove(context.wasmEntrypointByproducts));
Modified: trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyCodeBlock.cpp (218867 => 218868)
--- trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyCodeBlock.cpp 2017-06-28 06:23:23 UTC (rev 218867)
+++ trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyCodeBlock.cpp 2017-06-28 06:42:13 UTC (rev 218868)
@@ -58,7 +58,16 @@
m_wasmToJSExitStubs.reserveCapacity(m_codeBlock->functionImportCount());
for (unsigned importIndex = 0; importIndex < m_codeBlock->functionImportCount(); ++importIndex) {
Wasm::SignatureIndex signatureIndex = moduleInformation.importFunctionSignatureIndices.at(importIndex);
- m_wasmToJSExitStubs.uncheckedAppend(Wasm::wasmToJs(&vm, m_callLinkInfos, signatureIndex, importIndex));
+ auto binding = Wasm::wasmToJs(&vm, m_callLinkInfos, signatureIndex, importIndex);
+ if (UNLIKELY(!binding)) {
+ switch (binding.error()) {
+ case Wasm::BindingFailure::OutOfMemory:
+ m_errorMessage = ASCIILiteral("Out of executable memory");
+ return;
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ m_wasmToJSExitStubs.uncheckedAppend(binding.value());
importWasmToJSStub(importIndex) = m_wasmToJSExitStubs[importIndex].code().executableAddress();
}
}
Modified: trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyCodeBlock.h (218867 => 218868)
--- trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyCodeBlock.h 2017-06-28 06:23:23 UTC (rev 218867)
+++ trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyCodeBlock.h 2017-06-28 06:42:13 UTC (rev 218868)
@@ -75,15 +75,18 @@
Wasm::Callee& jsEntrypointCalleeFromFunctionIndexSpace(unsigned functionIndexSpace)
{
+ ASSERT(runnable());
return m_codeBlock->jsEntrypointCalleeFromFunctionIndexSpace(functionIndexSpace);
}
Wasm::WasmEntrypointLoadLocation wasmEntrypointLoadLocationFromFunctionIndexSpace(unsigned functionIndexSpace)
{
+ ASSERT(runnable());
return m_codeBlock->wasmEntrypointLoadLocationFromFunctionIndexSpace(functionIndexSpace);
}
Wasm::WasmEntrypointLoadLocation wasmToJsCallStubForImport(unsigned importIndex)
{
+ ASSERT(runnable());
return &importWasmToJSStub(importIndex);
}
@@ -96,6 +99,14 @@
void clearJSCallICs(VM&);
+ bool runnable() const { return !m_errorMessage; }
+
+ String errorMessage()
+ {
+ ASSERT(!runnable());
+ return m_errorMessage;
+ }
+
private:
JSWebAssemblyCodeBlock(VM&, Ref<Wasm::CodeBlock>&&, const Wasm::ModuleInformation&);
DECLARE_EXPORT_INFO;
@@ -127,6 +138,7 @@
Vector<MacroAssemblerCodeRef> m_wasmToJSExitStubs;
UnconditionalFinalizer m_unconditionalFinalizer;
Bag<CallLinkInfo> m_callLinkInfos;
+ String m_errorMessage;
};
} // namespace JSC
Modified: trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.cpp (218867 => 218868)
--- trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.cpp 2017-06-28 06:23:23 UTC (rev 218867)
+++ trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.cpp 2017-06-28 06:42:13 UTC (rev 218868)
@@ -112,6 +112,10 @@
m_codeBlock.set(vm, this, codeBlock);
} else {
codeBlock = JSWebAssemblyCodeBlock::create(vm, wasmCodeBlock.copyRef(), m_module.get());
+ if (UNLIKELY(!codeBlock->runnable())) {
+ throwException(exec, scope, JSWebAssemblyLinkError::create(exec, vm, globalObject()->WebAssemblyLinkErrorStructure(), codeBlock->errorMessage()));
+ return;
+ }
m_codeBlock.set(vm, this, codeBlock);
module()->setCodeBlock(vm, memoryMode(), codeBlock);
}
Modified: trunk/Tools/ChangeLog (218867 => 218868)
--- trunk/Tools/ChangeLog 2017-06-28 06:23:23 UTC (rev 218867)
+++ trunk/Tools/ChangeLog 2017-06-28 06:42:13 UTC (rev 218868)
@@ -1,3 +1,16 @@
+2017-06-27 JF Bastien <jfbast...@apple.com>
+
+ 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.
+
+ * 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.
+
2017-06-27 Wenson Hsieh <wenson_hs...@apple.com>
[iOS DnD] Support dragging out of contenteditable areas without a prior selection
Modified: trunk/Tools/Scripts/run-jsc-stress-tests (218867 => 218868)
--- trunk/Tools/Scripts/run-jsc-stress-tests 2017-06-28 06:23:23 UTC (rev 218867)
+++ trunk/Tools/Scripts/run-jsc-stress-tests 2017-06-28 06:42:13 UTC (rev 218868)
@@ -1234,7 +1234,6 @@
when :skip
return
end
-
return if !$jitTests
return if !$isFTLPlatform
prepareExtraAbsoluteFiles(WASMTESTS_PATH, ["wasm.json"])
@@ -1246,12 +1245,24 @@
prepareExtraRelativeFiles(harness.map { |f| "../../spec-harness/" + f }, $collection)
runWithOutputHandler("default-wasm", noisyOutputHandler, "../spec-harness.js", *FTL_OPTIONS)
- runWithOutputHandler("wasm-no-cjit-yes-tls-context", noisyOutputHandler, "../spec-harness.js", "--useFastTLSForWasmContext=true", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
- runWithOutputHandler("wasm-eager-jettison", noisyOutputHandler, "../spec-harness.js", "--forceCodeBlockToJettisonDueToOldAge=true", *FTL_OPTIONS)
- runWithOutputHandler("wasm-no-call-ic", noisyOutputHandler, "../spec-harness.js", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
- runWithOutputHandler("wasm-no-tls-context", noisyOutputHandler, "../spec-harness.js", "--useFastTLSForWasmContext=false", *FTL_OPTIONS)
+ if !$quickMode
+ runWithOutputHandler("wasm-no-cjit-yes-tls-context", noisyOutputHandler, "../spec-harness.js", "--useFastTLSForWasmContext=true", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
+ runWithOutputHandler("wasm-eager-jettison", noisyOutputHandler, "../spec-harness.js", "--forceCodeBlockToJettisonDueToOldAge=true", *FTL_OPTIONS)
+ runWithOutputHandler("wasm-no-call-ic", noisyOutputHandler, "../spec-harness.js", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
+ runWithOutputHandler("wasm-no-tls-context", noisyOutputHandler, "../spec-harness.js", "--useFastTLSForWasmContext=false", *FTL_OPTIONS)
+ end
end
+def runWebAssemblyLowExecutableMemory(*optionalTestSpecificOptions)
+ return if !$jitTests
+ return if !$isFTLPlatform
+ modules = Dir[WASMTESTS_PATH + "*.js"].map { |f| File.basename(f) }
+ prepareExtraAbsoluteFiles(WASMTESTS_PATH, ["wasm.json"])
+ prepareExtraRelativeFiles(modules.map { |f| "../" + f }, $collection)
+ # Only let WebAssembly get executable memory.
+ run("default-wasm", "--useConcurrentGC=0" , "--useConcurrentJIT=0", "--jitMemoryReservationSize=15000", "--useBaselineJIT=0", "--useDFGJIT=0", "--useFTLJIT=0", "-m")
+end
+
def runChakra(mode, exception, baselineFile, extraFiles)
raise unless $benchmark.to_s =~ /\.js$/
failsWithException = exception != "NoException"