Title: [224810] trunk
Revision
224810
Author
sbar...@apple.com
Date
2017-11-14 01:05:33 -0800 (Tue, 14 Nov 2017)

Log Message

We need to set topCallFrame when calling Wasm::Memory::grow from the JIT
https://bugs.webkit.org/show_bug.cgi?id=179639
<rdar://problem/35513018>

Reviewed by JF Bastien.

JSTests:

* wasm/function-tests/grow-memory-cause-gc.js: Added.
(escape):
(i.func):

Source/_javascript_Core:

Calling Wasm::Memory::grow from the JIT may cause us to GC. When we GC, we will
walk the stack for ShadowChicken (and maybe other things). We weren't updating
topCallFrame when calling grow from the Wasm JIT. This would cause the GC to
use stale topCallFrame bits in VM, often leading to crashes. This patch fixes
this bug by giving Wasm::Instance a lambda that is called when we need to store
the topCallFrame. Users of Wasm::Instance can provide a function to do this action.
Currently, JSWebAssemblyInstance passes in a lambda that stores to
VM.topCallFrame.

* wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::addGrowMemory):
* wasm/WasmInstance.cpp:
(JSC::Wasm::Instance::Instance):
(JSC::Wasm::Instance::create):
* wasm/WasmInstance.h:
(JSC::Wasm::Instance::storeTopCallFrame):
* wasm/js/JSWebAssemblyInstance.cpp:
(JSC::JSWebAssemblyInstance::create):
* wasm/js/JSWebAssemblyInstance.h:
* wasm/js/WasmToJS.cpp:
(JSC::Wasm::wasmToJSException):
* wasm/js/WebAssemblyInstanceConstructor.cpp:
(JSC::constructJSWebAssemblyInstance):
* wasm/js/WebAssemblyPrototype.cpp:
(JSC::instantiate):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (224809 => 224810)


--- trunk/JSTests/ChangeLog	2017-11-14 08:56:15 UTC (rev 224809)
+++ trunk/JSTests/ChangeLog	2017-11-14 09:05:33 UTC (rev 224810)
@@ -1,3 +1,15 @@
+2017-11-14  Saam Barati  <sbar...@apple.com>
+
+        We need to set topCallFrame when calling Wasm::Memory::grow from the JIT
+        https://bugs.webkit.org/show_bug.cgi?id=179639
+        <rdar://problem/35513018>
+
+        Reviewed by JF Bastien.
+
+        * wasm/function-tests/grow-memory-cause-gc.js: Added.
+        (escape):
+        (i.func):
+
 2017-11-13  Mark Lam  <mark....@apple.com>
 
         Add more overflow check book-keeping for MarkedArgumentBuffer.

Added: trunk/JSTests/wasm/function-tests/grow-memory-cause-gc.js (0 => 224810)


--- trunk/JSTests/wasm/function-tests/grow-memory-cause-gc.js	                        (rev 0)
+++ trunk/JSTests/wasm/function-tests/grow-memory-cause-gc.js	2017-11-14 09:05:33 UTC (rev 224810)
@@ -0,0 +1,61 @@
+import Builder from '../Builder.js';
+import * as assert from '../assert.js';
+
+function escape(){}
+noInline(escape);
+
+for (let i = 0; i < 10; ++i) {
+    const max = 1024*2;
+    const memoryDescription = {initial: 0, maximum: max};
+    const growMemoryAmount = 256;
+
+    const builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Function("imp", "func", {params: [], ret: "void"})
+            .Memory("imp", "memory", memoryDescription)
+        .End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", {params: [], ret: "i32"})
+                .I32Const(1)
+                .I32Const(1)
+                .I32Const(1)
+                .I32Const(1)
+                .I32Const(1)
+                .I32Const(1)
+                .I32Const(1)
+                .I32Const(1)
+                .I32Const(1)
+                .I32Const(1)
+                .I32Const(1)
+                .I32Const(1)
+                .I32Const(1)
+                .I32Const(1)
+                .I32Const(1)
+                .I32Const(1)
+                .Call(2)
+                .I32Const(growMemoryAmount)
+                .GrowMemory(0)
+                .Return()
+            .End()
+            .Function("bar", {params: ["i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32"], ret: "void"})
+                .Call(0)
+                .Return()
+            .End()
+        .End();
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const memory = new WebAssembly.Memory(memoryDescription);
+
+    function func() { }
+
+    const instance = new WebAssembly.Instance(module, {imp: {memory, func}});
+    for (let i = 0; i < max/growMemoryAmount; ++i) {
+        instance.exports.foo();
+    }
+}

