SBPF.Interpreter — the small-step sBPF semantics #
The operational semantics of the Solana eBPF interpreter, ported from
Interpreter.thy: the per-class evaluators (evalAlu32/64, evalPqr*,
evalJmp, evalLoad/Store, the call/exit frame machinery), the single-step
function step, and the fuel-bounded driver bpfInterp.
Registers are raw U64; signed behaviour is expressed with BitVec's signed
operations (sdiv/srem/slt/sshiftRight) and sign-extension, faithful to
the source theory. Note the sBPF-specific quirk (Challenge 1 of the paper): the
32-bit ADD/SUB/MUL results are sign-extended into the 64-bit register,
whereas the logical/shift results are zero-extended.
Main definitions #
Solanalib.SBPF.step— execute one instruction, yielding aBpfState.Solanalib.SBPF.bpfInterp— run the program under a fuel bound.
Operand evaluation #
The second operand as a 32-bit value: immediate verbatim, or the low 32
bits of the source register (eval_snd_op_{i,u}32 — same bits either way).
Equations
- Solanalib.SBPF.sndOp32 (Solanalib.SBPF.SndOp.imm i) rs = i
- Solanalib.SBPF.sndOp32 (Solanalib.SBPF.SndOp.reg r) rs = BitVec.setWidth 32 (rs r)
Instances For
The second operand as a 64-bit value: a sign-extended immediate, or the
source register verbatim (eval_snd_op_{i,u}64 — same bits either way).
Equations
- Solanalib.SBPF.sndOp64 (Solanalib.SBPF.SndOp.imm i) rs = BitVec.signExtend 64 i
- Solanalib.SBPF.sndOp64 (Solanalib.SBPF.SndOp.reg r) rs = rs r
Instances For
ALU #
32-bit ALU (eval_alu32). Add/sub/mul sign-extend the 32-bit result;
the rest zero-extend. mul/div/mod are v1-only.
Equations
- One or more equations did not get rendered due to their size.
- Solanalib.SBPF.evalAlu32 Solanalib.SBPF.Binop.mov dst sop rs isV1 = Solanalib.SBPF.RegOutcome.oks (Solanalib.SBPF.setReg rs dst (BitVec.setWidth 64 (Solanalib.SBPF.sndOp32 sop rs)))
Instances For
64-bit ALU (eval_alu64). mul/div/mod are v1-only; the shift amount
is masked to 6 bits.
Equations
- One or more equations did not get rendered due to their size.
- Solanalib.SBPF.evalAlu64 Solanalib.SBPF.Binop.add dst sop rs isV1 = Solanalib.SBPF.RegOutcome.oks (Solanalib.SBPF.setReg rs dst (rs dst + Solanalib.SBPF.sndOp64 sop rs))
- Solanalib.SBPF.evalAlu64 Solanalib.SBPF.Binop.or dst sop rs isV1 = Solanalib.SBPF.RegOutcome.oks (Solanalib.SBPF.setReg rs dst (rs dst ||| Solanalib.SBPF.sndOp64 sop rs))
- Solanalib.SBPF.evalAlu64 Solanalib.SBPF.Binop.and dst sop rs isV1 = Solanalib.SBPF.RegOutcome.oks (Solanalib.SBPF.setReg rs dst (rs dst &&& Solanalib.SBPF.sndOp64 sop rs))
- Solanalib.SBPF.evalAlu64 Solanalib.SBPF.Binop.xor dst sop rs isV1 = Solanalib.SBPF.RegOutcome.oks (Solanalib.SBPF.setReg rs dst (rs dst ^^^ Solanalib.SBPF.sndOp64 sop rs))
- Solanalib.SBPF.evalAlu64 Solanalib.SBPF.Binop.mov dst sop rs isV1 = Solanalib.SBPF.RegOutcome.oks (Solanalib.SBPF.setReg rs dst (Solanalib.SBPF.sndOp64 sop rs))
- Solanalib.SBPF.evalAlu64 Solanalib.SBPF.Binop.lsh dst sop rs isV1 = Solanalib.SBPF.RegOutcome.oks (Solanalib.SBPF.setReg rs dst (rs dst <<< BitVec.toNat (Solanalib.SBPF.sndOp32 sop rs &&& 63)))
- Solanalib.SBPF.evalAlu64 Solanalib.SBPF.Binop.rsh dst sop rs isV1 = Solanalib.SBPF.RegOutcome.oks (Solanalib.SBPF.setReg rs dst (rs dst >>> BitVec.toNat (Solanalib.SBPF.sndOp32 sop rs &&& 63)))
Instances For
Add a (sign-extended) immediate to the stack pointer (eval_add64_imm_R10).
v2-only.
Equations
- One or more equations did not get rendered due to their size.
Instances For
32-bit negate (eval_neg32). v1-only.
Equations
- One or more equations did not get rendered due to their size.
Instances For
64-bit negate (eval_neg64). v1-only.
Equations
- Solanalib.SBPF.evalNeg64 dst rs isV1 = if isV1 = true then Solanalib.SBPF.RegOutcome.oks (Solanalib.SBPF.setReg rs dst (-rs dst)) else Solanalib.SBPF.RegOutcome.okn
Instances For
Little-endian byte swap / truncation (eval_le). v1-only.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Big-endian byte swap (eval_be): reverse the bytes before reassembly.
v1-only.
Equations
- One or more equations did not get rendered due to their size.
Instances For
OR a high-order 32-bit immediate into the top half of dst (eval_hor64).
v2-only.
Equations
- One or more equations did not get rendered due to their size.
Instances For
PQR (product / quotient / remainder) #
A division/remainder guard: zero divisor faults as nok for an immediate
operand (verifier should reject) and okn for a register operand.
Equations
- Solanalib.SBPF.pqrDivResult (Solanalib.SBPF.SndOp.imm i) rs' zero = if zero = true then Solanalib.SBPF.RegOutcome.nok else Solanalib.SBPF.RegOutcome.oks rs'
- Solanalib.SBPF.pqrDivResult (Solanalib.SBPF.SndOp.reg r) rs' zero = if zero = true then Solanalib.SBPF.RegOutcome.okn else Solanalib.SBPF.RegOutcome.oks rs'
Instances For
High-half multiply (eval_pqr64_2). v2-only.
The standard high-multiply semantics: widen both operands to 128 bits (zero-
extend for uhmul, sign-extend for shmul), multiply, and take the high 64
bits. (Interpreter.thy's eval_pqr64_2 appears to mis-source its signed
operand from dst; we implement the intended semantics and let differential
testing against the reference VM adjudicate.)
Equations
- One or more equations did not get rendered due to their size.
Instances For
Memory #
Store the second operand at dst + off (eval_store).
Equations
- Solanalib.SBPF.evalStore chk dst sop off rs m = Solanalib.SBPF.storev chk m (rs dst + BitVec.signExtend 64 off) (Solanalib.SBPF.memoryChunkValueOfU64 chk (Solanalib.SBPF.sndOp64 sop rs))
Instances For
Load from src + off into dst, zero-extending narrow loads (eval_load).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Load a 64-bit immediate assembled from two halves (eval_load_imm).
Equations
- Solanalib.SBPF.evalLoadImm dst imm1 imm2 rs = Solanalib.SBPF.setReg rs dst (BitVec.setWidth 64 imm1 &&& ↑4294967295 ||| BitVec.setWidth 64 imm2 <<< 32)
Instances For
Jump #
Evaluate a branch condition (eval_jmp).
Equations
- Solanalib.SBPF.evalJmp Solanalib.SBPF.Condition.eq dst sop rs = (rs dst == Solanalib.SBPF.sndOp64 sop rs)
- Solanalib.SBPF.evalJmp Solanalib.SBPF.Condition.gt dst sop rs = BitVec.ult (Solanalib.SBPF.sndOp64 sop rs) (rs dst)
- Solanalib.SBPF.evalJmp Solanalib.SBPF.Condition.ge dst sop rs = BitVec.ule (Solanalib.SBPF.sndOp64 sop rs) (rs dst)
- Solanalib.SBPF.evalJmp Solanalib.SBPF.Condition.lt dst sop rs = BitVec.ult (rs dst) (Solanalib.SBPF.sndOp64 sop rs)
- Solanalib.SBPF.evalJmp Solanalib.SBPF.Condition.le dst sop rs = BitVec.ule (rs dst) (Solanalib.SBPF.sndOp64 sop rs)
- Solanalib.SBPF.evalJmp Solanalib.SBPF.Condition.sEt dst sop rs = (rs dst &&& Solanalib.SBPF.sndOp64 sop rs != 0)
- Solanalib.SBPF.evalJmp Solanalib.SBPF.Condition.ne dst sop rs = (rs dst != Solanalib.SBPF.sndOp64 sop rs)
- Solanalib.SBPF.evalJmp Solanalib.SBPF.Condition.sGt dst sop rs = BitVec.slt (Solanalib.SBPF.sndOp64 sop rs) (rs dst)
- Solanalib.SBPF.evalJmp Solanalib.SBPF.Condition.sGe dst sop rs = BitVec.sle (Solanalib.SBPF.sndOp64 sop rs) (rs dst)
- Solanalib.SBPF.evalJmp Solanalib.SBPF.Condition.sLt dst sop rs = BitVec.slt (rs dst) (Solanalib.SBPF.sndOp64 sop rs)
- Solanalib.SBPF.evalJmp Solanalib.SBPF.Condition.sLe dst sop rs = BitVec.sle (rs dst) (Solanalib.SBPF.sndOp64 sop rs)
Instances For
Call / exit #
Push a new call frame, saving BR6–BR9 and the frame pointer
(push_frame). Returns none (with rs unchanged) when the call stack is
full.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Call the function whose address is in a register (eval_callReg).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Call the function identified by an immediate, via the registry when the
source register is BR0 (eval_callImm).
Equations
- One or more equations did not get rendered due to their size.
Instances For
The frame to return into (pop_frame).
Equations
- Solanalib.SBPF.popFrame ss = ss.callFrames.getD (BitVec.toNat (ss.callDepth - 1)) Solanalib.SBPF.emptyFrame✝
Instances For
Return from the current frame, restoring saved registers (eval_exit).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Step #
Lift an ALU-style RegOutcome into the post-state of a pc + 1 step.
Equations
- Solanalib.SBPF.stepRegOutcome Solanalib.SBPF.RegOutcome.nok pc m ss sv fm curCu remainCu = Solanalib.SBPF.BpfState.err
- Solanalib.SBPF.stepRegOutcome Solanalib.SBPF.RegOutcome.okn pc m ss sv fm curCu remainCu = Solanalib.SBPF.BpfState.eflag
- Solanalib.SBPF.stepRegOutcome (Solanalib.SBPF.RegOutcome.oks rs') pc m ss sv fm curCu remainCu = Solanalib.SBPF.BpfState.ok (pc + 1) rs' m ss sv fm (curCu + 1) remainCu
Instances For
Execute a single instruction (step).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Run the program under a fuel bound (bpf_interp). Each step is gated on PC
being in range and fuel remaining; exhaustion or an out-of-range PC yields a
fault.
Equations
- One or more equations did not get rendered due to their size.
- Solanalib.SBPF.bpfInterp 0 x✝³ x✝² x✝¹ x✝ = Solanalib.SBPF.BpfState.eflag
- Solanalib.SBPF.bpfInterp n.succ x✝² Solanalib.SBPF.BpfState.eflag x✝¹ x✝ = Solanalib.SBPF.BpfState.eflag
- Solanalib.SBPF.bpfInterp n.succ x✝² Solanalib.SBPF.BpfState.err x✝¹ x✝ = Solanalib.SBPF.BpfState.err
- Solanalib.SBPF.bpfInterp n.succ x✝² (Solanalib.SBPF.BpfState.success v) x✝¹ x✝ = Solanalib.SBPF.BpfState.success v