BPF Virtual Machine and Registers

The Berkeley Packet Filter virtual machine — almost always just called the BPF virtual machine or eBPF VM — is the abstract 64-bit RISC (Reduced Instruction Set Computer) processor that every BPF program is written for. It is not a piece of code you can point at: it is a specification of a machine with eleven 64-bit registers, a small fixed-size stack, a fixed-width instruction format, and a deliberately spartan calling convention. Its defining design choice is that this abstract machine was drawn to look like the real 64-bit hardware it runs on — x86-64, arm64, and friends — so that the JIT compiler can translate BPF registers to hardware registers very nearly one-to-one, with no register spilling or argument shuffling on a call (kernel docs, Classic BPF vs eBPF). Everything else about BPF — the verifier that proves safety, the instruction encoding, the maps that hold state — is built on top of this machine model. This note is the orientation for the whole VM: the registers, the stack, and why the register/calling convention is shaped the way it is. As of the 6.12 LTS kernel (released 2024-11-17), this model is also an internet standard: the BPF Instruction Set Architecture was published as RFC 9669 in October 2024 (RFC 9669).

Mental Model

Think of the BPF VM as a purpose-built RISC CPU with no operating system underneath it. It has registers and a stack like any CPU, but it has no interrupts, no I/O ports, no privileged mode of its own, and no way to call arbitrary addresses — it can only execute its own verified instruction stream, call a curated set of helper functions and kfuncs, and touch memory the verifier has proven it owns. The single most important thing to internalize is the layering: the VM is the contract between the compiler (which emits BPF bytecode) and the kernel (which verifies and JITs it). The compiler does not need to know which real CPU it will run on; the kernel’s per-architecture JIT does that translation. The VM is the seam that makes “compile once” possible.

flowchart TB
  subgraph VM["The BPF abstract machine (the contract)"]
    direction TB
    REGS["11 registers, all 64-bit<br/>R0 ret · R1–R5 args · R6–R9 callee-saved · R10 frame ptr (read-only)"]
    STACK["512-byte stack frame<br/>addressed only via R10"]
    INSN["fixed 64-bit instructions<br/>(see BPF Instruction Set)"]
    REGS --- STACK
    STACK --- INSN
  end
  CLANG["clang -target bpf<br/>emits BPF bytecode for the VM"] --> VM
  VM --> VERIF["Verifier<br/>proves the bytecode is safe"]
  VERIF --> JIT["Per-arch JIT<br/>R0→rax, R1→rdi, … R10→rbp (x86-64)<br/>~1:1 register mapping, no shuffling"]
  JIT --> HW["Native CPU<br/>(x86-64 / arm64 / …)"]

The BPF VM as the contract between compiler and kernel. What it shows: the compiler targets the abstract eleven-register machine; the verifier proves the bytecode safe; the JIT maps each abstract register onto a hardware register almost one-to-one because the VM was deliberately modeled on native ABIs. The insight to take: the VM’s register count and calling convention are not arbitrary — they were chosen so the rightmost arrow (JIT → hardware) is nearly free. That single design decision is why a JIT-compiled BPF program runs at near-native speed.

The Eleven Registers

The BPF VM has eleven registers, named R0 through R10, each 64 bits wide. They are declared in the kernel’s user-facing header as a plain enum (include/uapi/linux/bpf.h, v6.12):

/* Register numbers */
enum {
        BPF_REG_0 = 0,
        BPF_REG_1,
        ...
        BPF_REG_10,
        __MAX_BPF_REG,
};
/* BPF has 10 general purpose 64-bit registers and stack frame. */
#define MAX_BPF_REG     __MAX_BPF_REG

Notice the kernel comment counts “10 general purpose registers and a stack frame” — R0R9 are general-purpose, and R10 is the frame pointer that addresses the stack frame. Counting R10, there are eleven named registers total. Their roles are fixed by the calling convention and enforced by the verifier:

  • R0 — return value. When a BPF program calls a helper or kfunc, the callee leaves its return value in R0. When the program itself finishes (BPF_EXIT), the value it leaves in R0 is the program’s return value — and that value has meaning to the attach point: an XDP program returns XDP_PASS/XDP_DROP in R0, a BPF-LSM program returns an allow/deny verdict, and so on. The kernel’s design FAQ is explicit that BPF “allows only register R0 to be used as return value” — multiple return values are not and will not be supported (bpf_design_QA.rst).
  • R1R5 — function arguments (caller-saved / “scratch”). On entry, the program’s single context pointer (ctx) is already placed in R1 — for a socket filter R1 points at the sk_buff, for seccomp it points at seccomp_data, and so on (classic_vs_extended.rst). When the program calls a helper, it must place that helper’s arguments in R1R5. These five registers are caller-saved: after any call, the verifier marks R1R5 as containing junk (unreadable), so the program must spill any values it still needs to the stack before the call and reload them after. Reading R1R5 after a call without re-initializing them is a verifier rejection.
  • R6R9 — callee-saved. Their contents are preserved across a helper call, so they are where a program keeps the values it needs to survive calls — most commonly a saved copy of the ctx pointer. The JIT achieves this by mapping them onto hardware callee-saved registers (see below), so the called kernel function is obliged by the native ABI to restore them.
  • R10 — read-only frame pointer. R10 always points at the top of the program’s 512-byte stack frame and cannot be written. The verifier rejects any instruction that targets R10 as a destination. A program addresses its stack with offsets off R10 (e.g. *(u64 *)(R10 - 8) = ...). The design FAQ states the rule plainly: “Only frame pointer (register R10) is accessible” — there is no readable stack-pointer register, no instruction pointer, and no return-address register exposed to the program (bpf_design_QA.rst).

