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:

  1. Direct Calls (call): The target function is hardcoded and known at compile time.
  2. 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).