WebAssembly 线性内存幻境
摘要 WebAssembly 通过线性内存和严格的类型系统为宿主环境提供安全隔离,但当 Wasm 模块被编译为原生代码(如通过 wasm2c)嵌入宿主时,安全边界完全依赖于自动生成的“沙箱胶水代码”的正确性。这些胶水代码负责将 Wasm 的虚拟指令映射为 C 语言操作,强制进行边界检查和类型转换。然而,胶水代码中用于模拟 Wasm 内存和函数调用的 memcpy 与 wasm_rt_typecheck 存在一类隐蔽的 Type Confusion 漏洞:在零长度拷贝、类型嵌套及 OOB 指针算术等边界条件下,胶水代码可能错误地信任 Wasm 模块内部提供的类型标签,导致将攻击者控制的整数视为指针或函数地址,从而打破宿主进程的内存安全约束。 Wasm 线性内存与 wasm2c 沙箱模型 隔离 WebAssembly 的安全模型核心是线性内存——一个单一的、字节寻址的、从零开始的地址空间,与宿主进程的堆栈和堆严格隔离。Wasm 模块不能直接访问宿主内存,所有内存操作(i32.load、i64.store 等)都必须经过编译后的边界检查:i32.load 必须验证 address + offset + 4 不超过当前内存大小,否则抛出陷阱。这种机制在设计上杜绝了越界读写。 wasm2c wasm2c 是 WABT 工具集中的一个工具,可将 WebAssembly 二进制模块(.wasm)转换为等效的 C 源文件(.c 和 .h)。生成的 C 代码不依赖任何外部运行时,可被直接编译并与宿主程序链接。其核心包括: 操作数栈模拟:Wasm 值栈被转换为 C 局部变量或结构体字段。 函数调度:通过函数指针表(wasm_rt_func_table)实现间接调用,并通过签名索引进行类型检查。 内存访问:所有 load/store 由内联的边界检查守卫,访问超出 mem_size 则调用 wasm_rt_trap。 类型判断:胶水代码中大量使用 wasm_rt_typecheck 宏,比较 Wasm 模块提供的函数签名是否与调用点期望的类型匹配。 整个转换与运行的流程如下: 沙箱的安全性此时完全迁移到了这些胶水代码的正确性上:C 编译器不再理解 Wasm…