Modified: trunk/Source/_javascript_Core/ChangeLog (224809 => 224810)


--- trunk/Source/_javascript_Core/ChangeLog	2017-11-14 08:56:15 UTC (rev 224809)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-11-14 09:05:33 UTC (rev 224810)
@@ -1,3 +1,37 @@
+2017-11-14  Saam Barati  <sbar...@apple.com>
+
+        We need to set topCallFrame when calling Wasm::Memory::grow from the JIT
+        https://bugs.webkit.org/show_bug.cgi?id=179639
+        <rdar://problem/35513018>
+
+        Reviewed by JF Bastien.
+
+        Calling Wasm::Memory::grow from the JIT may cause us to GC. When we GC, we will
+        walk the stack for ShadowChicken (and maybe other things). We weren't updating
+        topCallFrame when calling grow from the Wasm JIT. This would cause the GC to
+        use stale topCallFrame bits in VM, often leading to crashes. This patch fixes
+        this bug by giving Wasm::Instance a lambda that is called when we need to store
+        the topCallFrame. Users of Wasm::Instance can provide a function to do this action.
+        Currently, JSWebAssemblyInstance passes in a lambda that stores to
+        VM.topCallFrame.
+
+        * wasm/WasmB3IRGenerator.cpp:
+        (JSC::Wasm::B3IRGenerator::addGrowMemory):
+        * wasm/WasmInstance.cpp:
+        (JSC::Wasm::Instance::Instance):
+        (JSC::Wasm::Instance::create):
+        * wasm/WasmInstance.h:
+        (JSC::Wasm::Instance::storeTopCallFrame):
+        * wasm/js/JSWebAssemblyInstance.cpp:
+        (JSC::JSWebAssemblyInstance::create):
+        * wasm/js/JSWebAssemblyInstance.h:
+        * wasm/js/WasmToJS.cpp:
+        (JSC::Wasm::wasmToJSException):
+        * wasm/js/WebAssemblyInstanceConstructor.cpp:
+        (JSC::constructJSWebAssemblyInstance):
+        * wasm/js/WebAssemblyPrototype.cpp:
+        (JSC::instantiate):
+
 2017-11-13  Saam Barati  <sbar...@apple.com>
 
         Remove pointer caging for HashMapImpl, JSLexicalEnvironment, DirectArguments, ScopedArguments, and ScopedArgumentsTable

Modified: trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp (224809 => 224810)


--- trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp	2017-11-14 08:56:15 UTC (rev 224809)
+++ trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp	2017-11-14 09:05:33 UTC (rev 224810)
@@ -558,7 +558,9 @@
 
 auto B3IRGenerator::addGrowMemory(ExpressionType delta, ExpressionType& result) -> PartialResult
 {
-    int32_t (*growMemory)(Instance*, int32_t) = [] (Instance* instance, int32_t delta) -> int32_t {
+    int32_t (*growMemory)(void*, Instance*, int32_t) = [] (void* callFrame, Instance* instance, int32_t delta) -> int32_t {
+        instance->storeTopCallFrame(callFrame);
+
         if (delta < 0)
             return -1;
 
@@ -571,6 +573,7 @@
             case Memory::GrowFailReason::OutOfMemory:
                 return -1;
             }
+            RELEASE_ASSERT_NOT_REACHED();
         }
 
         return grown.value().pageCount();
@@ -578,7 +581,7 @@
 
     result = m_currentBlock->appendNew<CCallValue>(m_proc, Int32, origin(),
         m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), bitwise_cast<void*>(growMemory)),
-        instanceValue(), delta);
+        m_currentBlock->appendNew<B3::Value>(m_proc, B3::FramePointer, origin()), instanceValue(), delta);
 
     restoreWebAssemblyGlobalState(m_info.memory, instanceValue(), m_proc, m_currentBlock);
 

