翻译自2017.7.19发布的Memory in WebAssembly (and why it’s safer than you think)。
This is the 2nd article in a 3-part series:这是由 3 部分组成的系列文章中的第 2 篇文章:
- Creating a WebAssembly module instance with JavaScript 用JavaScript创建一个WebAssembly module对象
- Memory in WebAssembly (and why it’s safer than you think) WebAssembly中的内存(为什么比你想象的更安全)
- WebAssembly table imports… what are they? WebAssembly table import...是什么?
Memory in WebAssembly works a little differently than it does in JavaScript. With WebAssembly, you have direct access to the raw bytes… and that worries some people. But it’s actually safer than you might think.
WebAssembly中的内存的工作方式与JavaScript中的内存略有不同。使用WebAssembly,您可以直接访问原始字节...这让一些人感到担忧。但它实际上比你想象的更安全。
What is the memory object? 什么是内存对象?
When a WebAssembly module is instantiated, it needs a memory object. You can either create a new WebAssembly.Memory and pass that object in. Or, if you don’t, a memory object will be created and attached to the instance automatically.
当 WebAssembly 模块实例化时,它需要一个内存对象。您可以创建一个新的WebAssembly.Memory并将该内存对象传入。如果不这样做,内存对象将被自动创建并将其附加到WebAssembly实例。
All the JS engine will do internally is create an ArrayBuffer (which I explain in another article). The ArrayBuffer is a JavaScript object that JS has a reference to. JS allocates the memory for you. You tell it how much memory are going to need, and it will create an ArrayBuffer of that size.
WebAssembly创建时,JS引擎会创建一个ArrayBuffer(我在另一篇文章中对此进行了解释)。ArrayBuffer 是 JS 引用的 JavaScript 对象。JS 会为您分配内存。你告诉它需要多少内存,它将创建一个这种大小的ArrayBuffer。
The indexes to the array can be treated as though they were memory addresses. And if you need more memory later, you can do something called growing to make the array larger.
可以将数组的索引视为内存地址。如果您以后需要更多内存,可以执行一项名为growing的操作来使数组变大。
Handling WebAssembly’s memory as an ArrayBuffer — as an object in JavaScript — does two things:
将 WebAssembly 的内存作为 ArrayBuffer(作为 JavaScript 中的对象)进行处理可以做两件事:
- makes it easy to pass values between JS and WebAssembly 使得在 JS 和 WebAssembly 之间传递值变得容易
- helps make the memory management safe 有助于安全管理内存
Passing values between JS and WebAssembly 在 JS 和 WebAssembly 之间传值
Because this is just a JavaScript object, that means that JavaScript can also dig around in the bytes of this memory. So in this way, WebAssembly and JavaScript can share memory and pass values back and forth.
因为这只是一个 JavaScript 对象,这意味着 JavaScript 也可以处理内存。因此,通过这种方式,WebAssembly和JavaScript可以共享内存并来回传递值。
Instead of using a memory address, they use an array index to access each box.
可以不使用内存地址,而是使用数组索引来访问每个地址元素。
For example, the WebAssembly could put a string in memory. It would encode it into bytes…
例如,WebAssembly可以在内存中放置一个字符串,它会将其编码为字节...
…and then put those bytes in the array.
然后将这些字节放入数组中。
Then it would return the first index, which is an integer, to JavaScript. So JavaScript can pull the bytes out and use them.
然后,它会将第一个索引的元素(一个整数)返回给 JavaScript。因此,JavaScript可以将字节取出并使用它们。
Now, most JavaScript doesn’t know how to work directly with bytes. So you’ll need something on the JavaScript side, like you do on the WebAssembly side, that can convert from bytes into more useful values like strings.
现在,大多数JavaScript不知道如何直接使用字节。因此,就像您在WebAssembly方面所做的那样,您需要在JavaScript端使用一些东西,它可以从字节转换为更有用的值,如字符串。
In some browsers, you can use the TextDecoder and TextEncoder APIs. Or you can add helper functions into your .js file. For example, a tool like Emscripten can add encoding and decoding helpers.
在某些浏览器中,您可以使用 TextDecoder 和 TextEncoder API。或者,您可以将一些helper程序函数添加到.js文件中。例如,像 Emscripten 这样的工具可以用于编码和解码。
So that’s the first benefit of WebAssembly memory just being a JS object. WebAssembly and JavaScript can pass values back and forth directly through memory.
因此,这是WebAssembly内存作为JS对象的第一个好处。WebAssembly和JavaScript可以直接通过内存来回传递值。
Making memory access safer 更加安全地访问内存
There’s another benefit that comes from this WebAssembly memory just being a JavaScript object: safety. It makes things safer by helping to prevent browser-level memory leaks and providing memory isolation.
WebAssembly内存作为JavaScript对象还有另一个好处:安全。它可以帮助防止浏览器级别的内存泄漏和提供内存隔离,使内存管理变得更加安全。
Memory leaks 内存泄漏
As I mentioned in the article on memory management, when you manage your own memory you may forget to clear it out. This can cause the system to run out of memory.
正如我在关于内存管理的文章中提到的,当你管理自己的内存时,你可能会忘记清除失效对象,这可能会导致系统内存不足。
If a WebAssembly module instance had direct access to memory, and if it forgot to clear out that memory before it went out of scope, then the browser could leak memory.
如果 WebAssembly 模块实例可以直接访问内存,并且如果它超出被分配的内存范围前没有及时清理内存,则浏览器可能会内存泄漏。
But because the memory object is just a JavaScript object, it itself is tracked by the garbage collector (even though its contents are not).
但是由于内存对象只是一个 JavaScript 对象,因此垃圾回收器会跟踪这个内存对象(即使其内容没有被跟踪)。
That means that when the WebAssembly instance that the memory object is attached to goes out of scope, this whole memory array can just be garbage collected.
这意味着,当附加在 WebAssembly 实例的内存对象超出内存范围时,整个内存数组可以被垃圾回收。
Memory isolation 内存隔离
When people hear that WebAssembly gives you direct access to memory, it can make them a little nervous. They think that a malicious WebAssembly module could go in and dig around in memory it shouldn’t be able to. But that isn’t the case.
当人们听说WebAssembly可以让你直接访问内存时,这会让他们有点紧张。他们认为恶意的WebAssembly模块可以进入并使用它不应该使用的内存。但事实并非如此。
The bounds of the ArrayBuffer provide a boundary. It’s a limit to what memory the WebAssembly module can touch directly.
ArrayBuffer提供的内存边界可以限制对WebAssembly模块的内存直接使用。
It can directly touch the bytes that are inside of this array but it can’t see anything that’s outside the bounds of this array.
WebAssembly可以直接访问此数组内的内容,但不可访问此数组边界之外的任何内容。
For example, any other JS objects that are in memory, like the window global, aren’t accessible to WebAssembly. That’s really important for security.
例如,内存中的任何其他 JS 对象(如全局window)都无法访问 WebAssembly。这对安全性非常重要。
Whenever there’s a load or a store in WebAssembly, the engine does an array bounds checks to make sure that the address is inside the WebAssembly instance’s memory.
每当 WebAssembly load或store时,引擎都会执行数组边界检查以确保访问地址位于分配给WebAssembly 实例的内存中。
If the code tries to access an out-of-bounds address, the engine will throw an exception. This protects the rest of the memory.
如果代码尝试访问越界地址,引擎将抛出异常。这将保护内存其他地址不被访问。
So that’s the memory import. In the next article, we’ll look at another kind of import that makes things safer… the table import.
下一篇文章我们可以了解一下更加安全的table import