Bug 1389464 - Wasm atomics gating on shared memory enabled. rs=luke
authorLars T Hansen <lhansen@mozilla.com>
Tue, 21 Nov 2017 09:35:43 +0100
changeset 701920 576034b89b34b25e50c628392bbaa6f0c406f175
parent 701919 565ca96c41490c9367b476d41af9aa6e0b611bd9
child 701921 be8d2835531bf914caaed93b4c37438e26b3d6a2
push id90308
push userbmo:lhansen@mozilla.com
push dateWed, 22 Nov 2017 12:45:04 +0000
reviewersluke
bugs1389464
milestone59.0a1
Bug 1389464 - Wasm atomics gating on shared memory enabled. rs=luke
js/src/js.msg
js/src/vm/ArrayBufferObject.cpp
js/src/wasm/AsmJS.cpp
js/src/wasm/WasmBinaryToAST.cpp
js/src/wasm/WasmCompile.cpp
js/src/wasm/WasmCompile.h
js/src/wasm/WasmJS.cpp
js/src/wasm/WasmModule.cpp
js/src/wasm/WasmValidate.cpp
js/src/wasm/WasmValidate.h
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -352,24 +352,26 @@ MSG_DEF(JSMSG_ESCAPED_KEYWORD,         0
 
 // asm.js
 MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL,       1, JSEXN_TYPEERR, "asm.js type error: {0}")
 MSG_DEF(JSMSG_USE_ASM_LINK_FAIL,       1, JSEXN_TYPEERR, "asm.js link error: {0}")
 MSG_DEF(JSMSG_USE_ASM_TYPE_OK,         1, JSEXN_WARN,    "Successfully compiled asm.js code ({0})")
 
 // wasm
 MSG_DEF(JSMSG_WASM_COMPILE_ERROR,      1, JSEXN_WASMCOMPILEERROR, "{0}")
+MSG_DEF(JSMSG_WASM_NO_SHMEM_COMPILE,   0, JSEXN_WASMCOMPILEERROR, "shared memory is disabled")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_TYPE,    2, JSEXN_WASMLINKERROR, "import object field '{0}' is not a {1}")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_SIG,     2, JSEXN_WASMLINKERROR, "imported function '{0}.{1}' signature mismatch")
 MSG_DEF(JSMSG_WASM_BAD_IMP_SIZE,       1, JSEXN_WASMLINKERROR, "imported {0} with incompatible size")
 MSG_DEF(JSMSG_WASM_BAD_IMP_MAX,        1, JSEXN_WASMLINKERROR, "imported {0} with incompatible maximum size")
 MSG_DEF(JSMSG_WASM_IMP_SHARED_REQD,    0, JSEXN_WASMLINKERROR, "imported unshared memory but shared required")
 MSG_DEF(JSMSG_WASM_IMP_SHARED_BANNED,  0, JSEXN_WASMLINKERROR, "imported shared memory but unshared required")
 MSG_DEF(JSMSG_WASM_BAD_FIT,            2, JSEXN_WASMLINKERROR, "{0} segment does not fit in {1}")
 MSG_DEF(JSMSG_WASM_BAD_I64_LINK,       0, JSEXN_WASMLINKERROR, "cannot pass i64 to or from JS")