Modified: trunk/Source/_javascript_Core/wasm/WasmInstance.cpp (224809 => 224810)


--- trunk/Source/_javascript_Core/wasm/WasmInstance.cpp	2017-11-14 08:56:15 UTC (rev 224809)
+++ trunk/Source/_javascript_Core/wasm/WasmInstance.cpp	2017-11-14 09:05:33 UTC (rev 224810)
@@ -41,11 +41,12 @@
 }
 }
 
-Instance::Instance(Context* context, Ref<Module>&& module, EntryFrame** topEntryFramePointer)
+Instance::Instance(Context* context, Ref<Module>&& module, EntryFrame** topEntryFramePointer, StoreTopCallFrameCallback&& storeTopCallFrame)
     : m_context(context)
     , m_module(WTFMove(module))
     , m_globals(MallocPtr<uint64_t>::malloc(globalMemoryByteSize(m_module.get())))
     , m_topEntryFramePointer(topEntryFramePointer)
+    , m_storeTopCallFrame(WTFMove(storeTopCallFrame))
     , m_numImportFunctions(m_module->moduleInformation().importFunctionCount())
 {
     for (unsigned i = 0; i < m_numImportFunctions; ++i)
@@ -52,9 +53,9 @@
         new (importFunctionInfo(i)) ImportFunctionInfo();
 }
 
-Ref<Instance> Instance::create(Context* context, Ref<Module>&& module, EntryFrame** topEntryFramePointer)
+Ref<Instance> Instance::create(Context* context, Ref<Module>&& module, EntryFrame** topEntryFramePointer, StoreTopCallFrameCallback&& storeTopCallFrame)
 {
-    return adoptRef(*new (NotNull, fastMalloc(allocationSize(module->moduleInformation().importFunctionCount()))) Instance(context, WTFMove(module), topEntryFramePointer));
+    return adoptRef(*new (NotNull, fastMalloc(allocationSize(module->moduleInformation().importFunctionCount()))) Instance(context, WTFMove(module), topEntryFramePointer, WTFMove(storeTopCallFrame)));
 }
 
 Instance::~Instance() { }

Modified: trunk/Source/_javascript_Core/wasm/WasmInstance.h (224809 => 224810)


