Bug 1289080: Don't allow call_indirect without a table and memory accesses without memory; r=luke
authorBenjamin Bouvier <benj@benj.me>
Mon, 25 Jul 2016 14:54:50 +0200
changeset 348672 387964e7a8eb8f5c5c10f214fa3ec33a2aa55cfa
parent 348671 0719f3b4d3659141f70ad53d9b961c5b2e81dfb5
child 348673 836321463a2642ed518990efb5cbef4f9cf5498d
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1289080
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1289080: Don't allow call_indirect without a table and memory accesses without memory; r=luke MozReview-Commit-ID: DkuyBiGsaxs
js/src/asmjs/WasmCompile.cpp
js/src/asmjs/WasmGenerator.h
js/src/jit-test/tests/wasm/basic-memory.js
js/src/jit-test/tests/wasm/basic.js
--- a/js/src/asmjs/WasmCompile.cpp
+++ b/js/src/asmjs/WasmCompile.cpp
@@ -74,16 +74,22 @@ class FunctionDecoder
     ValidatingExprIter& iter() { return iter_; }
     const ValTypeVector& locals() const { return locals_; }
 
     bool checkI64Support() {
         if (!IsI64Implemented())
             return iter().notYetImplemented("i64 NYI on this platform");
         return true;
     }