+MSG_DEF(JSMSG_WASM_NO_SHMEM_LINK,      0, JSEXN_WASMLINKERROR, "shared memory is disabled")
 MSG_DEF(JSMSG_WASM_IND_CALL_TO_NULL,   0, JSEXN_WASMRUNTIMEERROR, "indirect call to null")
 MSG_DEF(JSMSG_WASM_IND_CALL_BAD_SIG,   0, JSEXN_WASMRUNTIMEERROR, "indirect call signature mismatch")
 MSG_DEF(JSMSG_WASM_UNREACHABLE,        0, JSEXN_WASMRUNTIMEERROR, "unreachable executed")
 MSG_DEF(JSMSG_WASM_INTEGER_OVERFLOW,   0, JSEXN_WASMRUNTIMEERROR, "integer overflow")
 MSG_DEF(JSMSG_WASM_INVALID_CONVERSION, 0, JSEXN_WASMRUNTIMEERROR, "invalid conversion to integer")
 MSG_DEF(JSMSG_WASM_INT_DIVIDE_BY_ZERO, 0, JSEXN_WASMRUNTIMEERROR, "integer divide by zero")
 MSG_DEF(JSMSG_WASM_OUT_OF_BOUNDS,      0, JSEXN_WASMRUNTIMEERROR, "index out of bounds")
 MSG_DEF(JSMSG_WASM_UNALIGNED_ACCESS,   0, JSEXN_WASMRUNTIMEERROR, "unaligned memory access")
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -873,16 +873,20 @@ js::CreateWasmBuffer(JSContext* cx, cons
         uint32_t clamp = (wasm::MaxMemoryMaximumPages - 2) * wasm::PageSize;
         MOZ_ASSERT(clamp < UINT32_MAX);
         MOZ_ASSERT(memory.initial <= clamp);
         maxSize = Some(clamp);
     }
 #endif
 
     if (memory.shared == wasm::Shareable::True) {
+        if (!cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled()) {
+            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_NO_SHMEM_LINK);
+            return false;
+        }
         return CreateBuffer<SharedArrayBufferObject, SharedArrayRawBuffer>(cx, memory.initial,
                                                                            maxSize, buffer);
     }
     return CreateBuffer<ArrayBufferObject, WasmArrayRawBuffer>(cx, memory.initial, maxSize,
                                                                buffer);
 }
 
 // Note this function can return false with or without an exception pending. The
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -1758,17 +1758,21 @@ class MOZ_STACK_CLASS ModuleValidator
         funcDefs_(cx),
         tables_(cx),
         globalMap_(cx),
         sigSet_(cx),
         funcImportMap_(cx),
         arrayViews_(cx),
         atomicsPresent_(false),
         simdPresent_(false),