--- trunk/Source/_javascript_Core/wasm/WasmInstance.h	2017-11-14 08:56:15 UTC (rev 224809)
+++ trunk/Source/_javascript_Core/wasm/WasmInstance.h	2017-11-14 09:05:33 UTC (rev 224810)
@@ -42,8 +42,10 @@
 
 class Instance : public ThreadSafeRefCounted<Instance> {
 public:
-    static Ref<Instance> create(Context* context, Ref<Module>&& module, EntryFrame** topEntryFramePointer);
+    using StoreTopCallFrameCallback = WTF::Function<void(void*)>;
 
+    static Ref<Instance> create(Context*, Ref<Module>&&, EntryFrame** topEntryFramePointer, StoreTopCallFrameCallback&&);
+
     void finalizeCreation(void* owner, Ref<CodeBlock>&& codeBlock)
     {
         m_owner = owner;
@@ -103,8 +105,13 @@
     static size_t offsetOfImportFunction(size_t importFunctionNum) { return offsetOfTail() + importFunctionNum * sizeof(ImportFunctionInfo) + OBJECT_OFFSETOF(ImportFunctionInfo, importFunction); }
     template<typename T> T* importFunction(unsigned importFunctionNum) { return reinterpret_cast<T*>(&importFunctionInfo(importFunctionNum)->importFunction); }
 
+    void storeTopCallFrame(void* callFrame)
+    {
+        m_storeTopCallFrame(callFrame);
+    }
+
 private:
-    Instance(Context* context, Ref<Module>&&, EntryFrame**);
+    Instance(Context*, Ref<Module>&&, EntryFrame**, StoreTopCallFrameCallback&&);
     
     static size_t allocationSize(Checked<size_t> numImportFunctions)
     {
@@ -120,7 +127,7 @@
     MallocPtr<uint64_t> m_globals;
     EntryFrame** m_topEntryFramePointer { nullptr };
     void* m_cachedStackLimit { bitwise_cast<void*>(std::numeric_limits<uintptr_t>::max()) };
-    
+    StoreTopCallFrameCallback m_storeTopCallFrame;
     unsigned m_numImportFunctions { 0 };
 };
 

Modified: trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.cpp (224809 => 224810)


--- trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.cpp	2017-11-14 08:56:15 UTC (rev 224809)
+++ trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.cpp	2017-11-14 09:05:33 UTC (rev 224810)
@@ -135,7 +135,7 @@
     RETURN_IF_EXCEPTION(scope, void());
 }
 
-JSWebAssemblyInstance* JSWebAssemblyInstance::create(VM& vm, ExecState* exec, JSWebAssemblyModule* jsModule, JSObject* importObject, Structure* instanceStructure, Ref<Wasm::Instance>&& instance)
+JSWebAssemblyInstance* JSWebAssemblyInstance::create(VM& vm, ExecState* exec, JSWebAssemblyModule* jsModule, JSObject* importObject, Structure* instanceStructure, Ref<Wasm::Module>&& module)
 {
     auto throwScope = DECLARE_THROW_SCOPE(vm);
     auto* globalObject = exec->lexicalGlobalObject();
@@ -163,8 +163,14 @@
     RETURN_IF_EXCEPTION(throwScope, nullptr);
 
     JSModuleNamespaceObject* moduleNamespace = moduleRecord->getModuleNamespace(exec);
+
+    auto storeTopCallFrame = [&vm] (void* topCallFrame) {
+        vm.topCallFrame = bitwise_cast<ExecState*>(topCallFrame);
+    };
+
     // FIXME: These objects could be pretty big we should try to throw OOM here.
-    auto* jsInstance = new (NotNull, allocateCell<JSWebAssemblyInstance>(vm.heap)) JSWebAssemblyInstance(vm, instanceStructure, WTFMove(instance));
+    auto* jsInstance = new (NotNull, allocateCell<JSWebAssemblyInstance>(vm.heap)) JSWebAssemblyInstance(vm, instanceStructure, 
+        Wasm::Instance::create(&vm.wasmContext, WTFMove(module), &vm.topEntryFrame, WTFMove(storeTopCallFrame)));
     jsInstance->finishCreation(vm, jsModule, moduleNamespace);
     RETURN_IF_EXCEPTION(throwScope, nullptr);
 

Modified: trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.h (224809 => 224810)


--- trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.h	2017-11-14 08:56:15 UTC (rev 224809)
+++ trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.h	2017-11-14 09:05:33 UTC (rev 224810)
@@ -48,7 +48,7 @@
 public:
     typedef JSDestructibleObject Base;
 
-    static JSWebAssemblyInstance* create(VM&, ExecState*, JSWebAssemblyModule*, JSObject* importObject, Structure*, Ref<Wasm::Instance>&&);
+    static JSWebAssemblyInstance* create(VM&, ExecState*, JSWebAssemblyModule*, JSObject* importObject, Structure*, Ref<Wasm::Module>&&);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_EXPORT_INFO;

Modified: trunk/Source/_javascript_Core/wasm/js/WasmToJS.cpp (224809 => 224810)


--- trunk/Source/_javascript_Core/wasm/js/WasmToJS.cpp	2017-11-14 08:56:15 UTC (rev 224809)
+++ trunk/Source/_javascript_Core/wasm/js/WasmToJS.cpp	2017-11-14 09:05:33 UTC (rev 224810)
@@ -638,25 +638,25 @@
 
 void* wasmToJSException(ExecState* exec, Wasm::ExceptionType type, Instance* wasmInstance)
 {
+    wasmInstance->storeTopCallFrame(exec);
     JSWebAssemblyInstance* instance = wasmInstance->owner<JSWebAssemblyInstance>();
-    VM* vm = instance->vm();
-    NativeCallFrameTracer tracer(vm, exec);
+    JSGlobalObject* globalObject = instance->globalObject();
+    VM& vm = globalObject->vm();
 
     {
-        auto throwScope = DECLARE_THROW_SCOPE(*vm);
-        JSGlobalObject* globalObject = instance->globalObject();
+        auto throwScope = DECLARE_THROW_SCOPE(vm);
 
         JSObject* error;
         if (type == ExceptionType::StackOverflow)
             error = createStackOverflowError(exec, globalObject);
         else
-            error = JSWebAssemblyRuntimeError::create(exec, *vm, globalObject->WebAssemblyRuntimeErrorStructure(), Wasm::errorMessageForExceptionType(type));
+            error = JSWebAssemblyRuntimeError::create(exec, vm, globalObject->WebAssemblyRuntimeErrorStructure(), Wasm::errorMessageForExceptionType(type));
         throwException(exec, throwScope, error);
     }
 
-    genericUnwind(vm, exec);
-    ASSERT(!!vm->callFrameForCatch);
-    ASSERT(!!vm->targetMachinePCForThrow);
+    genericUnwind(&vm, exec);
+    ASSERT(!!vm.callFrameForCatch);
+    ASSERT(!!vm.targetMachinePCForThrow);
     // FIXME: We could make this better:
     // This is a total hack, but the llint (both op_catch and handleUncaughtException)
     // require a cell in the callee field to load the VM. (The baseline JIT does not require
@@ -665,7 +665,7 @@
     // to the exception handler. If we did this, we could remove this terrible hack.
     // https://bugs.webkit.org/show_bug.cgi?id=170440
     bitwise_cast<uint64_t*>(exec)[CallFrameSlot::callee] = bitwise_cast<uint64_t>(instance->webAssemblyToJSCallee());
-    return vm->targetMachinePCForThrow;
+    return vm.targetMachinePCForThrow;
 }
 
 } } // namespace JSC::Wasm

