这是 3 部分系列中的第 3 篇文章
- 使用 JavaScript 创建 WebAssembly 模块实例
- WebAssembly 中的内存(以及为什么它比你想象的更安全)
- WebAssembly 表导入… 它们是什么?
在 第一篇文章 中,我介绍了 WebAssembly 模块实例可以具有的四种不同类型的导入
- 值
- 函数导入
- 内存
- 表
最后一个可能有点陌生。什么是表导入,它有什么用?
有时在一个程序中,您希望能够拥有一个指向函数的变量,就像一个回调函数。然后你可以做一些事情,比如将它传递给另一个函数。
在 C 中,这些被称为函数指针。函数存在于内存中。变量,即函数指针,只是指向该内存地址。
如果您需要,稍后您可以将变量指向另一个函数。这个概念应该很熟悉。
在网页中,所有函数都只是 JavaScript 对象。并且由于它们是 JavaScript 对象,因此它们位于 WebAssembly 内存之外的内存地址中。
如果我们想要拥有一个指向其中一个函数的变量,我们需要获取它的地址并将其放入我们的内存中。
但是,保持网页安全的一部分是隐藏这些内存地址。你不希望页面上的代码能够看到或操纵该内存地址。如果页面上有恶意代码,它可以使用对内存中事物布局方式的了解来创建漏洞。
例如,它可以更改您在那里拥有的内存地址,以指向不同的内存位置。
然后,当您尝试调用函数时,您将加载攻击者提供给您的内存地址中的任何内容。
这可能是恶意代码,它以某种方式插入了内存,也许嵌入在一个字符串中。
表使得拥有函数指针成为可能,但以一种不受这些攻击类型影响的方式。
表是一个数组,它存在于 WebAssembly 内存之外。这些值是对函数的引用。
在内部,这些引用包含内存地址,但由于它不在 WebAssembly 内存中,因此 WebAssembly 看不到这些地址。
但是,它确实可以访问数组索引。
如果 WebAssembly 模块想要调用其中一个函数,它会将索引传递给一个名为 call_indirect
的操作。这将调用该函数。
现在,表的用例非常有限。它们被添加到规范中是为了专门支持这些函数指针,因为 C 和 C++ 非常依赖这些函数指针。
因此,您目前可以在表中放置的唯一类型的引用是函数引用。但是随着 WebAssembly 功能的扩展 - 例如,当直接访问 DOM 被添加时 - 您可能会看到其他类型的引用存储在表中,以及除了 call_indirect
之外的其他表操作。
关于 Lin Clark
Lin 在 Mozilla 的高级开发部门工作,专注于 Rust 和 WebAssembly。