mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 23:35:34 +00:00
Bug 1717914 - wasm: Introduce sourceMaxPages and clampedMaxPages for memories. r=lth
Wasm memory objects contain a maxPages field which is ambiguous. It is usually the maximum specified in the module source, but may be clamped on 32-bit systems. We use this maximum for linking and type reflection which is not correct. This commit splits the dual uses out into two separate variables. - sourceMaxPages: The original maximum pages from the module, may not be present. Used for linking and reflection. - clampedMaxPages: The computed maximum pages the memory may grow to. Always present, even if the memory does not specify a max. Used for limiting what size a memory may grow to, while still respecting both the source maximum and implementation limits. The SMDOC for linear memory is updated with the invariants that these variables hold. Differential Revision: https://phabricator.services.mozilla.com/D118649
This commit is contained in:
parent
60ef57ffc0
commit
f162f75a6a
@ -9,6 +9,7 @@ excluded_tests = [
|
||||
# bulk-memory-operations/issues/133 (data.wast:161)
|
||||
"data.wast",
|
||||
# memory limits can be encoded with more bytes now
|
||||
"binary.wast",
|
||||
"binary-leb128.wast",
|
||||
# testing that multiple tables fail (imports.wast:309)
|
||||
"imports.wast",
|
||||
|
32
js/src/jit-test/tests/wasm/memory-maximum-clamping.js
Normal file
32
js/src/jit-test/tests/wasm/memory-maximum-clamping.js
Normal file
@ -0,0 +1,32 @@
|
||||
const MemoryMaxValid = 65536;
|
||||
|
||||
// Linking should fail if the imported memory has a higher maximum than required,
|
||||
// however if we internally clamp maximum values to an implementation limit
|
||||
// and use that for linking we may erroneously accept some modules.
|
||||
|
||||
function testLinkFail(importMax, importedMax) {
|
||||
assertErrorMessage(() => {
|
||||
let importedMemory = new WebAssembly.Memory({
|
||||
initial: 0,
|
||||
maximum: importedMax,
|
||||
});
|
||||
wasmEvalText(`(module
|
||||
(memory (import "" "") 0 ${importMax})
|
||||
)`, {"": {"": importedMemory}});
|
||||
}, WebAssembly.LinkError, /incompatible maximum/);
|
||||
}
|
||||
|
||||
testLinkFail(0, 1);
|
||||
testLinkFail(MemoryMaxValid - 1, MemoryMaxValid);
|
||||
|
||||
// The type reflection interface for WebAssembly.Memory should not report
|
||||
// an internally clamped maximum.
|
||||
|
||||
if ('type' in WebAssembly.Memory.prototype) {
|
||||
let memory = new WebAssembly.Memory({
|
||||
initial: 0,
|
||||
maximum: MemoryMaxValid,
|
||||
});
|
||||
let type = memory.type();
|
||||
assertEq(type.maximum, MemoryMaxValid, 'reported memory maximum is not clamped');
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,885 +0,0 @@
|
||||
/* Copyright 2021 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ./test/core/binary.wast
|
||||
|
||||
// ./test/core/binary.wast:1
|
||||
let $0 = instantiate(`(module binary "\\00asm\\01\\00\\00\\00")`);
|
||||
|
||||
// ./test/core/binary.wast:2
|
||||
let $1 = instantiate(`(module binary "\\00asm" "\\01\\00\\00\\00")`);
|
||||
|
||||
// ./test/core/binary.wast:3
|
||||
let $2 = instantiate(`(module $$M1 binary "\\00asm\\01\\00\\00\\00")`);
|
||||
register($2, `M1`);
|
||||
|
||||
// ./test/core/binary.wast:4
|
||||
let $3 = instantiate(`(module $$M2 binary "\\00asm" "\\01\\00\\00\\00")`);
|
||||
register($3, `M2`);
|
||||
|
||||
// ./test/core/binary.wast:6
|
||||
assert_malformed(() => instantiate(`(module binary "")`), `unexpected end`);
|
||||
|
||||
// ./test/core/binary.wast:7
|
||||
assert_malformed(() => instantiate(`(module binary "\\01")`), `unexpected end`);
|
||||
|
||||
// ./test/core/binary.wast:8
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\00as")`),
|
||||
`unexpected end`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:9
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "asm\\00")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:10
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "msa\\00")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:11
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "msa\\00\\01\\00\\00\\00")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:12
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "msa\\00\\00\\00\\00\\01")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:13
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "asm\\01\\00\\00\\00\\00")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:14
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "wasm\\01\\00\\00\\00")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:15
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\7fasm\\01\\00\\00\\00")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:16
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\80asm\\01\\00\\00\\00")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:17
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\82asm\\01\\00\\00\\00")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:18
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\ffasm\\01\\00\\00\\00")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:21
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\00\\00\\00\\01msa\\00")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:24
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "a\\00ms\\00\\01\\00\\00")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:25
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "sm\\00a\\00\\00\\01\\00")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:28
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\00ASM\\01\\00\\00\\00")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:31
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\00\\81\\a2\\94\\01\\00\\00\\00")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:34
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\ef\\bb\\bf\\00asm\\01\\00\\00\\00")`),
|
||||
`magic header not detected`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:37
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\00asm")`),
|
||||
`unexpected end`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:38
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\00asm\\01")`),
|
||||
`unexpected end`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:39
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\00asm\\01\\00\\00")`),
|
||||
`unexpected end`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:40
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\00asm\\00\\00\\00\\00")`),
|
||||
`unknown binary version`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:41
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\00asm\\0d\\00\\00\\00")`),
|
||||
`unknown binary version`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:42
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\00asm\\0e\\00\\00\\00")`),
|
||||
`unknown binary version`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:43
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\00asm\\00\\01\\00\\00")`),
|
||||
`unknown binary version`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:44
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\00asm\\00\\00\\01\\00")`),
|
||||
`unknown binary version`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:45
|
||||
assert_malformed(
|
||||
() => instantiate(`(module binary "\\00asm\\00\\00\\00\\01")`),
|
||||
`unknown binary version`,
|
||||
);
|
||||
|
||||
// ./test/core/binary.wast:49
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\04\\04\\01\\70\\00\\00" ;; Table section
|
||||
"\\0a\\09\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\07\\00"
|
||||
"\\41\\00" ;; i32.const 0
|
||||
"\\11\\00" ;; call_indirect (type 0)
|
||||
"\\01" ;; call_indirect reserved byte is not equal to zero!
|
||||
"\\0b" ;; end
|
||||
)`), `zero flag expected`);
|
||||
|
||||
// ./test/core/binary.wast:68
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\04\\04\\01\\70\\00\\00" ;; Table section
|
||||
"\\0a\\0a\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\07\\00"
|
||||
"\\41\\00" ;; i32.const 0
|
||||
"\\11\\00" ;; call_indirect (type 0)
|
||||
"\\80\\00" ;; call_indirect reserved byte
|
||||
"\\0b" ;; end
|
||||
)`), `zero flag expected`);
|
||||
|
||||
// ./test/core/binary.wast:87
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\04\\04\\01\\70\\00\\00" ;; Table section
|
||||
"\\0a\\0b\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\08\\00"
|
||||
"\\41\\00" ;; i32.const 0
|
||||
"\\11\\00" ;; call_indirect (type 0)
|
||||
"\\80\\80\\00" ;; call_indirect reserved byte
|
||||
"\\0b" ;; end
|
||||
)`), `zero flag expected`);
|
||||
|
||||
// ./test/core/binary.wast:105
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\04\\04\\01\\70\\00\\00" ;; Table section
|
||||
"\\0a\\0c\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\09\\00"
|
||||
"\\41\\00" ;; i32.const 0
|
||||
"\\11\\00" ;; call_indirect (type 0)
|
||||
"\\80\\80\\80\\00" ;; call_indirect reserved byte
|
||||
"\\0b" ;; end
|
||||
)`), `zero flag expected`);
|
||||
|
||||
// ./test/core/binary.wast:123
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\04\\04\\01\\70\\00\\00" ;; Table section
|
||||
"\\0a\\0d\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\0a\\00"
|
||||
"\\41\\00" ;; i32.const 0
|
||||
"\\11\\00" ;; call_indirect (type 0)
|
||||
"\\80\\80\\80\\80\\00" ;; call_indirect reserved byte
|
||||
"\\0b" ;; end
|
||||
)`), `zero flag expected`);
|
||||
|
||||
// ./test/core/binary.wast:142
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\05\\03\\01\\00\\00" ;; Memory section
|
||||
"\\0a\\09\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\07\\00"
|
||||
"\\41\\00" ;; i32.const 0
|
||||
"\\40" ;; memory.grow
|
||||
"\\01" ;; memory.grow reserved byte is not equal to zero!
|
||||
"\\1a" ;; drop
|
||||
"\\0b" ;; end
|
||||
)`), `zero flag expected`);
|
||||
|
||||
// ./test/core/binary.wast:162
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\05\\03\\01\\00\\00" ;; Memory section
|
||||
"\\0a\\0a\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\08\\00"
|
||||
"\\41\\00" ;; i32.const 0
|
||||
"\\40" ;; memory.grow
|
||||
"\\80\\00" ;; memory.grow reserved byte
|
||||
"\\1a" ;; drop
|
||||
"\\0b" ;; end
|
||||
)`), `zero flag expected`);
|
||||
|
||||
// ./test/core/binary.wast:182
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\05\\03\\01\\00\\00" ;; Memory section
|
||||
"\\0a\\0b\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\09\\00"
|
||||
"\\41\\00" ;; i32.const 0
|
||||
"\\40" ;; memory.grow
|
||||
"\\80\\80\\00" ;; memory.grow reserved byte
|
||||
"\\1a" ;; drop
|
||||
"\\0b" ;; end
|
||||
)`), `zero flag expected`);
|
||||
|
||||
// ./test/core/binary.wast:201
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\05\\03\\01\\00\\00" ;; Memory section
|
||||
"\\0a\\0c\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\0a\\00"
|
||||
"\\41\\00" ;; i32.const 0
|
||||
"\\40" ;; memory.grow
|
||||
"\\80\\80\\80\\00" ;; memory.grow reserved byte
|
||||
"\\1a" ;; drop
|
||||
"\\0b" ;; end
|
||||
)`), `zero flag expected`);
|
||||
|
||||
// ./test/core/binary.wast:220
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\05\\03\\01\\00\\00" ;; Memory section
|
||||
"\\0a\\0d\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\0b\\00"
|
||||
"\\41\\00" ;; i32.const 0
|
||||
"\\40" ;; memory.grow
|
||||
"\\80\\80\\80\\80\\00" ;; memory.grow reserved byte
|
||||
"\\1a" ;; drop
|
||||
"\\0b" ;; end
|
||||
)`), `zero flag expected`);
|
||||
|
||||
// ./test/core/binary.wast:240
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\05\\03\\01\\00\\00" ;; Memory section
|
||||
"\\0a\\07\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\05\\00"
|
||||
"\\3f" ;; memory.size
|
||||
"\\01" ;; memory.size reserved byte is not equal to zero!
|
||||
"\\1a" ;; drop
|
||||
"\\0b" ;; end
|
||||
)`), `zero flag expected`);
|
||||
|
||||
// ./test/core/binary.wast:259
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\05\\03\\01\\00\\00" ;; Memory section
|
||||
"\\0a\\08\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\06\\00"
|
||||
"\\3f" ;; memory.size
|
||||
"\\80\\00" ;; memory.size reserved byte
|
||||
"\\1a" ;; drop
|
||||
"\\0b" ;; end
|
||||
)`), `zero flag expected`);
|
||||
|
||||
// ./test/core/binary.wast:278
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\05\\03\\01\\00\\00" ;; Memory section
|
||||
"\\0a\\09\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\07\\00"
|
||||
"\\3f" ;; memory.size
|
||||
"\\80\\80\\00" ;; memory.size reserved byte
|
||||
"\\1a" ;; drop
|
||||
"\\0b" ;; end
|
||||
)`), `zero flag expected`);
|
||||
|
||||
// ./test/core/binary.wast:296
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\05\\03\\01\\00\\00" ;; Memory section
|
||||
"\\0a\\0a\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\08\\00"
|
||||
"\\3f" ;; memory.size
|
||||
"\\80\\80\\80\\00" ;; memory.size reserved byte
|
||||
"\\1a" ;; drop
|
||||
"\\0b" ;; end
|
||||
)`), `zero flag expected`);
|
||||
|
||||
// ./test/core/binary.wast:314
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\05\\03\\01\\00\\00" ;; Memory section
|
||||
"\\0a\\0b\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\09\\00"
|
||||
"\\3f" ;; memory.size
|
||||
"\\80\\80\\80\\80\\00" ;; memory.size reserved byte
|
||||
"\\1a" ;; drop
|
||||
"\\0b" ;; end
|
||||
)`), `zero flag expected`);
|
||||
|
||||
// ./test/core/binary.wast:333
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\0a\\0c\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\0a\\02"
|
||||
"\\ff\\ff\\ff\\ff\\0f\\7f" ;; 0xFFFFFFFF i32
|
||||
"\\02\\7e" ;; 0x00000002 i64
|
||||
"\\0b" ;; end
|
||||
)`), `too many locals`);
|
||||
|
||||
// ./test/core/binary.wast:350
|
||||
let $4 = instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\0a\\0a\\01" ;; Code section
|
||||
|
||||
;; function 0
|
||||
"\\08\\03"
|
||||
"\\00\\7f" ;; 0 i32
|
||||
"\\00\\7e" ;; 0 i64
|
||||
"\\02\\7d" ;; 2 f32
|
||||
"\\0b" ;; end
|
||||
)`);
|
||||
|
||||
// ./test/core/binary.wast:365
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\03\\02\\00\\00" ;; Function section with 2 functions
|
||||
)`), `function and code section have inconsistent lengths`);
|
||||
|
||||
// ./test/core/binary.wast:375
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\0a\\04\\01\\02\\00\\0b" ;; Code section with 1 empty function
|
||||
)`), `function and code section have inconsistent lengths`);
|
||||
|
||||
// ./test/core/binary.wast:384
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\03\\02\\00\\00" ;; Function section with 2 functions
|
||||
"\\0a\\04\\01\\02\\00\\0b" ;; Code section with 1 empty function
|
||||
)`), `function and code section have inconsistent lengths`);
|
||||
|
||||
// ./test/core/binary.wast:395
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section with 1 function
|
||||
"\\0a\\07\\02\\02\\00\\0b\\02\\00\\0b" ;; Code section with 2 empty functions
|
||||
)`), `function and code section have inconsistent lengths`);
|
||||
|
||||
// ./test/core/binary.wast:406
|
||||
let $5 = instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\03\\01\\00" ;; Function section with 0 functions
|
||||
)`);
|
||||
|
||||
// ./test/core/binary.wast:412
|
||||
let $6 = instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\0a\\01\\00" ;; Code section with 0 functions
|
||||
)`);
|
||||
|
||||
// ./test/core/binary.wast:418
|
||||
let $7 = instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\01\\00" ;; type count can be zero
|
||||
)`);
|
||||
|
||||
// ./test/core/binary.wast:424
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\07\\02" ;; type section with inconsistent count (2 declared, 1 given)
|
||||
"\\60\\00\\00" ;; 1st type
|
||||
;; "\\60\\00\\00" ;; 2nd type (missed)
|
||||
)`), `unexpected end of section or function`);
|
||||
|
||||
// ./test/core/binary.wast:435
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\07\\01" ;; type section with inconsistent count (1 declared, 2 given)
|
||||
"\\60\\00\\00" ;; 1st type
|
||||
"\\60\\00\\00" ;; 2nd type (redundant)
|
||||
)`), `section size mismatch`);
|
||||
|
||||
// ./test/core/binary.wast:446
|
||||
let $8 = instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\05\\01" ;; type section
|
||||
"\\60\\01\\7f\\00" ;; type 0
|
||||
"\\02\\01\\00" ;; import count can be zero
|
||||
)`);
|
||||
|
||||
// ./test/core/binary.wast:454
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\05\\01" ;; type section
|
||||
"\\60\\01\\7f\\00" ;; type 0
|
||||
"\\02\\16\\02" ;; import section with inconsistent count (2 declared, 1 given)
|
||||
;; 1st import
|
||||
"\\08" ;; string length
|
||||
"\\73\\70\\65\\63\\74\\65\\73\\74" ;; spectest
|
||||
"\\09" ;; string length
|
||||
"\\70\\72\\69\\6e\\74\\5f\\69\\33\\32" ;; print_i32
|
||||
"\\00\\00" ;; import kind, import signature index
|
||||
;; 2nd import
|
||||
;; (missed)
|
||||
)`), `unexpected end of section or function`);
|
||||
|
||||
// ./test/core/binary.wast:473
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\09\\02" ;; type section
|
||||
"\\60\\01\\7f\\00" ;; type 0
|
||||
"\\60\\01\\7d\\00" ;; type 1
|
||||
"\\02\\2b\\01" ;; import section with inconsistent count (1 declared, 2 given)
|
||||
;; 1st import
|
||||
"\\08" ;; string length
|
||||
"\\73\\70\\65\\63\\74\\65\\73\\74" ;; spectest
|
||||
"\\09" ;; string length
|
||||
"\\70\\72\\69\\6e\\74\\5f\\69\\33\\32" ;; print_i32
|
||||
"\\00\\00" ;; import kind, import signature index
|
||||
;; 2nd import
|
||||
;; (redundant)
|
||||
"\\08" ;; string length
|
||||
"\\73\\70\\65\\63\\74\\65\\73\\74" ;; spectest
|
||||
"\\09" ;; string length
|
||||
"\\70\\72\\69\\6e\\74\\5f\\66\\33\\32" ;; print_f32
|
||||
"\\00\\01" ;; import kind, import signature index
|
||||
)`), `section size mismatch`);
|
||||
|
||||
// ./test/core/binary.wast:498
|
||||
let $9 = instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\04\\01\\00" ;; table count can be zero
|
||||
)`);
|
||||
|
||||
// ./test/core/binary.wast:504
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\04\\01\\01" ;; table section with inconsistent count (1 declared, 0 given)
|
||||
;; "\\70\\01\\00\\00" ;; table entity
|
||||
)`), `unexpected end of section or function`);
|
||||
|
||||
// ./test/core/binary.wast:514
|
||||
let $10 = instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\05\\01\\00" ;; memory count can be zero
|
||||
)`);
|
||||
|
||||
// ./test/core/binary.wast:520
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\05\\01\\01" ;; memory section with inconsistent count (1 declared, 0 given)
|
||||
;; "\\00\\00" ;; memory 0 (missed)
|
||||
)`), `unexpected end of section or function`);
|
||||
|
||||
// ./test/core/binary.wast:530
|
||||
let $11 = instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\06\\01\\00" ;; global count can be zero
|
||||
)`);
|
||||
|
||||
// ./test/core/binary.wast:536
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\06\\06\\02" ;; global section with inconsistent count (2 declared, 1 given)
|
||||
"\\7f\\00\\41\\00\\0b" ;; global 0
|
||||
;; "\\7f\\00\\41\\00\\0b" ;; global 1 (missed)
|
||||
)`), `unexpected end of section or function`);
|
||||
|
||||
// ./test/core/binary.wast:547
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\06\\0b\\01" ;; global section with inconsistent count (1 declared, 2 given)
|
||||
"\\7f\\00\\41\\00\\0b" ;; global 0
|
||||
"\\7f\\00\\41\\00\\0b" ;; global 1 (redundant)
|
||||
)`), `section size mismatch`);
|
||||
|
||||
// ./test/core/binary.wast:558
|
||||
let $12 = instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01" ;; type section
|
||||
"\\60\\00\\00" ;; type 0
|
||||
"\\03\\03\\02\\00\\00" ;; func section
|
||||
"\\07\\01\\00" ;; export count can be zero
|
||||
"\\0a\\07\\02" ;; code section
|
||||
"\\02\\00\\0b" ;; function body 0
|
||||
"\\02\\00\\0b" ;; function body 1
|
||||
)`);
|
||||
|
||||
// ./test/core/binary.wast:570
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01" ;; type section
|
||||
"\\60\\00\\00" ;; type 0
|
||||
"\\03\\03\\02\\00\\00" ;; func section
|
||||
"\\07\\06\\02" ;; export section with inconsistent count (2 declared, 1 given)
|
||||
"\\02" ;; export 0
|
||||
"\\66\\31" ;; export name
|
||||
"\\00\\00" ;; export kind, export func index
|
||||
;; "\\02" ;; export 1 (missed)
|
||||
;; "\\66\\32" ;; export name
|
||||
;; "\\00\\01" ;; export kind, export func index
|
||||
"\\0a\\07\\02" ;; code section
|
||||
"\\02\\00\\0b" ;; function body 0
|
||||
"\\02\\00\\0b" ;; function body 1
|
||||
)`), `unexpected end of section or function`);
|
||||
|
||||
// ./test/core/binary.wast:591
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01" ;; type section
|
||||
"\\60\\00\\00" ;; type 0
|
||||
"\\03\\03\\02\\00\\00" ;; func section
|
||||
"\\07\\0b\\01" ;; export section with inconsistent count (1 declared, 2 given)
|
||||
"\\02" ;; export 0
|
||||
"\\66\\31" ;; export name
|
||||
"\\00\\00" ;; export kind, export func index
|
||||
"\\02" ;; export 1 (redundant)
|
||||
"\\66\\32" ;; export name
|
||||
"\\00\\01" ;; export kind, export func index
|
||||
"\\0a\\07\\02" ;; code section
|
||||
"\\02\\00\\0b" ;; function body 0
|
||||
"\\02\\00\\0b" ;; function body 1
|
||||
)`), `section size mismatch`);
|
||||
|
||||
// ./test/core/binary.wast:612
|
||||
let $13 = instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01" ;; type section
|
||||
"\\60\\00\\00" ;; type 0
|
||||
"\\03\\02\\01\\00" ;; func section
|
||||
"\\04\\04\\01" ;; table section
|
||||
"\\70\\00\\01" ;; table 0
|
||||
"\\09\\01\\00" ;; elem segment count can be zero
|
||||
"\\0a\\04\\01" ;; code section
|
||||
"\\02\\00\\0b" ;; function body
|
||||
)`);
|
||||
|
||||
// ./test/core/binary.wast:625
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01" ;; type section
|
||||
"\\60\\00\\00" ;; type 0
|
||||
"\\03\\02\\01\\00" ;; func section
|
||||
"\\04\\04\\01" ;; table section
|
||||
"\\70\\00\\01" ;; table 0
|
||||
"\\09\\07\\02" ;; elem with inconsistent segment count (2 declared, 1 given)
|
||||
"\\00\\41\\00\\0b\\01\\00" ;; elem 0
|
||||
;; "\\00\\41\\00\\0b\\01\\00" ;; elem 1 (missed)
|
||||
"\\0a\\04\\01" ;; code section
|
||||
"\\02\\00\\0b" ;; function body
|
||||
)`), `invalid value type`);
|
||||
|
||||
// ./test/core/binary.wast:643
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01" ;; type section
|
||||
"\\60\\00\\00" ;; type 0
|
||||
"\\03\\02\\01\\00" ;; func section
|
||||
"\\04\\04\\01" ;; table section
|
||||
"\\70\\00\\01" ;; table 0
|
||||
"\\09\\0d\\01" ;; elem with inconsistent segment count (1 declared, 2 given)
|
||||
"\\00\\41\\00\\0b\\01\\00" ;; elem 0
|
||||
"\\00\\41\\00\\0b\\01\\00" ;; elem 1 (redundant)
|
||||
"\\0a\\04\\01" ;; code section
|
||||
"\\02\\00\\0b" ;; function body
|
||||
)`), `section size mismatch`);
|
||||
|
||||
// ./test/core/binary.wast:661
|
||||
let $14 = instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\05\\03\\01" ;; memory section
|
||||
"\\00\\01" ;; memory 0
|
||||
"\\0b\\01\\00" ;; data segment count can be zero
|
||||
)`);
|
||||
|
||||
// ./test/core/binary.wast:669
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\05\\03\\01" ;; memory section
|
||||
"\\00\\01" ;; memory 0
|
||||
"\\0b\\07\\02" ;; data with inconsistent segment count (2 declared, 1 given)
|
||||
"\\00\\41\\00\\0b\\01\\61" ;; data 0
|
||||
;; "\\00\\41\\01\\0b\\01\\62" ;; data 1 (missed)
|
||||
)`), `unexpected end of section or function`);
|
||||
|
||||
// ./test/core/binary.wast:682
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\05\\03\\01" ;; memory section
|
||||
"\\00\\01" ;; memory 0
|
||||
"\\0b\\0d\\01" ;; data with inconsistent segment count (1 declared, 2 given)
|
||||
"\\00\\41\\00\\0b\\01\\61" ;; data 0
|
||||
"\\00\\41\\01\\0b\\01\\62" ;; data 1 (redundant)
|
||||
)`), `section size mismatch`);
|
||||
|
||||
// ./test/core/binary.wast:695
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\05\\03\\01" ;; memory section
|
||||
"\\00\\01" ;; memory 0
|
||||
"\\0b\\0c\\01" ;; data section
|
||||
"\\00\\41\\03\\0b" ;; data segment 0
|
||||
"\\07" ;; data segment size with inconsistent lengths (7 declared, 6 given)
|
||||
"\\61\\62\\63\\64\\65\\66" ;; 6 bytes given
|
||||
)`), `unexpected end of section or function`);
|
||||
|
||||
// ./test/core/binary.wast:709
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\05\\03\\01" ;; memory section
|
||||
"\\00\\01" ;; memory 0
|
||||
"\\0b\\0c\\01" ;; data section
|
||||
"\\00\\41\\00\\0b" ;; data segment 0
|
||||
"\\05" ;; data segment size with inconsistent lengths (5 declared, 6 given)
|
||||
"\\61\\62\\63\\64\\65\\66" ;; 6 bytes given
|
||||
)`), `section size mismatch`);
|
||||
|
||||
// ./test/core/binary.wast:723
|
||||
let $15 = instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01" ;; type section
|
||||
"\\60\\00\\00" ;; type 0
|
||||
"\\03\\02\\01\\00" ;; func section
|
||||
"\\0a\\11\\01" ;; code section
|
||||
"\\0f\\00" ;; func 0
|
||||
"\\02\\40" ;; block 0
|
||||
"\\41\\01" ;; condition of if 0
|
||||
"\\04\\40" ;; if 0
|
||||
"\\41\\01" ;; index of br_table element
|
||||
"\\0e\\00" ;; br_table target count can be zero
|
||||
"\\02" ;; break depth for default
|
||||
"\\0b\\0b\\0b" ;; end
|
||||
)`);
|
||||
|
||||
// ./test/core/binary.wast:740
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01" ;; type section
|
||||
"\\60\\00\\00" ;; type 0
|
||||
"\\03\\02\\01\\00" ;; func section
|
||||
"\\0a\\12\\01" ;; code section
|
||||
"\\10\\00" ;; func 0
|
||||
"\\02\\40" ;; block 0
|
||||
"\\41\\01" ;; condition of if 0
|
||||
"\\04\\40" ;; if 0
|
||||
"\\41\\01" ;; index of br_table element
|
||||
"\\0e\\02" ;; br_table with inconsistent target count (2 declared, 1 given)
|
||||
"\\00" ;; break depth 0
|
||||
;; "\\01" ;; break depth 1 (missed)
|
||||
"\\02" ;; break depth for default
|
||||
"\\0b\\0b\\0b" ;; end
|
||||
)`), `unexpected end of section or function`);
|
||||
|
||||
// ./test/core/binary.wast:762
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01" ;; type section
|
||||
"\\60\\00\\00" ;; type 0
|
||||
"\\03\\02\\01\\00" ;; func section
|
||||
"\\0a\\12\\01" ;; code section
|
||||
"\\11\\00" ;; func 0
|
||||
"\\02\\40" ;; block 0
|
||||
"\\41\\01" ;; condition of if 0
|
||||
"\\04\\40" ;; if 0
|
||||
"\\41\\01" ;; index of br_table element
|
||||
"\\0e\\01" ;; br_table with inconsistent target count (1 declared, 2 given)
|
||||
"\\00" ;; break depth 0
|
||||
"\\01" ;; break depth 1
|
||||
"\\02" ;; break depth for default
|
||||
"\\0b\\0b\\0b" ;; end
|
||||
)`), `invalid value type`);
|
||||
|
||||
// ./test/core/binary.wast:784
|
||||
let $16 = instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\08\\01\\00" ;; Start section: function 0
|
||||
|
||||
"\\0a\\04\\01" ;; Code section
|
||||
;; function 0
|
||||
"\\02\\00"
|
||||
"\\0b" ;; end
|
||||
)`);
|
||||
|
||||
// ./test/core/binary.wast:797
|
||||
assert_malformed(() =>
|
||||
instantiate(`(module binary
|
||||
"\\00asm" "\\01\\00\\00\\00"
|
||||
"\\01\\04\\01\\60\\00\\00" ;; Type section
|
||||
"\\03\\02\\01\\00" ;; Function section
|
||||
"\\08\\01\\00" ;; Start section: function 0
|
||||
"\\08\\01\\00" ;; Start section: function 0
|
||||
|
||||
"\\0a\\04\\01" ;; Code section
|
||||
;; function 0
|
||||
"\\02\\00"
|
||||
"\\0b" ;; end
|
||||
)`), `junk after last section`);
|
@ -509,6 +509,7 @@ void ArrayBufferObject::detach(JSContext* cx,
|
||||
buffer->setIsDetached();
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
/*
|
||||
* [SMDOC] WASM Linear Memory structure
|
||||
*
|
||||
@ -517,18 +518,31 @@ void ArrayBufferObject::detach(JSContext* cx,
|
||||
* The linear heap in Wasm is an mmaped array buffer. Several
|
||||
* constants manage its lifetime:
|
||||
*
|
||||
* - length - the wasm-visible current length of the buffer. Accesses in the
|
||||
* range [0, length] succeed. May only increase.
|
||||
* - length - the wasm-visible current length of the buffer in bytes. Accesses
|
||||
* in the range [0, length] succeed. May only increase.
|
||||
*
|
||||
* - boundsCheckLimit - the size against which we perform bounds checks. It is
|
||||
* always a constant offset smaller than mappedSize. Currently that constant
|
||||
* offset is 64k (wasm::GuardSize).
|
||||
*
|
||||
* - maxSize - the optional declared limit on how much length can grow.
|
||||
* - sourceMaxSize - the optional declared limit on how much length can grow in
|
||||
* pages. This is the unmodified maximum size from the source module or
|
||||
* JS-API invocation. This may not be representable in byte lengths, nor
|
||||
* feasible for a module to actually grow to due to implementation limits.
|
||||
* It is used for correct linking checks and js-types reflection.
|
||||
*
|
||||
* - clampedMaxSize - the maximum size on how much the length can grow to in
|
||||
* pages. This value respects implementation limits and is always
|
||||
* representable as a byte length. Every memory has a clampedMaxSize, even
|
||||
* if no maximum was specified in source. When a memory has no
|
||||
* sourceMaxSize, the clampedMaxSize will be the maximum amount of memory
|
||||
* that can be grown to while still respecting implementation limits.
|
||||
*
|
||||
* - mappedSize - the actual mmaped size. Access in the range
|
||||
* [0, mappedSize] will either succeed, or be handled by the wasm signal
|
||||
* handlers.
|
||||
* handlers. If sourceMaxSize is present at initialization, then we attempt
|
||||
* to map the whole clampedMaxSize. Otherwise we only map the region needed
|
||||
* for the initial size.
|
||||
*
|
||||
* The below diagram shows the layout of the wasm heap. The wasm-visible
|
||||
* portion of the heap starts at 0. There is one extra page prior to the
|
||||
@ -539,23 +553,25 @@ void ArrayBufferObject::detach(JSContext* cx,
|
||||
* \ ArrayBufferObject::dataPointer()
|
||||
* \ /
|
||||
* \ |
|
||||
* ______|_|____________________________________________________________
|
||||
* |______|_|______________|___________________|____________|____________|
|
||||
* 0 length maxSize boundsCheckLimit mappedSize
|
||||
* ______|_|___________________________________________________________________
|
||||
* |______|_|______________|___________________|___________________|____________|
|
||||
* 0 length clampedMaxSize boundsCheckLimit mappedSize
|
||||
*
|
||||
* \_______________________/
|
||||
* COMMITED
|
||||
* \____________________________________________/
|
||||
* \___________________________________________________/
|
||||
* SLOP
|
||||
* \_____________________________________________________________________/
|
||||
* \____________________________________________________________________________/
|
||||
* MAPPED
|
||||
*
|
||||
* Invariants:
|
||||
* - length only increases
|
||||
* - 0 <= length <= maxSize (if present) <= boundsCheckLimit <= mappedSize
|
||||
* - 0 <= length <= clampedMaxSize <= boundsCheckLimit <= mappedSize
|
||||
* - on ARM boundsCheckLimit must be a valid ARM immediate.
|
||||
* - if maxSize is not specified, boundsCheckLimit/mappedSize may grow. They
|
||||
* are otherwise constant.
|
||||
* - if sourceMaxSize is not specified, boundsCheckLimit/mappedSize may grow.
|
||||
* They are otherwise constant.
|
||||
* - initialLength <= clampedMaxSize <= sourceMaxSize (if present)
|
||||
* - clampedMaxSize <= wasm::MaxMemoryPages()
|
||||
*
|
||||
* NOTE: For asm.js on non-x64 we guarantee that
|
||||
*
|
||||
@ -569,22 +585,23 @@ void ArrayBufferObject::detach(JSContext* cx,
|
||||
* two parts:
|
||||
*
|
||||
* - from length to boundsCheckLimit - this part of the SLOP serves to catch
|
||||
* accesses to memory we have reserved but not yet grown into. This allows us
|
||||
* to grow memory up to max (when present) without having to patch/update the
|
||||
* bounds checks.
|
||||
* accesses to memory we have reserved but not yet grown into. This allows us
|
||||
* to grow memory up to max (when present) without having to patch/update the
|
||||
* bounds checks.
|
||||
*
|
||||
* - from boundsCheckLimit to mappedSize - this part of the SLOP allows us to
|
||||
* bounds check against base pointers and fold some constant offsets inside
|
||||
* loads. This enables better Bounds Check Elimination.
|
||||
* bounds check against base pointers and fold some constant offsets inside
|
||||
* loads. This enables better Bounds Check Elimination.
|
||||
*
|
||||
*/
|
||||
/* clang-format on */
|
||||
|
||||
[[nodiscard]] bool WasmArrayRawBuffer::growToPagesInPlace(Pages newPages) {
|
||||
size_t newSize = newPages.byteLength();
|
||||
size_t oldSize = byteLength();
|
||||
|
||||
MOZ_ASSERT(newSize >= oldSize);
|
||||
MOZ_ASSERT_IF(maxPages(), newPages <= maxPages().value());
|
||||
MOZ_ASSERT(newPages <= clampedMaxPages());
|
||||
MOZ_ASSERT(newSize <= mappedSize());
|
||||
|
||||
size_t delta = newSize - oldSize;
|
||||
@ -618,32 +635,42 @@ bool WasmArrayRawBuffer::extendMappedSize(Pages maxPages) {
|
||||
}
|
||||
|
||||
void WasmArrayRawBuffer::tryGrowMaxPagesInPlace(Pages deltaMaxPages) {
|
||||
Pages newMaxPages = *maxPages_;
|
||||
Pages newMaxPages = clampedMaxPages_;
|
||||
|
||||
DebugOnly<bool> valid = newMaxPages.checkedIncrement(deltaMaxPages);
|
||||
// Caller must ensure increment does not overflow or increase over the
|
||||
// specified maximum pages.
|
||||
MOZ_ASSERT(valid);
|
||||
MOZ_ASSERT_IF(sourceMaxPages_.isSome(), newMaxPages <= *sourceMaxPages_);
|
||||
|
||||
if (!extendMappedSize(newMaxPages)) {
|
||||
return;
|
||||
}
|
||||
maxPages_ = Some(newMaxPages);
|
||||
clampedMaxPages_ = newMaxPages;
|
||||
}
|
||||
|
||||
/* static */
|
||||
WasmArrayRawBuffer* WasmArrayRawBuffer::AllocateWasm(
|
||||
IndexType indexType, Pages initialPages, const Maybe<Pages>& maxPages,
|
||||
const Maybe<size_t>& mapped) {
|
||||
IndexType indexType, Pages initialPages, Pages clampedMaxPages,
|
||||
const Maybe<Pages>& sourceMaxPages, const Maybe<size_t>& mapped) {
|
||||
// Prior code has asserted that initial pages is within our implementation
|
||||
// limits (wasm::MaxMemoryPages) and we can assume it is a valid size_t.
|
||||
MOZ_ASSERT(initialPages.hasByteLength());
|
||||
size_t numBytes = initialPages.byteLength();
|
||||
|
||||
// If there is a specified maximum, attempt to map the whole range for
|
||||
// clampedMaxPages. Or else map only what's required for initialPages.
|
||||
Pages initialMappedPages =
|
||||
sourceMaxPages.isSome() ? clampedMaxPages : initialPages;
|
||||
|
||||
// Use an override mapped size, or else compute the mapped size from
|
||||
// initialMappedPages.
|
||||
size_t mappedSize =
|
||||
mapped.isSome() ? *mapped
|
||||
: wasm::ComputeMappedSize(maxPages.valueOr(initialPages));
|
||||
mapped.isSome() ? *mapped : wasm::ComputeMappedSize(initialMappedPages);
|
||||
|
||||
MOZ_RELEASE_ASSERT(mappedSize <= SIZE_MAX - gc::SystemPageSize());
|
||||
MOZ_RELEASE_ASSERT(numBytes <= SIZE_MAX - gc::SystemPageSize());
|
||||
MOZ_RELEASE_ASSERT(initialPages <= maxPages.valueOr(wasm::MaxMemoryPages()));
|
||||
MOZ_RELEASE_ASSERT(initialPages <= clampedMaxPages);
|
||||
MOZ_ASSERT(numBytes % gc::SystemPageSize() == 0);
|
||||
MOZ_ASSERT(mappedSize % gc::SystemPageSize() == 0);
|
||||
|
||||
@ -659,8 +686,8 @@ WasmArrayRawBuffer* WasmArrayRawBuffer::AllocateWasm(
|
||||
uint8_t* base = reinterpret_cast<uint8_t*>(data) + gc::SystemPageSize();
|
||||
uint8_t* header = base - sizeof(WasmArrayRawBuffer);
|
||||
|
||||
auto rawBuf = new (header)
|
||||
WasmArrayRawBuffer(indexType, base, maxPages, mappedSize, numBytes);
|
||||
auto rawBuf = new (header) WasmArrayRawBuffer(
|
||||
indexType, base, clampedMaxPages, sourceMaxPages, mappedSize, numBytes);
|
||||
return rawBuf;
|
||||
}
|
||||
|
||||
@ -680,47 +707,6 @@ WasmArrayRawBuffer* ArrayBufferObject::BufferContents::wasmBuffer() const {
|
||||
return (WasmArrayRawBuffer*)(data_ - sizeof(WasmArrayRawBuffer));
|
||||
}
|
||||
|
||||
static Pages AdjustWasmMaxPages(Pages initialPages, Pages maxPages) {
|
||||
#ifdef JS_64BIT
|
||||
# ifdef ENABLE_WASM_CRANELIFT
|
||||
// On 64-bit platforms when we aren't using huge memory and we're using
|
||||
// Cranelift, clamp clampedMaxSize to a value that satisfies the 32-bit
|
||||
// invariants clampedMaxSize + wasm::PageSize < UINT32_MAX and
|
||||
// clampedMaxSize % wasm::PageSize == 0.
|
||||
//
|
||||
// Note MaxMemory32LimitField*PageSize == UINT32_MAX+1 == 4GB, as you would
|
||||
// expect for a 32-bit memory.
|
||||
//
|
||||
// Note that this will correspond with MaxMemory32BoundsCheckLimit().
|
||||
//
|
||||
// We do this only when Cranelift is present because Cranelift has not been
|
||||
// updated to handle a 64-bit boundsCheckLimit field on 64-bit systems.
|
||||
static const uint64_t CraneliftMaxPages =
|
||||
(UINT32_MAX - wasm::PageSize) / wasm::PageSize;
|
||||
if (!useHugeMemory && maxPages.value() >= CraneliftMaxPages) {
|
||||
maxPages = Pages(CraneliftMaxPages - 1);
|
||||
}
|
||||
# endif
|
||||
#else
|
||||
static_assert(sizeof(uintptr_t) == 4, "assuming not 64 bit implies 32 bit");
|
||||
|
||||
// On 32-bit platforms, prevent applications specifying a large max
|
||||
// (like UINT32_MAX) from unintentially OOMing the browser: they just
|
||||
// want "a lot of memory". Maintain the invariant that
|
||||
// initialSize <= clampedMaxSize.
|
||||
static const uint64_t OneGib = 1 << 30;
|
||||
static const Pages OneGibPages = Pages(OneGib >> wasm::PageBits);
|
||||
static_assert(wasm::HighestValidARMImmediate > OneGib,
|
||||
"computing mapped size on ARM requires clamped max size");
|
||||
|
||||
Pages clampedPages = std::max(OneGibPages, initialPages);
|
||||
maxPages = std::min(clampedPages, maxPages);
|
||||
#endif
|
||||
|
||||
MOZ_RELEASE_ASSERT(initialPages <= maxPages);
|
||||
return maxPages;
|
||||
}
|
||||
|
||||
template <typename ObjT, typename RawbufT>
|
||||
static bool CreateSpecificWasmBuffer32(
|
||||
JSContext* cx, const wasm::MemoryDesc& memory,
|
||||
@ -728,13 +714,9 @@ static bool CreateSpecificWasmBuffer32(
|
||||
bool useHugeMemory =
|
||||
memory.indexType() == IndexType::I32 && wasm::IsHugeMemoryEnabled();
|
||||
Pages initialPages = memory.initialPages();
|
||||
Maybe<Pages> maxPages = memory.maximumPages();
|
||||
|
||||
// Adjust the maximum pages specified to conform to extra invariants in the
|
||||
// engine.
|
||||
if (maxPages) {
|
||||
maxPages = Some(AdjustWasmMaxPages(initialPages, *maxPages));
|
||||
}
|
||||
Maybe<Pages> sourceMaxPages = memory.maximumPages();
|
||||
Pages clampedMaxPages =
|
||||
wasm::ClampedMaxPages(initialPages, sourceMaxPages, useHugeMemory);
|
||||
|
||||
Maybe<size_t> mappedSize;
|
||||
#ifdef WASM_SUPPORTS_HUGE_MEMORY
|
||||
@ -745,8 +727,9 @@ static bool CreateSpecificWasmBuffer32(
|
||||
}
|
||||
#endif
|
||||
|
||||
RawbufT* buffer = RawbufT::AllocateWasm(memory.limits.indexType, initialPages,
|
||||
maxPages, mappedSize);
|
||||
RawbufT* buffer =
|
||||
RawbufT::AllocateWasm(memory.limits.indexType, initialPages,
|
||||
clampedMaxPages, sourceMaxPages, mappedSize);
|
||||
if (!buffer) {
|
||||
if (useHugeMemory) {
|
||||
WarnNumberASCII(cx, JSMSG_WASM_HUGE_MEMORY_FAILED);
|
||||
@ -758,19 +741,19 @@ static bool CreateSpecificWasmBuffer32(
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we fail, and have a maxPages, try to reserve the biggest chunk
|
||||
// in the range [initialPages, maxPages) using log backoff.
|
||||
if (!maxPages) {
|
||||
// If we fail, and have a sourceMaxPages, try to reserve the biggest
|
||||
// chunk in the range [initialPages, clampedMaxPages) using log backoff.
|
||||
if (!sourceMaxPages) {
|
||||
wasm::Log(cx, "new Memory({initial=%" PRIu64 " pages}) failed",
|
||||
initialPages.value());
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t cur = maxPages->value() / 2;
|
||||
uint64_t cur = clampedMaxPages.value() / 2;
|
||||
for (; Pages(cur) > initialPages; cur /= 2) {
|
||||
buffer = RawbufT::AllocateWasm(memory.limits.indexType, initialPages,
|
||||
Some(Pages(cur)), mappedSize);
|
||||
Pages(cur), sourceMaxPages, mappedSize);
|
||||
if (buffer) {
|
||||
break;
|
||||
}
|
||||
@ -816,19 +799,19 @@ static bool CreateSpecificWasmBuffer32(
|
||||
}
|
||||
|
||||
// Log the result with details on the memory allocation
|
||||
if (maxPages) {
|
||||
if (sourceMaxPages) {
|
||||
if (useHugeMemory) {
|
||||
wasm::Log(cx,
|
||||
"new Memory({initial:%" PRIu64 " pages, maximum:%" PRIu64
|
||||
" pages}) succeeded",
|
||||
initialPages.value(), maxPages->value());
|
||||
initialPages.value(), sourceMaxPages->value());
|
||||
} else {
|
||||
wasm::Log(cx,
|
||||
"new Memory({initial:%" PRIu64 " pages, maximum:%" PRIu64
|
||||
" pages}) succeeded "
|
||||
"with internal maximum of %" PRIu64 " pages",
|
||||
initialPages.value(), maxPages->value(),
|
||||
object->wasmMaxPages()->value());
|
||||
initialPages.value(), sourceMaxPages->value(),
|
||||
object->wasmClampedMaxPages().value());
|
||||
}
|
||||
} else {
|
||||
wasm::Log(cx, "new Memory({initial:%" PRIu64 " pages}) succeeded",
|
||||
@ -1020,9 +1003,17 @@ Pages ArrayBufferObject::wasmPages() const {
|
||||
return Pages::fromByteLengthExact(byteLength());
|
||||
}
|
||||
|
||||
Maybe<Pages> ArrayBufferObject::wasmMaxPages() const {
|
||||
Pages ArrayBufferObject::wasmClampedMaxPages() const {
|
||||
if (isWasm()) {
|
||||
return contents().wasmBuffer()->maxPages();
|
||||
return contents().wasmBuffer()->clampedMaxPages();
|
||||
}
|
||||
MOZ_ASSERT(isPreparedForAsmJS());
|
||||
return Pages::fromByteLengthExact(byteLength());
|
||||
}
|
||||
|
||||
Maybe<Pages> ArrayBufferObject::wasmSourceMaxPages() const {
|
||||
if (isWasm()) {
|
||||
return contents().wasmBuffer()->sourceMaxPages();
|
||||
}
|
||||
MOZ_ASSERT(isPreparedForAsmJS());
|
||||
return Some<Pages>(Pages::fromByteLengthExact(byteLength()));
|
||||
@ -1048,12 +1039,19 @@ Pages js::WasmArrayBufferPages(const ArrayBufferObjectMaybeShared* buf) {
|
||||
}
|
||||
return buf->as<SharedArrayBufferObject>().volatileWasmPages();
|
||||
}
|
||||
Maybe<Pages> js::WasmArrayBufferMaxPages(
|
||||
Pages js::WasmArrayBufferClampedMaxPages(
|
||||
const ArrayBufferObjectMaybeShared* buf) {
|
||||
if (buf->is<ArrayBufferObject>()) {
|
||||
return buf->as<ArrayBufferObject>().wasmMaxPages();
|
||||
return buf->as<ArrayBufferObject>().wasmClampedMaxPages();
|
||||
}
|
||||
return Some(buf->as<SharedArrayBufferObject>().wasmMaxPages());
|
||||
return buf->as<SharedArrayBufferObject>().wasmClampedMaxPages();
|
||||
}
|
||||
Maybe<Pages> js::WasmArrayBufferSourceMaxPages(
|
||||
const ArrayBufferObjectMaybeShared* buf) {
|
||||
if (buf->is<ArrayBufferObject>()) {
|
||||
return buf->as<ArrayBufferObject>().wasmSourceMaxPages();
|
||||
}
|
||||
return Some(buf->as<SharedArrayBufferObject>().wasmSourceMaxPages());
|
||||
}
|
||||
|
||||
static void CheckStealPreconditions(Handle<ArrayBufferObject*> buffer,
|
||||
@ -1073,8 +1071,17 @@ bool ArrayBufferObject::wasmGrowToPagesInPlace(
|
||||
|
||||
MOZ_ASSERT(oldBuf->isWasm());
|
||||
|
||||
// The caller must verify that the new page size won't overflow when
|
||||
// converted to a byte length.
|
||||
// Check that the new pages is within our allowable range. This will
|
||||
// simultaneously check against the maximum specified in source and our
|
||||
// implementation limits.
|
||||
if (newPages > oldBuf->wasmClampedMaxPages()) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(newPages <= wasm::MaxMemoryPages() &&
|
||||
newPages.byteLength() < ArrayBufferObject::maxBufferByteLength());
|
||||
|
||||
// We have checked against the clamped maximum and so we know we can convert
|
||||
// to byte lengths now.
|
||||
size_t newSize = newPages.byteLength();
|
||||
|
||||
// On failure, do not throw and ensure that the original buffer is
|
||||
@ -1082,11 +1089,6 @@ bool ArrayBufferObject::wasmGrowToPagesInPlace(
|
||||
// wasm-visible length of the buffer has been increased so it must be the
|
||||
// last fallible operation.
|
||||
|
||||
// Note, caller must guard on limit appropriate for the memory type
|
||||
if (newSize > ArrayBufferObject::maxBufferByteLength()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
newBuf.set(ArrayBufferObject::createEmpty(cx));
|
||||
if (!newBuf) {
|
||||
cx->clearPendingException();
|
||||
@ -1124,14 +1126,18 @@ bool ArrayBufferObject::wasmMovingGrowToPages(
|
||||
// On failure, do not throw and ensure that the original buffer is
|
||||
// unmodified and valid.
|
||||
|
||||
// The caller must verify that the new page size won't overflow when
|
||||
// converted to a byte length.
|
||||
size_t newSize = newPages.byteLength();
|
||||
|
||||
// Note, caller must guard on the limit appropriate to the memory type
|
||||
if (newSize > ArrayBufferObject::maxBufferByteLength()) {
|
||||
// Check that the new pages is within our allowable range. This will
|
||||
// simultaneously check against the maximum specified in source and our
|
||||
// implementation limits.
|
||||
if (newPages > oldBuf->wasmClampedMaxPages()) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(newPages <= wasm::MaxMemoryPages() &&
|
||||
newPages.byteLength() < ArrayBufferObject::maxBufferByteLength());
|
||||
|
||||
// We have checked against the clamped maximum and so we know we can convert
|
||||
// to byte lengths now.
|
||||
size_t newSize = newPages.byteLength();
|
||||
|
||||
if (wasm::ComputeMappedSize(newPages) <= oldBuf->wasmMappedSize() ||
|
||||
oldBuf->contents().wasmBuffer()->extendMappedSize(newPages)) {
|
||||
@ -1144,8 +1150,10 @@ bool ArrayBufferObject::wasmMovingGrowToPages(
|
||||
return false;
|
||||
}
|
||||
|
||||
Pages clampedMaxPages =
|
||||
wasm::ClampedMaxPages(newPages, Nothing(), /* hugeMemory */ false);
|
||||
WasmArrayRawBuffer* newRawBuf = WasmArrayRawBuffer::AllocateWasm(
|
||||
oldBuf->wasmIndexType(), newPages, Nothing(), Nothing());
|
||||
oldBuf->wasmIndexType(), newPages, clampedMaxPages, Nothing(), Nothing());
|
||||
if (!newRawBuf) {
|
||||
return false;
|
||||
}
|
||||
|
@ -112,7 +112,9 @@ class ArrayBufferObjectMaybeShared;
|
||||
wasm::IndexType WasmArrayBufferIndexType(
|
||||
const ArrayBufferObjectMaybeShared* buf);
|
||||
wasm::Pages WasmArrayBufferPages(const ArrayBufferObjectMaybeShared* buf);
|
||||
mozilla::Maybe<wasm::Pages> WasmArrayBufferMaxPages(
|
||||
wasm::Pages WasmArrayBufferClampedMaxPages(
|
||||
const ArrayBufferObjectMaybeShared* buf);
|
||||
mozilla::Maybe<wasm::Pages> WasmArrayBufferSourceMaxPages(
|
||||
const ArrayBufferObjectMaybeShared* buf);
|
||||
size_t WasmArrayBufferMappedSize(const ArrayBufferObjectMaybeShared* buf);
|
||||
|
||||
@ -130,8 +132,11 @@ class ArrayBufferObjectMaybeShared : public NativeObject {
|
||||
return WasmArrayBufferIndexType(this);
|
||||
}
|
||||
wasm::Pages wasmPages() const { return WasmArrayBufferPages(this); }
|
||||
mozilla::Maybe<wasm::Pages> wasmMaxPages() const {
|
||||
return WasmArrayBufferMaxPages(this);
|
||||
wasm::Pages wasmClampedMaxPages() const {
|
||||
return WasmArrayBufferClampedMaxPages(this);
|
||||
}
|
||||
mozilla::Maybe<wasm::Pages> wasmSourceMaxPages() const {
|
||||
return WasmArrayBufferSourceMaxPages(this);
|
||||
}
|
||||
size_t wasmMappedSize() const { return WasmArrayBufferMappedSize(this); }
|
||||
|
||||
@ -456,7 +461,8 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared {
|
||||
|
||||
wasm::IndexType wasmIndexType() const;
|
||||
wasm::Pages wasmPages() const;
|
||||
mozilla::Maybe<wasm::Pages> wasmMaxPages() const;
|
||||
wasm::Pages wasmClampedMaxPages() const;
|
||||
mozilla::Maybe<wasm::Pages> wasmSourceMaxPages() const;
|
||||
|
||||
[[nodiscard]] static bool wasmGrowToPagesInPlace(
|
||||
wasm::Pages newPages, Handle<ArrayBufferObject*> oldBuf,
|
||||
@ -587,16 +593,19 @@ class MutableWrappedPtrOperations<InnerViewTable, Wrapper>
|
||||
|
||||
class WasmArrayRawBuffer {
|
||||
wasm::IndexType indexType_;
|
||||
mozilla::Maybe<wasm::Pages> maxPages_;
|
||||
wasm::Pages clampedMaxPages_;
|
||||
mozilla::Maybe<wasm::Pages> sourceMaxPages_;
|
||||
size_t mappedSize_; // Not including the header page
|
||||
size_t length_;
|
||||
|
||||
protected:
|
||||
WasmArrayRawBuffer(wasm::IndexType indexType, uint8_t* buffer,
|
||||
const mozilla::Maybe<wasm::Pages>& maxPages,
|
||||
wasm::Pages clampedMaxPages,
|
||||
const mozilla::Maybe<wasm::Pages>& sourceMaxPages,
|
||||
size_t mappedSize, size_t length)
|
||||
: indexType_(indexType),
|
||||
maxPages_(maxPages),
|
||||
clampedMaxPages_(clampedMaxPages),
|
||||
sourceMaxPages_(sourceMaxPages),
|
||||
mappedSize_(mappedSize),
|
||||
length_(length) {
|
||||
MOZ_ASSERT(buffer == dataPointer());
|
||||
@ -605,7 +614,8 @@ class WasmArrayRawBuffer {
|
||||
public:
|
||||
static WasmArrayRawBuffer* AllocateWasm(
|
||||
wasm::IndexType indexType, wasm::Pages initialPages,
|
||||
const mozilla::Maybe<wasm::Pages>& maxPages,
|
||||
wasm::Pages clampedMaxPages,
|
||||
const mozilla::Maybe<wasm::Pages>& sourceMaxPages,
|
||||
const mozilla::Maybe<size_t>& mappedSize);
|
||||
static void Release(void* mem);
|
||||
|
||||
@ -631,7 +641,9 @@ class WasmArrayRawBuffer {
|
||||
return wasm::Pages::fromByteLengthExact(length_);
|
||||
}
|
||||
|
||||
mozilla::Maybe<wasm::Pages> maxPages() const { return maxPages_; }
|
||||
wasm::Pages clampedMaxPages() const { return clampedMaxPages_; }
|
||||
|
||||
mozilla::Maybe<wasm::Pages> sourceMaxPages() const { return sourceMaxPages_; }
|
||||
|
||||
[[nodiscard]] bool growToPagesInPlace(wasm::Pages newPages);
|
||||
|
||||
|
@ -46,11 +46,12 @@ static size_t SharedArrayMappedSize(size_t length) {
|
||||
return SharedArrayAccessibleSize(length) + gc::SystemPageSize();
|
||||
}
|
||||
|
||||
// `wasmMaxPages` must always be something for wasm and nothing for other
|
||||
// users.
|
||||
// `wasmSourceMaxPages` must always be something for wasm and nothing for
|
||||
// other users.
|
||||
SharedArrayRawBuffer* SharedArrayRawBuffer::AllocateInternal(
|
||||
wasm::IndexType wasmIndexType, size_t length,
|
||||
const Maybe<wasm::Pages>& wasmMaxPages,
|
||||
const Maybe<wasm::Pages>& wasmClampedMaxPages,
|
||||
const Maybe<wasm::Pages>& wasmSourceMaxPages,
|
||||
const Maybe<size_t>& wasmMappedSize) {
|
||||
MOZ_RELEASE_ASSERT(length <= ArrayBufferObject::maxBufferByteLength());
|
||||
|
||||
@ -59,12 +60,12 @@ SharedArrayRawBuffer* SharedArrayRawBuffer::AllocateInternal(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool preparedForWasm = wasmMaxPages.isSome();
|
||||
bool preparedForWasm = wasmClampedMaxPages.isSome();
|
||||
size_t computedMappedSize;
|
||||
if (preparedForWasm) {
|
||||
computedMappedSize = wasmMappedSize.isSome()
|
||||
? *wasmMappedSize
|
||||
: wasm::ComputeMappedSize(*wasmMaxPages);
|
||||
: wasm::ComputeMappedSize(*wasmClampedMaxPages);
|
||||
} else {
|
||||
MOZ_ASSERT(wasmMappedSize.isNothing());
|
||||
computedMappedSize = accessibleSize;
|
||||
@ -82,33 +83,37 @@ SharedArrayRawBuffer* SharedArrayRawBuffer::AllocateInternal(
|
||||
uint8_t* buffer = reinterpret_cast<uint8_t*>(p) + gc::SystemPageSize();
|
||||
uint8_t* base = buffer - sizeof(SharedArrayRawBuffer);
|
||||
SharedArrayRawBuffer* rawbuf = new (base) SharedArrayRawBuffer(
|
||||
wasmIndexType, buffer, length, wasmMaxPages.valueOr(Pages(0)),
|
||||
computedMappedSize, preparedForWasm);
|
||||
wasmIndexType, buffer, length, wasmClampedMaxPages.valueOr(Pages(0)),
|
||||
wasmSourceMaxPages.valueOr(Pages(0)), computedMappedSize,
|
||||
preparedForWasm);
|
||||
MOZ_ASSERT(rawbuf->length_ == length); // Deallocation needs this
|
||||
return rawbuf;
|
||||
}
|
||||
|
||||
SharedArrayRawBuffer* SharedArrayRawBuffer::Allocate(size_t length) {
|
||||
return SharedArrayRawBuffer::AllocateInternal(wasm::IndexType::I32, length,
|
||||
Nothing(), Nothing());
|
||||
return SharedArrayRawBuffer::AllocateInternal(
|
||||
wasm::IndexType::I32, length, Nothing(), Nothing(), Nothing());
|
||||
}
|
||||
|
||||
SharedArrayRawBuffer* SharedArrayRawBuffer::AllocateWasm(
|
||||
wasm::IndexType indexType, Pages initialPages,
|
||||
const mozilla::Maybe<wasm::Pages>& maxPages,
|
||||
wasm::IndexType indexType, Pages initialPages, wasm::Pages clampedMaxPages,
|
||||
const mozilla::Maybe<wasm::Pages>& sourceMaxPages,
|
||||
const mozilla::Maybe<size_t>& mappedSize) {
|
||||
// Prior code has asserted that initial pages is within our implementation
|
||||
// limits (wasm::MaxMemory32Pages) and we can assume it is a valid size_t.
|
||||
MOZ_ASSERT(initialPages.hasByteLength());
|
||||
size_t length = initialPages.byteLength();
|
||||
return SharedArrayRawBuffer::AllocateInternal(indexType, length, maxPages,
|
||||
mappedSize);
|
||||
return SharedArrayRawBuffer::AllocateInternal(
|
||||
indexType, length, Some(clampedMaxPages), sourceMaxPages, mappedSize);
|
||||
}
|
||||
|
||||
void SharedArrayRawBuffer::tryGrowMaxPagesInPlace(Pages deltaMaxPages) {
|
||||
Pages newMaxPages = wasmMaxPages_;
|
||||
Pages newMaxPages = wasmClampedMaxPages_;
|
||||
DebugOnly<bool> valid = newMaxPages.checkedIncrement(deltaMaxPages);
|
||||
// Caller must ensure increment does not overflow or increase over the
|
||||
// specified maximum pages.
|
||||
MOZ_ASSERT(valid);
|
||||
MOZ_ASSERT(newMaxPages <= wasmSourceMaxPages_);
|
||||
|
||||
size_t newMappedSize = wasm::ComputeMappedSize(newMaxPages);
|
||||
MOZ_ASSERT(mappedSize_ <= newMappedSize);
|
||||
@ -121,19 +126,23 @@ void SharedArrayRawBuffer::tryGrowMaxPagesInPlace(Pages deltaMaxPages) {
|
||||
}
|
||||
|
||||
mappedSize_ = newMappedSize;
|
||||
wasmMaxPages_ = newMaxPages;
|
||||
wasmClampedMaxPages_ = newMaxPages;
|
||||
}
|
||||
|
||||
bool SharedArrayRawBuffer::wasmGrowToPagesInPlace(const Lock&,
|
||||
wasm::Pages newPages) {
|
||||
// The caller must verify that the new page size won't overflow when
|
||||
// converted to a byte length.
|
||||
size_t newLength = newPages.byteLength();
|
||||
|
||||
// Note, caller must guard on the limit appropriate to the memory type
|
||||
if (newLength > ArrayBufferObject::maxBufferByteLength()) {
|
||||
// Check that the new pages is within our allowable range. This will
|
||||
// simultaneously check against the maximum specified in source and our
|
||||
// implementation limits.
|
||||
if (newPages > wasmClampedMaxPages_) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(newPages <= wasm::MaxMemoryPages() &&
|
||||
newPages.byteLength() < ArrayBufferObject::maxBufferByteLength());
|
||||
|
||||
// We have checked against the clamped maximum and so we know we can convert
|
||||
// to byte lengths now.
|
||||
size_t newLength = newPages.byteLength();
|
||||
|
||||
MOZ_ASSERT(newLength >= length_);
|
||||
|
||||
|
@ -56,7 +56,8 @@ class SharedArrayRawBuffer {
|
||||
wasm::IndexType wasmIndexType_;
|
||||
// The maximum size of this buffer in wasm pages. If this buffer was not
|
||||
// prepared for wasm, then this is zero.
|
||||
wasm::Pages wasmMaxPages_;
|
||||
wasm::Pages wasmClampedMaxPages_;
|
||||
wasm::Pages wasmSourceMaxPages_;
|
||||
size_t mappedSize_; // Does not include the page for the header
|
||||
bool preparedForWasm_;
|
||||
|
||||
@ -72,13 +73,15 @@ class SharedArrayRawBuffer {
|
||||
|
||||
protected:
|
||||
SharedArrayRawBuffer(wasm::IndexType wasmIndexType, uint8_t* buffer,
|
||||
size_t length, wasm::Pages wasmMaxPages,
|
||||
size_t mappedSize, bool preparedForWasm)
|
||||
size_t length, wasm::Pages wasmClampedMaxPages,
|
||||
wasm::Pages wasmSourceMaxPages, size_t mappedSize,
|
||||
bool preparedForWasm)
|
||||
: refcount_(1),
|
||||
length_(length),
|
||||
growLock_(mutexid::SharedArrayGrow),
|
||||
wasmIndexType_(wasmIndexType),
|
||||
wasmMaxPages_(wasmMaxPages),
|
||||
wasmClampedMaxPages_(wasmClampedMaxPages),
|
||||
wasmSourceMaxPages_(wasmSourceMaxPages),
|
||||
mappedSize_(mappedSize),
|
||||
preparedForWasm_(preparedForWasm),
|
||||
waiters_(nullptr) {
|
||||
@ -86,11 +89,12 @@ class SharedArrayRawBuffer {
|
||||
}
|
||||
|
||||
// Allocate a SharedArrayRawBuffer for either Wasm or other users.
|
||||
// `wasmMaxPages` must always be something for wasm and nothing for other
|
||||
// users.
|
||||
// `wasmClampedMaxPages` and `wasmSourceMaxPages` must always be something
|
||||
// for wasm and nothing for other users.
|
||||
static SharedArrayRawBuffer* AllocateInternal(
|
||||
wasm::IndexType wasmIndexType, size_t length,
|
||||
const mozilla::Maybe<wasm::Pages>& wasmMaxPages,
|
||||
const mozilla::Maybe<wasm::Pages>& wasmClampedMaxPages,
|
||||
const mozilla::Maybe<wasm::Pages>& wasmSourceMaxPages,
|
||||
const mozilla::Maybe<size_t>& wasmMappedSize);
|
||||
|
||||
public:
|
||||
@ -110,7 +114,8 @@ class SharedArrayRawBuffer {
|
||||
static SharedArrayRawBuffer* Allocate(size_t length);
|
||||
static SharedArrayRawBuffer* AllocateWasm(
|
||||
wasm::IndexType indexType, wasm::Pages initialPages,
|
||||
const mozilla::Maybe<wasm::Pages>& maxPages,
|
||||
wasm::Pages clampedMaxPages,
|
||||
const mozilla::Maybe<wasm::Pages>& sourceMaxPages,
|
||||
const mozilla::Maybe<size_t>& mappedSize);
|
||||
|
||||
// This may be called from multiple threads. The caller must take
|
||||
@ -140,7 +145,8 @@ class SharedArrayRawBuffer {
|
||||
return wasm::Pages::fromByteLengthExact(length_);
|
||||
}
|
||||
|
||||
wasm::Pages wasmMaxPages() const { return wasmMaxPages_; }
|
||||
wasm::Pages wasmClampedMaxPages() const { return wasmClampedMaxPages_; }
|
||||
wasm::Pages wasmSourceMaxPages() const { return wasmSourceMaxPages_; }
|
||||
|
||||
size_t mappedSize() const { return mappedSize_; }
|
||||
|
||||
@ -261,7 +267,12 @@ class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared {
|
||||
wasm::Pages volatileWasmPages() const {
|
||||
return rawBufferObject()->volatileWasmPages();
|
||||
}
|
||||
wasm::Pages wasmMaxPages() const { return rawBufferObject()->wasmMaxPages(); }
|
||||
wasm::Pages wasmClampedMaxPages() const {
|
||||
return rawBufferObject()->wasmClampedMaxPages();
|
||||
}
|
||||
wasm::Pages wasmSourceMaxPages() const {
|
||||
return rawBufferObject()->wasmSourceMaxPages();
|
||||
}
|
||||
|
||||
size_t wasmMappedSize() const { return rawBufferObject()->mappedSize(); }
|
||||
|
||||
|
@ -2542,7 +2542,7 @@ bool WasmMemoryObject::typeImpl(JSContext* cx, const CallArgs& args) {
|
||||
cx, &args.thisv().toObject().as<WasmMemoryObject>());
|
||||
Rooted<IdValueVector> props(cx, IdValueVector(cx));
|
||||
|
||||
Maybe<Pages> maxPages = memoryObj->maxPages();
|
||||
Maybe<Pages> maxPages = memoryObj->sourceMaxPages();
|
||||
if (maxPages.isSome()) {
|
||||
uint32_t maxPages32 = mozilla::AssertedCast<uint32_t>(maxPages->value());
|
||||
if (!props.append(IdValuePair(NameToId(cx->names().maximum),
|
||||
@ -2592,11 +2592,18 @@ wasm::Pages WasmMemoryObject::volatilePages() const {
|
||||
return buffer().wasmPages();
|
||||
}
|
||||
|
||||
Maybe<wasm::Pages> WasmMemoryObject::maxPages() const {
|
||||
wasm::Pages WasmMemoryObject::clampedMaxPages() const {
|
||||
if (isShared()) {
|
||||
return Some(sharedArrayRawBuffer()->wasmMaxPages());
|
||||
return sharedArrayRawBuffer()->wasmClampedMaxPages();
|
||||
}
|
||||
return buffer().wasmMaxPages();
|
||||
return buffer().wasmClampedMaxPages();
|
||||
}
|
||||
|
||||
Maybe<wasm::Pages> WasmMemoryObject::sourceMaxPages() const {
|
||||
if (isShared()) {
|
||||
return Some(sharedArrayRawBuffer()->wasmSourceMaxPages());
|
||||
}
|
||||
return buffer().wasmSourceMaxPages();
|
||||
}
|
||||
|
||||
wasm::IndexType WasmMemoryObject::indexType() const {
|
||||
@ -2649,7 +2656,7 @@ bool WasmMemoryObject::isHuge() const {
|
||||
}
|
||||
|
||||
bool WasmMemoryObject::movingGrowable() const {
|
||||
return !isHuge() && !buffer().wasmMaxPages();
|
||||
return !isHuge() && !buffer().wasmSourceMaxPages();
|
||||
}
|
||||
|
||||
size_t WasmMemoryObject::boundsCheckLimit() const {
|
||||
@ -2701,16 +2708,6 @@ uint32_t WasmMemoryObject::growShared(HandleWasmMemoryObject memory,
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Always check against the max here, do not rely on the buffer resizers to
|
||||
// use the correct limit, they don't have enough context.
|
||||
if (newPages > MaxMemoryPages()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (newPages > rawBuf->wasmMaxPages()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!rawBuf->wasmGrowToPagesInPlace(lock, newPages)) {
|
||||
return -1;
|
||||
}
|
||||
@ -2747,12 +2744,6 @@ uint32_t WasmMemoryObject::grow(HandleWasmMemoryObject memory, uint32_t delta,
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Always check against the max here, do not rely on the buffer resizers to
|
||||
// use the correct limit, they don't have enough context.
|
||||
if (newPages > MaxMemoryPages()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
RootedArrayBufferObject newBuf(cx);
|
||||
|
||||
if (memory->movingGrowable()) {
|
||||
@ -2761,17 +2752,9 @@ uint32_t WasmMemoryObject::grow(HandleWasmMemoryObject memory, uint32_t delta,
|
||||
cx)) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (Maybe<Pages> maxPages = oldBuf->wasmMaxPages()) {
|
||||
if (newPages > *maxPages) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ArrayBufferObject::wasmGrowToPagesInPlace(newPages, oldBuf, &newBuf,
|
||||
cx)) {
|
||||
return -1;
|
||||
}
|
||||
} else if (!ArrayBufferObject::wasmGrowToPagesInPlace(newPages, oldBuf,
|
||||
&newBuf, cx)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memory->setReservedSlot(BUFFER_SLOT, ObjectValue(*newBuf));
|
||||
@ -4849,3 +4832,53 @@ wasm::Pages wasm::MaxMemoryPages() {
|
||||
|
||||
size_t wasm::MaxMemoryBoundsCheckLimit() { return size_t(INT32_MAX) + 1; }
|
||||
#endif
|
||||
|
||||
Pages wasm::ClampedMaxPages(Pages initialPages,
|
||||
const Maybe<Pages>& sourceMaxPages,
|
||||
bool useHugeMemory) {
|
||||
Pages clampedMaxPages;
|
||||
|
||||
if (sourceMaxPages.isSome()) {
|
||||
// There is a specified maximum, clamp it to the implementation limit of
|
||||
// maximum pages
|
||||
clampedMaxPages = std::min(*sourceMaxPages, wasm::MaxMemoryPages());
|
||||
|
||||
#if defined(JS_64BIT) && defined(ENABLE_WASM_CRANELIFT)
|
||||
// On 64-bit platforms when we aren't using huge memory and we're using
|
||||
// Cranelift, we must satisfy the 32-bit invariants that:
|
||||
// clampedMaxSize + wasm::PageSize < UINT32_MAX
|
||||
// This is ensured by clamping sourceMaxPages to wasm::MaxMemoryPages(),
|
||||
// which has special logic for cranelift.
|
||||
MOZ_ASSERT_IF(!useHugeMemory,
|
||||
clampedMaxPages.byteLength() + wasm::PageSize < UINT32_MAX);
|
||||
#endif
|
||||
|
||||
#ifndef JS_64BIT
|
||||
static_assert(sizeof(uintptr_t) == 4, "assuming not 64 bit implies 32 bit");
|
||||
|
||||
// On 32-bit platforms, prevent applications specifying a large max
|
||||
// (like MaxMemory32Pages) from unintentially OOMing the browser: they just
|
||||
// want "a lot of memory". Maintain the invariant that
|
||||
// initialPages <= clampedMaxPages.
|
||||
static const uint64_t OneGib = 1 << 30;
|
||||
static const Pages OneGibPages = Pages(OneGib >> wasm::PageBits);
|
||||
static_assert(wasm::HighestValidARMImmediate > OneGib,
|
||||
"computing mapped size on ARM requires clamped max size");
|
||||
|
||||
Pages clampedPages = std::max(OneGibPages, initialPages);
|
||||
clampedMaxPages = std::min(clampedPages, clampedMaxPages);
|
||||
#endif
|
||||
} else {
|
||||
// There is not a specified maximum, fill it in with the implementation
|
||||
// limit of maximum pages
|
||||
clampedMaxPages = wasm::MaxMemoryPages();
|
||||
}
|
||||
|
||||
// Double-check our invariants
|
||||
MOZ_RELEASE_ASSERT(sourceMaxPages.isNothing() ||
|
||||
clampedMaxPages <= *sourceMaxPages);
|
||||
MOZ_RELEASE_ASSERT(clampedMaxPages <= wasm::MaxMemoryPages());
|
||||
MOZ_RELEASE_ASSERT(initialPages <= clampedMaxPages);
|
||||
|
||||
return clampedMaxPages;
|
||||
}
|
||||
|
@ -166,6 +166,11 @@ static inline uint64_t MaxMemoryLimitField(IndexType indexType) {
|
||||
: MaxMemory64LimitField;
|
||||
}
|
||||
|
||||
// Compute the 'clamped' maximum size of a memory. See
|
||||
// 'WASM Linear Memory structure' in ArrayBufferObject.cpp for background.
|
||||
Pages ClampedMaxPages(Pages initialPages, const Maybe<Pages>& sourceMaxPages,
|
||||
bool useHugeMemory);
|
||||
|
||||
// Compiles the given binary wasm module given the ArrayBufferObject
|
||||
// and links the module's imports with the given import object.
|
||||
|
||||
@ -417,7 +422,8 @@ class WasmMemoryObject : public NativeObject {
|
||||
|
||||
// The maximum length of the memory in pages. This is not 'volatile' in
|
||||
// contrast to the current length, as it cannot change for shared memories.
|
||||
mozilla::Maybe<wasm::Pages> maxPages() const;
|
||||
wasm::Pages clampedMaxPages() const;
|
||||
mozilla::Maybe<wasm::Pages> sourceMaxPages() const;
|
||||
|
||||
wasm::IndexType indexType() const;
|
||||
bool isShared() const;
|
||||
|
@ -719,7 +719,7 @@ bool Module::instantiateMemory(JSContext* cx,
|
||||
if (!CheckLimits(cx, desc.initialPages(), desc.maximumPages(),
|
||||
/* defaultMax */ MaxMemoryPages(),
|
||||
/* actualLength */
|
||||
memory->volatilePages(), memory->maxPages(),
|
||||
memory->volatilePages(), memory->sourceMaxPages(),
|
||||
metadata().isAsmJS(), "Memory")) {
|
||||
return false;
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ struct Pages {
|
||||
uint64_t value_;
|
||||
|
||||
public:
|
||||
constexpr Pages() : value_(0) {}
|
||||
constexpr explicit Pages(uint64_t value) : value_(value) {}
|
||||
|
||||
// Get the wrapped page value. Only use this if you must, prefer to use or
|
||||
|
@ -375,7 +375,8 @@ bool wasm::IsValidBoundsCheckImmediate(uint32_t i) {
|
||||
}
|
||||
|
||||
size_t wasm::ComputeMappedSize(wasm::Pages maxPages) {
|
||||
// TODO: memory64 maximum size may overflow size_t
|
||||
// Caller is responsible to ensure that maxPages has been clamped to
|
||||
// implementation limits.
|
||||
size_t maxSize = maxPages.byteLength();
|
||||
|
||||
// It is the bounds-check limit, not the mapped size, that gets baked into
|
||||
|
Loading…
Reference in New Issue
Block a user