A subtlety worth nailing down because it trips people up: the kernel’s internal headers define an extra, twelfth register, BPF_REG_AX (auxiliary), in include/linux/filter.h (#define BPF_REG_AX MAX_BPF_REG). This is not part of the program-visible machine — it is a hidden scratch register the JIT and verifier use internally (for instance, to materialize constants during JIT hardening’s constant blinding). The programmer’s model is eleven registers, R0R10; R11/AX is plumbing.

Why the Stack Is 512 Bytes

Every BPF program gets a single stack frame of 512 bytes, defined as #define MAX_BPF_STACK 512 in include/linux/filter.h. This is the only writable scratch memory a program has that is not a map. It is reached exclusively through R10 (e.g. R10 - 8, R10 - 16, …); the verifier checks every such access is within [R10 - 512, R10) and that you never read a slot you have not first written (no uninitialized reads). The design FAQ confirms “all program types are limited to 512 bytes of stack space,” while noting the verifier computes the actual amount each program uses (bpf_design_QA.rst).

Why so small? Two reasons. First, BPF programs run in kernel context, often deep in a call chain (e.g. inside a NIC driver’s receive path), where the kernel’s stack is itself a scarce, fixed-size resource — a large per-program stack would risk kernel stack overflow. Second, a small bounded stack is one more thing that makes a program cheap to verify: the verifier must track the type and initialization state of every stack slot it can reach, and a 512-byte ceiling bounds that work. Programs that need more than 512 bytes of scratch space spill into a map (for example a per-CPU array map used as a large scratch buffer) — the canonical idiom for “I need a 4 KB buffer in a BPF program.” The deep mechanics of how the verifier tracks individual stack slots, spill/fill, and how nested BPF-to-BPF calls each get their own 512-byte frame are covered in The BPF Calling Convention and Stack; this note states the rule and the rationale.

Why the Calling Convention Mirrors Native ABIs

This is the crux of the VM’s design, and the reason BPF is fast rather than interpreted-slow. An Application Binary Interface (ABI) is the platform contract that says which CPU register holds which function argument and which registers a callee must preserve. The eBPF calling convention was deliberately drawn to match the native 64-bit ABIs so the JIT does almost no work to honor a call.

The kernel’s own explanation walks through it (classic_vs_extended.rst). On x86-64, the System V ABI passes the first arguments in rdi, rsi, rdx, rcx, r8, r9 and treats rbx, r12–r15 as callee-saved. So the x86-64 JIT maps BPF registers like this — and crucially this is the live mapping in the v6.12 kernel, not just an old illustration: it is the reg2hex[] table in arch/x86/net/bpf_jit_comp.c:

BPF reg   x86-64 reg   role on x86-64
R0    →   rax          return value (rax is the SysV return register)
R1    →   rdi          1st arg
R2    →   rsi          2nd arg
R3    →   rdx          3rd arg
R4    →   rcx          4th arg
R5    →   r8           5th arg
R6    →   rbx          callee-saved
R7    →   r13          callee-saved
R8    →   r14          callee-saved
R9    →   r15          callee-saved
R10   →   rbp          frame pointer (read-only)

Trace what this buys you. When a BPF program does bpf_call foo after loading its arguments into R1R5, those arguments are already sitting in rdi, rsi, rdx, rcx, r8 — exactly where the x86-64 ABI wants the arguments for a real C function call. So the JIT emits the BPF BPF_CALL instruction as a single native call instruction with no argument-shuffling moves at all (classic_vs_extended.rst). Likewise R0 is rax, so the helper’s return value lands exactly where BPF expects it. And because R6R9 map onto rbx/r13/r14/r15, which the System V ABI obliges every callee to preserve, “callee-saved across a BPF call” is enforced for free by the real ABI. The kernel doc shows a worked example where a BPF program computing foo(ctx,2,3,4,5) + bar(ctx,6,7,8,9) JITs to straight-line x86-64 with one callq foo, one callq bar, and no register juggling between them.

Contrast this with what would happen if the VM had, say, 16 argument registers or a stack-based calling convention: the JIT would have to translate between the BPF convention and the native ABI on every single call, inserting moves, and the elegant “one BPF call → one native call” property would be lost. The number of argument registers (five) was also chosen with native ABIs in mind: x86-64 passes six args in registers and arm64/sparcv9/mips64 pass seven or eight, so capping BPF at five (plus the implicit ctx) keeps the convention satisfiable everywhere with a direct mapping (classic_vs_extended.rst).

The registers being 64-bit is part of the same logic. On a 64-bit host, pointers are 64-bit and kernel functions pass 64-bit values, so 64-bit BPF registers map directly onto 64-bit hardware registers. Had BPF registers been 32-bit, the JIT would have to model 64-bit values as register pairs and split/combine them on every call — “complex, bug prone and slow,” in the kernel doc’s words (classic_vs_extended.rst). BPF still supports 32-bit operations, but as the lower subregister of each 64-bit register: a 32-bit write zero-extends into the upper 32 bits, which again matches how x86-64 and arm64 define their 32-bit subregisters (eax is the low half of rax, and writing eax zeroes the upper 32 bits of rax). The mechanics of 32-bit subregisters and ALU32 belong to BPF Instruction Set.

The Two Design Constraints

Everything above falls out of two constraints that the VM was engineered to satisfy simultaneously, and it is worth stating them explicitly because they explain almost every odd-looking decision in BPF.

  1. It must be verifiable. The kernel must be able to prove, statically and quickly, that a program cannot crash, leak kernel memory, or loop forever before it is allowed to run. This pushes toward a small, regular machine: a fixed instruction width, a tiny bounded stack, a handful of registers with fixed roles, no readable instruction pointer or return address (so control flow is analyzable), and — historically — no arbitrary jumps or unbounded loops. The verifier is the component that exploits this regularity; the VM is shaped to make the verifier’s job tractable.

  2. It must be fast to JIT. A verified program is useless if it runs through a slow interpreter. This pushes toward a machine that resembles real hardware: 64-bit registers, a calling convention matching the native ABI, two-operand instructions that map one-to-one onto x86/arm64 instructions during translation. The kernel doc says it outright — “eBPF is designed to be JITed with one to one mapping” (classic_vs_extended.rst). On most modern, hardened configurations the interpreter is compiled out entirely and every program is JITed, which is also a security posture (an interpreter is an attack surface). The relationship between the two is detailed in BPF Interpreter vs JIT.

These two pulls — small enough to prove, real enough to compile fast — are in tension, and the BPF VM is the equilibrium between them. When you later wonder “why can’t a BPF program read its stack pointer?” or “why only five arguments?” or “why is the stack so small?”, the answer is almost always one of these two constraints.

Common Misunderstandings

“There are ten registers.” A frequent off-by-one. There are ten general-purpose registers (R0R9) plus the read-only frame pointer R10, for eleven named registers visible to the program (bpf.h). The kernel-internal BPF_REG_AX/R11 auxiliary register is not part of the model — counting it as a “twelfth program register” is wrong.

“R10 is the stack pointer.” R10 is the frame pointer, fixed at the top of the frame for the program’s lifetime; it does not move as you push and pop. BPF has no movable stack-pointer register exposed to the program — the LLVM BPF backend internally uses R11 as a stack pointer but is required to never emit code that reads it (bpf_design_QA.rst). You allocate stack space implicitly by writing to negative offsets of R10.

“A BPF program can only call kernel helpers, never its own functions.” This was true historically and the old classic_vs_extended.rst prose still says “it cannot call other eBPF functions.” That statement is dated: modern kernels support BPF-to-BPF calls (a program calling its own subfunctions), each getting its own 512-byte stack frame. Treat the doc’s wording as a historical artifact, not current behavior.

Uncertain

Verify: the exact kernel version in which the program-visible register set was last changed and whether any program type sees a different register/stack model. Reason: the eleven-register / 512-byte-stack model is stable and verified against the v6.12 bpf.h and filter.h headers, but I did not exhaustively check every program type for special-casing. To resolve: diff MAX_BPF_REG / MAX_BPF_STACK across 6.12 and 6.18 LTS headers (expected: unchanged) and skim kernel/bpf/verifier.c for per-prog-type stack overrides. #uncertain

See Also

  • BPF Instruction Set — how those registers are actually manipulated: the 64-bit instruction encoding, the ALU/JMP/LD/ST classes, and the wide instruction for 64-bit immediates
  • The BPF Calling Convention and Stack — the deep mechanics of spill/fill, stack-slot tracking, and nested BPF-to-BPF call frames hinted at here
  • Classic BPF vs Extended BPF — how the two-register cBPF machine (A, X, hidden frame pointer) became this eleven-register VM
  • BPF JIT Compiler — the component that turns the abstract registers into real hardware registers using the near-1:1 mapping
  • BPF Interpreter vs JIT — the fallback interpreter (__bpf_prog_run) vs. the JIT, and why the interpreter is disabled on hardened builds
  • eBPF Verifier — the safety proof engine the small/regular VM was shaped to satisfy
  • BPF-to-BPF Function Calls — why the dated “cannot call other eBPF functions” claim is no longer true
  • Linux eBPF MOC — the parent map; this is the orientation note for §1, the BPF VM and instruction set