WebAssembly 表导入… 它们是什么?

这是 3 部分系列中的第 3 篇文章

  1. 使用 JavaScript 创建 WebAssembly 模块实例
  2. WebAssembly 中的内存(以及为什么它比你想象的更安全)
  3. WebAssembly 表导入… 它们是什么?

第一篇文章 中,我介绍了 WebAssembly 模块实例可以具有的四种不同类型的导入

  • 函数导入
  • 内存

最后一个可能有点陌生。什么是表导入,它有什么用?

有时在一个程序中,您希望能够拥有一个指向函数的变量,就像一个回调函数。然后你可以做一些事情,比如将它传递给另一个函数。 定义回调函数并将其传递给函数

在 C 中,这些被称为函数指针。函数存在于内存中。变量,即函数指针,只是指向该内存地址。

Function pointer at memory address 4 points to the callback at memory address 1

如果您需要,稍后您可以将变量指向另一个函数。这个概念应该很熟悉。

Function pointer at memory address 4 changes to point to callback2 at memory address 4

在网页中,所有函数都只是 JavaScript 对象。并且由于它们是 JavaScript 对象,因此它们位于 WebAssembly 内存之外的内存地址中。

JS function living in JS managed memory

如果我们想要拥有一个指向其中一个函数的变量,我们需要获取它的地址并将其放入我们的内存中。

Function pointer in WebAssembly memory pointing to function

但是,保持网页安全的一部分是隐藏这些内存地址。你不希望页面上的代码能够看到或操纵该内存地址。如果页面上有恶意代码,它可以使用对内存中事物布局方式的了解来创建漏洞。

例如,它可以更改您在那里拥有的内存地址,以指向不同的内存位置。

然后,当您尝试调用函数时,您将加载攻击者提供给您的内存地址中的任何内容。

Malicious actor changing the address in WebAssembly memory to point to malicious code

这可能是恶意代码,它以某种方式插入了内存,也许嵌入在一个字符串中。

表使得拥有函数指针成为可能,但以一种不受这些攻击类型影响的方式。

表是一个数组,它存在于 WebAssembly 内存之外。这些值是对函数的引用。

Another region of memory is added, distinct from WebAssembly memory, which contains the function pointer

在内部,这些引用包含内存地址,但由于它不在 WebAssembly 内存中,因此 WebAssembly 看不到这些地址。

但是,它确实可以访问数组索引。

All memory outside of the WebAssembly memory object is obfuscated

如果 WebAssembly 模块想要调用其中一个函数,它会将索引传递给一个名为 call_indirect 的操作。这将调用该函数。

call_indirect points to the first element of the obfuscated array, which in turn points to the function

现在,表的用例非常有限。它们被添加到规范中是为了专门支持这些函数指针,因为 C 和 C++ 非常依赖这些函数指针。

因此,您目前可以在表中放置的唯一类型的引用是函数引用。但是随着 WebAssembly 功能的扩展 - 例如,当直接访问 DOM 被添加时 - 您可能会看到其他类型的引用存储在表中,以及除了 call_indirect 之外的其他表操作。

关于 Lin Clark

Lin 在 Mozilla 的高级开发部门工作,专注于 Rust 和 WebAssembly。

更多 Lin Clark 的文章…