WebAssembly Table and Indirect Calls Explained
This article explains the purpose of the WebAssembly (WASM) table section and how it enables indirect function calls. You will learn how tables act as secure, typed arrays for function references, allowing WebAssembly to safely implement dynamic dispatch, function pointers, and callbacks without compromising the security of the runtime environment.
The Problem: Security in a Sandboxed Environment
To understand tables and indirect calls, you must first understand WebAssembly’s security model. In traditional environments like C or C++, function pointers are represented as raw memory addresses. Programmers can jump to any memory address to execute code.
However, WebAssembly is designed to be highly secure and sandboxed. It isolates its linear memory from the executable code. If WASM allowed raw memory pointers to represent functions, a malicious actor could overwrite a pointer in memory to redirect execution to arbitrary, unauthorized code. To prevent this, WebAssembly does not allow direct access to code addresses.
What is the WebAssembly Table Section?
The WASM table section solves the security problem by introducing an abstraction layer. A table is a safe, indexable array that lives outside of WebAssembly’s linear memory.
Instead of storing raw memory addresses, a table stores references to functions. WebAssembly code cannot read or modify the actual underlying machine addresses of these functions; it can only reference them by their integer index within the table.
For example, if a table contains three functions, they are mapped as:
* Index 0 -> function_A * Index
1 -> function_B * Index 2
-> function_C
By using indexes instead of raw pointers, the runtime ensures that execution can only jump to valid, predefined entry points.
What are Indirect Function Calls?
In WebAssembly, there are two ways to call a function:
- Direct Calls (
call): The target function is hardcoded and known at compile time. - Indirect Calls (
call_indirect): The target function is determined dynamically at runtime.
An indirect function call uses the call_indirect
instruction to look up a function in a table using an index variable.
This is the mechanism that enables high-level language features such as:
* Function pointers in C and C++ * Virtual
method dispatch (polymorphism) in Object-Oriented programming *
Callbacks passed dynamically to other functions
How Type Safety is Maintained
To prevent type confusion attacks (where a program calls a function
expecting one signature but executes another with a different
signature), call_indirect enforces strict runtime type
checking.
When invoking call_indirect, the instruction specifies
an expected function signature (the parameter types and return types).
Before executing the function at the given table index, the WebAssembly
runtime verifies that the signature of the stored function matches the
expected signature. If the signatures do not match, or if the index is
out of bounds, the runtime immediately halts execution with a trap
(error).