+
+    bool checkHasMemory() {
+        if (!mg().usesMemory())
+            return iter().fail("can't touch memory without memory");
+        return true;
+    }
 };
 
 } // end anonymous namespace
 
 static bool
 CheckValType(Decoder& d, ValType type)
 {
     switch (type) {
@@ -141,16 +147,19 @@ DecodeCall(FunctionDecoder& f)
     const Sig& sig = f.mg().funcSig(calleeIndex);
     return DecodeCallArgs(f, arity, sig) &&
            DecodeCallReturn(f, sig);
 }
 
 static bool
 DecodeCallIndirect(FunctionDecoder& f)
 {
+    if (!f.mg().numTables())
+        return f.iter().fail("can't call_indirect without a table");
+
     uint32_t sigIndex;
     uint32_t arity;
     if (!f.iter().readCallIndirect(&sigIndex, &arity))
         return false;
 
     if (sigIndex >= f.mg().numSigs())
         return f.iter().fail("signature index out of range");
 
@@ -393,63 +402,81 @@ DecodeExpr(FunctionDecoder& f)
       case Expr::F64ConvertUI64:
       case Expr::F64ReinterpretI64:
         return f.checkI64Support() &&
                f.iter().readConversion(ValType::I64, ValType::F64, nullptr);
       case Expr::F64PromoteF32:
         return f.iter().readConversion(ValType::F32, ValType::F64, nullptr);
       case Expr::I32Load8S:
       case Expr::I32Load8U:
-        return f.iter().readLoad(ValType::I32, 1, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readLoad(ValType::I32, 1, nullptr);
       case Expr::I32Load16S:
       case Expr::I32Load16U:
-        return f.iter().readLoad(ValType::I32, 2, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readLoad(ValType::I32, 2, nullptr);
       case Expr::I32Load:
-        return f.iter().readLoad(ValType::I32, 4, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readLoad(ValType::I32, 4, nullptr);
       case Expr::I64Load8S:
       case Expr::I64Load8U:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readLoad(ValType::I64, 1, nullptr);
       case Expr::I64Load16S:
       case Expr::I64Load16U:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readLoad(ValType::I64, 2, nullptr);
       case Expr::I64Load32S:
       case Expr::I64Load32U:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readLoad(ValType::I64, 4, nullptr);
       case Expr::I64Load:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readLoad(ValType::I64, 8, nullptr);
       case Expr::F32Load:
-        return f.iter().readLoad(ValType::F32, 4, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readLoad(ValType::F32, 4, nullptr);
       case Expr::F64Load:
-        return f.iter().readLoad(ValType::F64, 8, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readLoad(ValType::F64, 8, nullptr);
       case Expr::I32Store8:
-        return f.iter().readStore(ValType::I32, 1, nullptr, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readStore(ValType::I32, 1, nullptr, nullptr);
       case Expr::I32Store16:
-        return f.iter().readStore(ValType::I32, 2, nullptr, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readStore(ValType::I32, 2, nullptr, nullptr);
       case Expr::I32Store:
-        return f.iter().readStore(ValType::I32, 4, nullptr, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readStore(ValType::I32, 4, nullptr, nullptr);
       case Expr::I64Store8:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readStore(ValType::I64, 1, nullptr, nullptr);
       case Expr::I64Store16:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readStore(ValType::I64, 2, nullptr, nullptr);
       case Expr::I64Store32:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readStore(ValType::I64, 4, nullptr, nullptr);
       case Expr::I64Store:
         return f.checkI64Support() &&
+               f.checkHasMemory() &&
                f.iter().readStore(ValType::I64, 8, nullptr, nullptr);
       case Expr::F32Store:
-        return f.iter().readStore(ValType::F32, 4, nullptr, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readStore(ValType::F32, 4, nullptr, nullptr);
       case Expr::F64Store:
-        return f.iter().readStore(ValType::F64, 8, nullptr, nullptr);
+        return f.checkHasMemory() &&
+               f.iter().readStore(ValType::F64, 8, nullptr, nullptr);
       case Expr::Br:
         return f.iter().readBr(nullptr, nullptr, nullptr);
       case Expr::BrIf:
         return f.iter().readBrIf(nullptr, nullptr, nullptr, nullptr);
       case Expr::BrTable:
         return DecodeBrTable(f);
       case Expr::Return:
         return f.iter().readReturn(nullptr);
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -148,16 +148,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     SignalUsage usesSignal() const { return metadata_->assumptions.usesSignal; }
     jit::MacroAssembler& masm() { return masm_; }
 
     // Memory:
     bool usesMemory() const { return UsesMemory(shared_->memoryUsage); }
     uint32_t minMemoryLength() const { return shared_->minMemoryLength; }
 
     // Tables:
+    uint32_t numTables() const { return numTables_; }
     const TableDescVector& tables() const { return shared_->tables; }
 
     // Signatures:
     uint32_t numSigs() const { return numSigs_; }
     const SigWithId& sig(uint32_t sigIndex) const;
 
     // Function declarations:
     uint32_t numFuncSigs() const { return shared_->funcSigs.length(); }
--- a/js/src/jit-test/tests/wasm/basic-memory.js
+++ b/js/src/jit-test/tests/wasm/basic-memory.js
@@ -99,16 +99,62 @@ function testStore(type, ext, base, offs
 }
 
 function testStoreOOB(type, ext, base, offset, align, value) {
     if (type === 'i64')
         value = createI64(value);
     assertErrorMessage(() => storeModule(type, ext, offset, align).store(base, value), Error, /invalid or out-of-range index/);
 }
 
+function badLoadModule(type, ext) {
+    return wasmEvalText( `(module (func (param i32) (${type}.load${ext} (get_local 0))) (export "" 0))`);
+}
+
+function badStoreModule(type, ext) {
+    return wasmEvalText( `(module (func (param i32) (${type}.store${ext} (get_local 0) (${type}.const 0))) (export "" 0))`);
+}
+
+// Can't touch memory.
+for (let [type, ext] of [
+    ['i32', ''],
+    ['i32', '8_s'],
+    ['i32', '8_u'],
+    ['i32', '16_s'],
+    ['i32', '16_u'],
+    ['i64', ''],
+    ['i64', '8_s'],
+    ['i64', '8_u'],
+    ['i64', '16_s'],
+    ['i64', '16_u'],
+    ['i64', '32_s'],
+    ['i64', '32_u'],
+    ['f32', ''],
+    ['f64', ''],
+])
+{
+    if (type !== 'i64' || hasI64())
+        assertErrorMessage(() => badLoadModule(type, ext), TypeError, /can't touch memory/);
+}
+
+for (let [type, ext] of [
+    ['i32', ''],
+    ['i32', '8'],
+    ['i32', '16'],
+    ['i64', ''],
+    ['i64', '8'],
+    ['i64', '16'],
+    ['i64', '32'],
+    ['f32', ''],
+    ['f64', ''],
+])
+{
+    if (type !== 'i64' || hasI64())
+        assertErrorMessage(() => badStoreModule(type, ext), TypeError, /can't touch memory/);
+}
+
 // Test bounds checks and edge cases.
 const align = 0;
 
 for (var ind = 0; ind < 2; ind++) {
   if (ind == 1)
     setJitCompilerOption('wasm.explicit-bounds-checks', 1);
 
   testLoad('i32', '', 0, 0, 0, 0x03020100);
--- a/js/src/jit-test/tests/wasm/basic.js
+++ b/js/src/jit-test/tests/wasm/basic.js
@@ -398,16 +398,18 @@ if (hasI64()) {
     (export "" 0))`, imp)(), { low: 1337, high: 0x12345678 });
 
     setJitCompilerOption('wasm.test-mode', 0);
 } else {
     assertErrorMessage(() => wasmEvalText('(module (import "a" "" (param i64) (result i32)))'), TypeError, /NYI/);
     assertErrorMessage(() => wasmEvalText('(module (import "a" "" (result i64)))'), TypeError, /NYI/);
 }
 
+assertErrorMessage(() => wasmEvalText(`(module (type $t (func)) (func (call_indirect $t (i32.const 0))))`), TypeError, /can't call_indirect without a table/);
+
 var {v2i, i2i, i2v} = wasmEvalText(`(module
     (type (func (result i32)))
     (type (func (param i32) (result i32)))
     (type (func (param i32)))
     (func (type 0) (i32.const 13))
     (func (type 0) (i32.const 42))
     (func (type 1) (i32.add (get_local 0) (i32.const 1)))
     (func (type 1) (i32.add (get_local 0) (i32.const 2)))