title: WebAssembly中的記憶體(為什麼它比你想象的更安全)
date: 2018-3-22 23:58:00
categories: 翻譯
tags: WebAssembly
source: 原文地址
auther: Lin Clark
WebAssembly中的記憶體(為什麼它比你想象的更安全)
這是此係列的第二篇文章:
- Creating a WebAssembly module instance with JavaScript
- Memory in WebAssembly (and why it’s safer than you think)
- WebAssembly table imports… what are they?
WebAssembly中的記憶體與JavaScript中的記憶體稍有不同。使用WebAssembly,您可以直接訪問原始位元組…並且會讓一些人感到擔憂。但它實際上比你想象的更安全。
什麼是memory物件?
當一個WebAssembly模組被例項化時,它需要一個記憶體物件。你可以建立一個新的WebAssembly.Memory物件並將該物件傳入。如果不存在,則會建立一個記憶體物件並將其自動附加到該例項。
所有JS引擎在內部都會建立一個ArrayBuffer(我在另一篇文章中解釋過)。ArrayBuffer是一個物件,JS對其保持引用。JS為你分配記憶體。你告訴它需要多少記憶體,它會建立一個給定大小的ArrayBuffer。
陣列的索引可以看作是記憶體地址。如果需要更多的記憶體,你可以做一些叫做 growing(增長) 的事情來擴大陣列。
將WebAssembly的記憶體作為JavaScript中的一個物件 – 作為ArrayBuffer處理 – 做到了兩件事:
- 便於JS和WebAssembly之間的傳值
- 使記憶體管理更加安全
在JS和WebAssembly之間傳遞值
因為這只是一個JavaScript物件,這意味著JavaScript也可以挖掘出這些記憶體中的位元組。於此,WebAssembly和JavaScript可以通過共享記憶體並來回傳遞值。
他們不使用記憶體地址,而是使用陣列索引來訪問每個box(記憶體單元)。
例如,WebAssembly可以在記憶體中放置一個字串。它會將它編碼成位元組……
…然後將這些位元組放入陣列中。
然後它會將第一個索引(整數)返回給JavaScript。所以JavaScript可以將位元組拉出來並使用它們。
現在,大多數JavaScript方法不知道如何直接使用位元組。因此,你需要JavaScript端的一些東西,就你您在WebAssembly端做的那樣,它可以將位元組轉換為更有用的值,比如字串。
在某些瀏覽器中,你可以使用TextDecoder和TextEncoder API。或者你可以在你的.js檔案中新增輔助函式。例如,像Emscripten這樣的工具可以新增編碼和解碼輔助。
這就是使用JS物件作為WebAssembly記憶體的第一個好處。WebAssembly和JavaScript可以通過記憶體直接傳遞值。
記憶體洩漏 Memory leaks
正如我在關於記憶體管理的文章中提到的那樣,當你手動管理記憶體時,你可能會忘記清除它。這可能會導致系統記憶體不足。
如果WebAssembly模組例項可以直接訪問記憶體,並且在記憶體離開作用域之前忘記清除記憶體,則瀏覽器可能會發生記憶體洩露。
但是因為記憶體物件只是一個JavaScript物件,所以它本身被垃圾收集器跟蹤(即使它的內容不是)。
這意味著,當記憶體物件所附的WebAssembly例項離開作用域時,整個記憶體陣列可能會被垃圾收集。
記憶體隔離 Memory isolation
當人們聽到WebAssembly能讓你直接訪問記憶體時,它可能會讓一些人有點緊張。他們認為一個惡意的WebAssembly模組可以進入記憶體並且在記憶體中進行挖掘,但它不應該這樣做。但事實並非如此。
ArrayBuffer的陣列範圍天然提供了一個邊界。這是WebAssembly模組可以直接觸控記憶體的限制。
它可以直接接觸該陣列內部的位元組,但它不能看到超出此陣列邊界的任何內容。
例如,記憶體中的任何其他JS物件(如window)都不能被WebAssembly訪問。這對安全性非常重要。
當在WebAssembly中載入或儲存時,引擎會執行陣列邊界檢查(array bounds checks)以確保地址位於WebAssembly例項的記憶體中。
如果程式碼嘗試訪問超出界限的地址,則引擎將丟擲異常。這種行為保護了其餘的記憶體。
這就是記憶體匯入。在下一篇文章中,我們將看看另一種匯入更安全的匯入方法…table import(匯入表格)。