-        env_(CompileMode::Once, Tier::Ion, DebugEnabled::False, ModuleKind::AsmJS),
+        env_(CompileMode::Once, Tier::Ion, DebugEnabled::False,
+             cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled()
+               ? Shareable::True
+               : Shareable::False,
+             ModuleKind::AsmJS),
         errorString_(nullptr),
         errorOffset_(UINT32_MAX),
         errorOverRecursed_(false)
     {
         env_.minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0);
     }
 
     ~ModuleValidator() {
--- a/js/src/wasm/WasmBinaryToAST.cpp
+++ b/js/src/wasm/WasmBinaryToAST.cpp
@@ -18,16 +18,17 @@
 
 #include "wasm/WasmBinaryToAST.h"
 
 #include "mozilla/CheckedInt.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Sprintf.h"
 
 #include "jscntxt.h"
+#include "jscompartment.h"
 
 #include "wasm/WasmBinaryIterator.h"
 #include "wasm/WasmValidate.h"
 
 using namespace js;
 using namespace js::wasm;
 
 using mozilla::CheckedInt;
@@ -99,16 +100,20 @@ class AstDecodeContext
 
   public:
     AstDecodeContext(JSContext* cx, LifoAlloc& lifo, Decoder& d, AstModule& module,
                      bool generateNames)
      : cx(cx),
        lifo(lifo),
        d(d),
        generateNames(generateNames),
+       env_(CompileMode::Once, Tier::Ion, DebugEnabled::False,
+            cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled()
+            ? Shareable::True
+            : Shareable::False),
        module_(module),
        iter_(nullptr),
        exprs_(lifo),
        depths_(lifo),
        locals_(nullptr),
        blockLabels_(lifo),
        currentLabelIndex_(0),
        retType_(ExprType::Limit)
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -84,16 +84,17 @@ DecodeCodeSection(const ModuleEnvironmen
     return mg.finishFuncDefs();
 }
 
 bool
 CompileArgs::initFromContext(JSContext* cx, ScriptedCaller&& scriptedCaller)
 {
     baselineEnabled = cx->options().wasmBaseline();
     ionEnabled = cx->options().wasmIon();
+    sharedMemoryEnabled = cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled();
     testTiering = cx->options().testWasmAwaitTier2();
 
     // Debug information such as source view or debug traps will require
     // additional memory and permanently stay in baseline code, so we try to
     // only enable it when a developer actually cares: when the debugger tab
     // is open.
     debugEnabled = cx->compartment()->debuggerObservesAsmJS();
 
@@ -417,17 +418,18 @@ wasm::CompileBuffer(const CompileArgs& a
 
     Decoder d(bytecode.bytes, 0, error);
 
     CompileMode mode;
     Tier tier;
     DebugEnabled debug;
     InitialCompileFlags(args, d, &mode, &tier, &debug);
 
-    ModuleEnvironment env(mode, tier, debug);
+    ModuleEnvironment env(mode, tier, debug,
+                          args.sharedMemoryEnabled ? Shareable::True : Shareable::False);
     if (!DecodeModuleEnvironment(d, &env))
         return nullptr;
 
     ModuleGenerator mg(args, &env, nullptr, error);
     if (!mg.init())
         return nullptr;
 
     if (!DecodeCodeSection(env, d, mg))
@@ -442,17 +444,18 @@ wasm::CompileBuffer(const CompileArgs& a
 bool
 wasm::CompileTier2(const CompileArgs& args, Module& module, Atomic<bool>* cancelled)
 {
     MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
 
     UniqueChars error;
     Decoder d(module.bytecode().bytes, 0, &error);
 
-    ModuleEnvironment env(CompileMode::Tier2, Tier::Ion, DebugEnabled::False);
+    ModuleEnvironment env(CompileMode::Tier2, Tier::Ion, DebugEnabled::False,
+                          args.sharedMemoryEnabled ? Shareable::True : Shareable::False);
     if (!DecodeModuleEnvironment(d, &env))
         return false;
 
     ModuleGenerator mg(args, &env, cancelled, &error);
     if (!mg.init())
         return false;
 
     if (!DecodeCodeSection(env, d, mg))
@@ -563,17 +566,18 @@ wasm::CompileStreaming(const CompileArgs
     {
         Decoder d(envBytes, 0, error);
 
         CompileMode mode;
         Tier tier;
         DebugEnabled debug;
         InitialCompileFlags(args, d, &mode, &tier, &debug);
 
-        env.emplace(mode, tier, debug);
+        env.emplace(mode, tier, debug,
+                    args.sharedMemoryEnabled ? Shareable::True : Shareable::False);
         if (!DecodeModuleEnvironment(d, env.ptr()))
             return nullptr;
 
         MOZ_ASSERT(d.done());
     }
 
     ModuleGenerator mg(args, env.ptr(), &cancelled, error);
     if (!mg.init())
--- a/js/src/wasm/WasmCompile.h
+++ b/js/src/wasm/WasmCompile.h
@@ -37,24 +37,26 @@ struct ScriptedCaller
 
 struct CompileArgs : ShareableBase<CompileArgs>
 {
     Assumptions assumptions;
     ScriptedCaller scriptedCaller;
     bool baselineEnabled;
     bool debugEnabled;
     bool ionEnabled;
+    bool sharedMemoryEnabled;
     bool testTiering;
 
     CompileArgs(Assumptions&& assumptions, ScriptedCaller&& scriptedCaller)
       : assumptions(Move(assumptions)),
         scriptedCaller(Move(scriptedCaller)),
         baselineEnabled(false),
         debugEnabled(false),
         ionEnabled(false),
+        sharedMemoryEnabled(false),
         testTiering(false)
     {}
 
     // If CompileArgs is constructed without arguments, initFromContext() must
     // be called to complete initialization.
     CompileArgs() = default;
     bool initFromContext(JSContext* cx, ScriptedCaller&& scriptedCaller);
 };
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -376,20 +376,28 @@ GetLimits(JSContext* cx, HandleObject ob
 
         if (foundShared) {
             RootedValue sharedVal(cx);
             if (!GetProperty(cx, obj, obj, sharedId, &sharedVal))
                 return false;
 
             limits->shared = ToBoolean(sharedVal) ? Shareable::True : Shareable::False;
 
-            if (limits->shared == Shareable::True && !foundMaximum) {
-                JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_MISSING_MAXIMUM,
-                                          kind);
-                return false;
+            if (limits->shared == Shareable::True) {
+                if (!foundMaximum) {
+                    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_MISSING_MAXIMUM,
+                                              kind);
+                    return false;
+                }
+
+                if (!cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled()) {
+                    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                              JSMSG_WASM_NO_SHMEM_LINK);
+                    return false;
+                }
             }
         }
     }
 #endif
 
     return true;
 }
 
@@ -2100,17 +2108,17 @@ WebAssembly_validate(JSContext* cx, unsi
 {
     CallArgs callArgs = CallArgsFromVp(argc, vp);
 
     MutableBytes bytecode;
     if (!GetBufferSource(cx, callArgs, "WebAssembly.validate", &bytecode))
         return false;
 
     UniqueChars error;
-    bool validated = Validate(*bytecode, &error);
+    bool validated = Validate(cx, *bytecode, &error);
 
     // If the reason for validation failure was OOM (signalled by null error
     // message), report out-of-memory so that validate's return is always
     // correct.
     if (!validated && !error) {
         ReportOutOfMemory(cx);
         return false;
     }
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -582,20 +582,33 @@ wasm::DeserializeModule(PRFileDesc* byte
 
     memcpy(bytecode->bytes.begin(), bytecodeMapping.get(), bytecodeInfo.size);
 
     ScriptedCaller scriptedCaller;
     scriptedCaller.filename = Move(filename);
     scriptedCaller.line = line;
     scriptedCaller.column = column;
 
-    SharedCompileArgs args = js_new<CompileArgs>(Assumptions(Move(buildId)), Move(scriptedCaller));
+    MutableCompileArgs args = js_new<CompileArgs>(Assumptions(Move(buildId)), Move(scriptedCaller));
     if (!args)
         return nullptr;
 
+    // The true answer to whether shared memory is enabled is provided by
+    // cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled()
+    // where cx is the context that originated the call that caused this
+    // deserialization attempt to happen.  We don't have that context here, so
+    // we assume that shared memory is enabled; we will catch a wrong assumption
+    // later, during instantiation.
+    //
+    // (We would prefer to store this value with the Assumptions when
+    // serializing, and for the caller of the deserialization machinery to
+    // provide the value from the originating context.)
+
+    args->sharedMemoryEnabled = true;
+
     UniqueChars error;
     return CompileBuffer(*args, *bytecode, &error);
 }
 
 /* virtual */ void
 Module::addSizeOfMisc(MallocSizeOf mallocSizeOf,
                       Metadata::SeenSet* seenMetadata,
                       ShareableBytes::SeenSet* seenBytes,
@@ -864,16 +877,21 @@ CheckLimits(JSContext* cx, uint32_t decl
     }
 
     return true;
 }
 
 static bool
 CheckSharing(JSContext* cx, bool declaredShared, bool isShared)
 {
+    if (isShared && !cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled()) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_NO_SHMEM_LINK);
+        return false;
+    }
+
     if (declaredShared && !isShared) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_IMP_SHARED_REQD);
         return false;
     }
 
     if (!declaredShared && isShared) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_IMP_SHARED_BANNED);
         return false;
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -15,16 +15,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "wasm/WasmValidate.h"
 
 #include "mozilla/CheckedInt.h"
 
+#include "jscntxt.h"
+#include "jscompartment.h"
 #include "jsprf.h"
 
 #include "jit/JitOptions.h"
 #include "wasm/WasmBinaryIterator.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
@@ -999,16 +1001,19 @@ DecodeMemoryLimits(Decoder& d, ModuleEnv
         CheckedInt<uint32_t> maximumBytes = *memory.maximum;
         maximumBytes *= PageSize;
 
         // Clamp the maximum memory value to UINT32_MAX; it's not semantically
         // visible since growing will fail for values greater than INT32_MAX.
         memory.maximum = Some(maximumBytes.isValid() ? maximumBytes.value() : UINT32_MAX);
     }
 
+    if (memory.shared == Shareable::True && env->sharedMemoryEnabled == Shareable::False)
+        return d.fail("shared memory is disabled");
+
     env->memoryUsage = memory.shared == Shareable::True
                      ? MemoryUsage::Shared
                      : MemoryUsage::Unshared;
     env->minMemoryLength = memory.initial;
     env->maxMemoryLength = memory.maximum;
     return true;
 }
 
