V8pedia

The V8 sandbox

V8 is a high-value attack target: a memory-safety bug in the engine can become a full exploit of the host process (a browser tab, a server). The V8 sandbox is a defense-in-depth mechanism that assumes an attacker can corrupt memory inside V8 and tries to stop that corruption from reaching the rest of the process. This page is a high-level orientation, not an exploitation guide.

::: info Ubiquitous language Sandbox: a reserved virtual-address region V8 places its objects in. Sandboxed pointer: an intra-sandbox reference stored as an offset, not a raw pointer. External pointer table: an indirection table for references to memory outside the sandbox. :::

The threat model

The sandbox starts from a humbling premise, stated in the header:

// When enabled, V8 reserves a large region of virtual address space - the
// sandbox - and places most of its objects inside of it. It is then assumed
// that an attacker can, by exploiting a vulnerability in V8, corrupt memory
// inside the sandbox arbitrarily and from different threads. The sandbox
// attempts to stop an attacker from corrupting other memory in the process.

src/sandbox/sandbox.h#L23-L150

In other words: V8 does not claim its bugs are unreachable. It accepts that type-confusion and out-of-bounds bugs happen, and builds a wall so that their blast radius is limited to the sandbox region instead of the whole address space.

The layout

+-  ~~~  -+----------------------------------------  ~~~  -+-  ~~~  -+
|  32 GB  |                 (Ideally) 1 TB                 |  32 GB  |
| Guard   |      4 GB      :  ArrayBuffer backing stores,  | Guard   |
| Region  |    V8 Heap     :  WASM memory, other sandboxed | Region  |
| (front) |    Region      :  objects                      | (back)  |
+-  ~~~  -+----------------+-----------------------  ~~~  -+-  ~~~  -+

— layout comment in src/sandbox/sandbox.h

The pointer-compression cage — the 4 GB region holding most V8 objects — sits at the front of the sandbox. The rest holds large buffers (ArrayBuffers, WASM memory). Large guard regions flank it so that an out-of-bounds access near the edges faults instead of hitting valid memory.

How it contains corruption

Two indirection mechanisms do the heavy lifting:

  1. Sandboxed (compressed) pointers. Object-to-object references inside the cage are 32-bit offsets from the cage base (see pointer compression). A corrupted reference can therefore only ever name an address within the 4 GB cage — it is structurally incapable of pointing at arbitrary process memory. Memory efficiency and security come from the same design.

  2. External pointer table. References to memory outside the sandbox (host C++ objects, native callbacks) are not stored as raw pointers inside sandboxed objects. They are stored as indices into a per-isolate external pointer table; the real pointer lives in the table, which is outside the sandbox. A corrupted index just selects a different (type-checked) table slot — it cannot forge an arbitrary external pointer.

The reserved Smi address range is also kept inaccessible to help catch Smi↔HeapObject confusion:

bool smi_address_range_is_inaccessible() const {
  return smi_address_range_reserved_;
}

src/sandbox/sandbox.h

What it is and isn't

  • It is an in-process mitigation: it raises the bar from "memory bug → process compromise" to "memory bug → contained corruption that still needs a second bug to escape."

  • It is not a substitute for the OS process sandbox (site isolation) or for fixing the underlying bugs. It is one layer of defense in depth.

  • It is gated behind V8_ENABLE_SANDBOX and interacts with pointer compression (the cage is its core).

::: tip Why a language-runtime author should care The sandbox is a case study in designing for the inevitability of bugs. Rather than only trying to be bug-free, V8 reshapes its pointer representations so that the common corruption primitives are structurally weaker. The lesson — choose representations (offsets, table indices) that bound an attacker's reach — applies to any runtime that runs untrusted input. :::

See also