Modified: trunk/Source/_javascript_Core/wasm/js/WebAssemblyInstanceConstructor.cpp (224809 => 224810)


--- trunk/Source/_javascript_Core/wasm/js/WebAssemblyInstanceConstructor.cpp	2017-11-14 08:56:15 UTC (rev 224809)
+++ trunk/Source/_javascript_Core/wasm/js/WebAssemblyInstanceConstructor.cpp	2017-11-14 09:05:33 UTC (rev 224810)
@@ -77,7 +77,7 @@
     Structure* instanceStructure = InternalFunction::createSubclassStructure(exec, exec->newTarget(), exec->lexicalGlobalObject()->WebAssemblyInstanceStructure());
     RETURN_IF_EXCEPTION(scope, { });
 
-    JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, exec, module, importObject, instanceStructure, Wasm::Instance::create(&vm.wasmContext, Ref<Wasm::Module>(module->module()), &vm.topEntryFrame));
+    JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, exec, module, importObject, instanceStructure, Ref<Wasm::Module>(module->module()));
     RETURN_IF_EXCEPTION(scope, { });
 
     instance->finalizeCreation(vm, exec, module->module().compileSync(&vm.wasmContext, instance->memoryMode(), &Wasm::createJSToWasmWrapper, &Wasm::wasmToJSException));

Modified: trunk/Source/_javascript_Core/wasm/js/WebAssemblyPrototype.cpp (224809 => 224810)


--- trunk/Source/_javascript_Core/wasm/js/WebAssemblyPrototype.cpp	2017-11-14 08:56:15 UTC (rev 224809)
+++ trunk/Source/_javascript_Core/wasm/js/WebAssemblyPrototype.cpp	2017-11-14 09:05:33 UTC (rev 224810)
@@ -137,7 +137,7 @@
 {
     auto scope = DECLARE_CATCH_SCOPE(vm);
     // In order to avoid potentially recompiling a module. We first gather all the import/memory information prior to compiling code.
-    JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, exec, module, importObject, exec->lexicalGlobalObject()->WebAssemblyInstanceStructure(), Wasm::Instance::create(&vm.wasmContext, Ref<Wasm::Module>(module->module()), &vm.topEntryFrame));
+    JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, exec, module, importObject, exec->lexicalGlobalObject()->WebAssemblyInstanceStructure(), Ref<Wasm::Module>(module->module()));
     RETURN_IF_EXCEPTION(scope, reject(exec, scope, promise));
 
     Vector<Strong<JSCell>> dependencies;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to