@@ -1763,21 +1768,24 @@ wasm::DecodeModuleTail(Decoder& d, Modul
     }
 
     return true;
 }
 
 // Validate algorithm.
 
 bool
-wasm::Validate(const ShareableBytes& bytecode, UniqueChars* error)
+wasm::Validate(JSContext* cx, const ShareableBytes& bytecode, UniqueChars* error)
 {
     Decoder d(bytecode.bytes, 0, error);
 
-    ModuleEnvironment env;
+    ModuleEnvironment env(CompileMode::Once, Tier::Ion, DebugEnabled::False,
+                          cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled()
+                          ? Shareable::True
+                          : Shareable::False);
     if (!DecodeModuleEnvironment(d, &env))
         return false;
 
     if (!DecodeCodeSection(d, &env))
         return false;
 
     if (!DecodeModuleTail(d, &env))
         return false;
--- a/js/src/wasm/WasmValidate.h
+++ b/js/src/wasm/WasmValidate.h
@@ -54,16 +54,17 @@ typedef Maybe<SectionRange> MaybeSection
 // conditions.
 
 struct ModuleEnvironment
 {
     // Constant parameters for the entire compilation:
     const DebugEnabled        debug;
     const ModuleKind          kind;
     const CompileMode         mode;
+    const Shareable           sharedMemoryEnabled;
     const Tier                tier;
 
     // Module fields decoded from the module environment (or initialized while
     // validating an asm.js module) and immutable during compilation:
     MemoryUsage               memoryUsage;
     uint32_t                  minMemoryLength;
     Maybe<uint32_t>           maxMemoryLength;
     SigWithIdVector           sigs;
@@ -81,20 +82,22 @@ struct ModuleEnvironment
     ElemSegmentVector         elemSegments;
     DataSegmentVector         dataSegments;
     NameInBytecodeVector      funcNames;
     CustomSectionVector       customSections;
 
     explicit ModuleEnvironment(CompileMode mode = CompileMode::Once,
                                Tier tier = Tier::Ion,
                                DebugEnabled debug = DebugEnabled::False,
+                               Shareable sharedMemoryEnabled = Shareable::False,
                                ModuleKind kind = ModuleKind::Wasm)
       : debug(debug),
         kind(kind),
         mode(mode),
+        sharedMemoryEnabled(sharedMemoryEnabled),
         tier(tier),
         memoryUsage(MemoryUsage::None),
         minMemoryLength(0)
     {}
 
     size_t numTables() const {
         return tables.length();
     }
@@ -710,14 +713,14 @@ MOZ_MUST_USE bool
 DecodeModuleTail(Decoder& d, ModuleEnvironment* env);
 
 // Validate an entire module, returning true if the module was validated
 // successfully. If Validate returns false:
 //  - if *error is null, the caller should report out-of-memory
 //  - otherwise, there was a legitimate error described by *error
 
 MOZ_MUST_USE bool
-Validate(const ShareableBytes& bytecode, UniqueChars* error);
+Validate(JSContext* cx, const ShareableBytes& bytecode, UniqueChars* error);
 
 }  // namespace wasm
 }  // namespace js
 
 #endif // namespace wasm_validate_h