solana_sbpf/
verifier.rs

1#![allow(clippy::arithmetic_side_effects)]
2// Derived from uBPF <https://github.com/iovisor/ubpf>
3// Copyright 2015 Big Switch Networks, Inc
4//      (uBPF: safety checks, originally in C)
5// Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
6//      (Translation to Rust)
7// Copyright 2020 Solana Maintainers <maintainers@solana.com>
8//
9// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
10// the MIT license <http://opensource.org/licenses/MIT>, at your option. This file may not be
11// copied, modified, or distributed except according to those terms.
12
13//! Verifies that the bytecode is valid for the given config.
14
15use crate::{
16    ebpf,
17    program::{BuiltinFunction, FunctionRegistry, SBPFVersion},
18    vm::{Config, ContextObject},
19};
20use thiserror::Error;
21
22/// Error definitions
23#[derive(Debug, Error, Eq, PartialEq)]
24pub enum VerifierError {
25    /// ProgramLengthNotMultiple
26    #[error("program length must be a multiple of {} octets", ebpf::INSN_SIZE)]
27    ProgramLengthNotMultiple,
28    /// Deprecated
29    #[error("Deprecated")]
30    ProgramTooLarge(usize),
31    /// NoProgram
32    #[error("no program set, call prog_set() to load one")]
33    NoProgram,
34    /// Division by zero
35    #[error("division by 0 (insn #{0})")]
36    DivisionByZero(usize),
37    /// UnsupportedLEBEArgument
38    #[error("unsupported argument for LE/BE (insn #{0})")]
39    UnsupportedLEBEArgument(usize),
40    /// LDDWCannotBeLast
41    #[error("LD_DW instruction cannot be last in program")]
42    LDDWCannotBeLast,
43    /// IncompleteLDDW
44    #[error("incomplete LD_DW instruction (insn #{0})")]
45    IncompleteLDDW(usize),
46    /// InfiniteLoop
47    #[error("infinite loop (insn #{0})")]
48    InfiniteLoop(usize),
49    /// JumpOutOfCode
50    #[error("jump out of code to #{0} (insn #{1})")]
51    JumpOutOfCode(usize, usize),
52    /// JumpToMiddleOfLDDW
53    #[error("jump to middle of LD_DW at #{0} (insn #{1})")]
54    JumpToMiddleOfLDDW(usize, usize),
55    /// InvalidSourceRegister
56    #[error("invalid source register (insn #{0})")]
57    InvalidSourceRegister(usize),
58    /// CannotWriteR10
59    #[error("cannot write into register r10 (insn #{0})")]
60    CannotWriteR10(usize),
61    /// InvalidDestinationRegister
62    #[error("invalid destination register (insn #{0})")]
63    InvalidDestinationRegister(usize),
64    /// UnknownOpCode
65    #[error("unknown eBPF opcode {0:#2x} (insn #{1:?})")]
66    UnknownOpCode(u8, usize),
67    /// Shift with overflow
68    #[error("Shift with overflow of {0}-bit value by {1} (insn #{2:?})")]
69    ShiftWithOverflow(u64, u64, usize),
70    /// Invalid register specified
71    #[error("Invalid register specified at instruction {0}")]
72    InvalidRegister(usize),
73    /// Invalid function
74    #[error("Invalid function at instruction {0}")]
75    InvalidFunction(usize),
76    /// Invalid syscall
77    #[error("Invalid syscall code {0}")]
78    InvalidSyscall(u32),
79    /// Unaligned immediate
80    #[error("Unaligned immediate (insn #{0})")]
81    UnalignedImmediate(usize),
82}
83
84/// eBPF Verifier
85pub trait Verifier {
86    /// eBPF verification function that returns an error if the program does not meet its requirements.
87    ///
88    /// Some examples of things the verifier may reject the program for:
89    ///
90    ///   - Program does not terminate.
91    ///   - Unknown instructions.
92    ///   - Bad formed instruction.
93    ///   - Unknown eBPF syscall index.
94    fn verify<C: ContextObject>(
95        prog: &[u8],
96        config: &Config,
97        sbpf_version: SBPFVersion,
98        function_registry: &FunctionRegistry<usize>,
99        syscall_registry: &FunctionRegistry<BuiltinFunction<C>>,
100    ) -> Result<(), VerifierError>;
101}
102
103fn check_prog_len(prog: &[u8]) -> Result<(), VerifierError> {
104    if prog.len() % ebpf::INSN_SIZE != 0 {
105        return Err(VerifierError::ProgramLengthNotMultiple);
106    }
107    if prog.is_empty() {
108        return Err(VerifierError::NoProgram);
109    }
110    Ok(())
111}
112
113fn check_imm_nonzero(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), VerifierError> {
114    if insn.imm == 0 {
115        return Err(VerifierError::DivisionByZero(insn_ptr));
116    }
117    Ok(())
118}
119
120fn check_imm_endian(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), VerifierError> {
121    match insn.imm {
122        16 | 32 | 64 => Ok(()),
123        _ => Err(VerifierError::UnsupportedLEBEArgument(insn_ptr)),
124    }
125}
126
127fn check_imm_aligned(
128    insn: &ebpf::Insn,
129    insn_ptr: usize,
130    alignment: i64,
131) -> Result<(), VerifierError> {
132    if (insn.imm & (alignment - 1)) == 0 {
133        Ok(())
134    } else {
135        Err(VerifierError::UnalignedImmediate(insn_ptr))
136    }
137}
138
139fn check_load_dw(prog: &[u8], insn_ptr: usize) -> Result<(), VerifierError> {
140    if (insn_ptr + 1) * ebpf::INSN_SIZE >= prog.len() {
141        // Last instruction cannot be LD_DW because there would be no 2nd DW
142        return Err(VerifierError::LDDWCannotBeLast);
143    }
144    let next_insn = ebpf::get_insn(prog, insn_ptr + 1);
145    if next_insn.opc != 0 {
146        return Err(VerifierError::IncompleteLDDW(insn_ptr));
147    }
148    Ok(())
149}
150
151fn check_jmp_offset(
152    prog: &[u8],
153    insn_ptr: usize,
154    function_range: &std::ops::Range<usize>,
155) -> Result<(), VerifierError> {
156    let insn = ebpf::get_insn(prog, insn_ptr);
157
158    let dst_insn_ptr = insn_ptr as isize + 1 + insn.off as isize;
159    if dst_insn_ptr < 0 || !function_range.contains(&(dst_insn_ptr as usize)) {
160        return Err(VerifierError::JumpOutOfCode(
161            dst_insn_ptr as usize,
162            insn_ptr,
163        ));
164    }
165    let dst_insn = ebpf::get_insn(prog, dst_insn_ptr as usize);
166    if dst_insn.opc == 0 {
167        return Err(VerifierError::JumpToMiddleOfLDDW(
168            dst_insn_ptr as usize,
169            insn_ptr,
170        ));
171    }
172    Ok(())
173}
174
175fn check_call_target<T>(
176    key: u32,
177    function_registry: &FunctionRegistry<T>,
178    error: VerifierError,
179) -> Result<(), VerifierError>
180where
181    T: Copy,
182    T: PartialEq,
183{
184    function_registry
185        .lookup_by_key(key)
186        .map(|_| ())
187        .ok_or(error)
188}
189
190fn check_registers(
191    insn: &ebpf::Insn,
192    store: bool,
193    insn_ptr: usize,
194    sbpf_version: SBPFVersion,
195) -> Result<(), VerifierError> {
196    if insn.src > 10 {
197        return Err(VerifierError::InvalidSourceRegister(insn_ptr));
198    }
199
200    match (insn.dst, store) {
201        (0..=9, _) | (10, true) => Ok(()),
202        (10, false) if sbpf_version.dynamic_stack_frames() && insn.opc == ebpf::ADD64_IMM => Ok(()),
203        (10, false) => Err(VerifierError::CannotWriteR10(insn_ptr)),
204        (_, _) => Err(VerifierError::InvalidDestinationRegister(insn_ptr)),
205    }
206}
207
208/// Check that the imm is a valid shift operand
209fn check_imm_shift(insn: &ebpf::Insn, insn_ptr: usize, imm_bits: u64) -> Result<(), VerifierError> {
210    let shift_by = insn.imm as u64;
211    if insn.imm < 0 || shift_by >= imm_bits {
212        return Err(VerifierError::ShiftWithOverflow(
213            shift_by, imm_bits, insn_ptr,
214        ));
215    }
216    Ok(())
217}
218
219/// Check that callx has a valid register number
220fn check_callx_register(
221    insn: &ebpf::Insn,
222    insn_ptr: usize,
223    sbpf_version: SBPFVersion,
224) -> Result<(), VerifierError> {
225    let reg = if sbpf_version.callx_uses_src_reg() {
226        insn.src as i64
227    } else {
228        insn.imm
229    };
230    if !(0..10).contains(&reg) {
231        return Err(VerifierError::InvalidRegister(insn_ptr));
232    }
233    Ok(())
234}
235
236/// Mandatory verifier for solana programs to run on-chain
237#[derive(Debug)]
238pub struct RequisiteVerifier {}
239impl Verifier for RequisiteVerifier {
240    /// Check the program against the verifier's rules
241    #[rustfmt::skip]
242    fn verify<C: ContextObject>(prog: &[u8], _config: &Config, sbpf_version: SBPFVersion, function_registry: &FunctionRegistry<usize>, syscall_registry: &FunctionRegistry<BuiltinFunction<C>>) -> Result<(), VerifierError> {
243        check_prog_len(prog)?;
244
245        let program_range = 0..prog.len() / ebpf::INSN_SIZE;
246        let mut function_iter = function_registry.keys().map(|insn_ptr| insn_ptr as usize).peekable();
247        let mut function_range = program_range.start..program_range.end;
248        let mut insn_ptr: usize = 0;
249        while (insn_ptr + 1) * ebpf::INSN_SIZE <= prog.len() {
250            let insn = ebpf::get_insn(prog, insn_ptr);
251            let mut store = false;
252
253            if sbpf_version.static_syscalls() && function_iter.peek() == Some(&insn_ptr) {
254                function_range.start = function_iter.next().unwrap_or(0);
255                function_range.end = *function_iter.peek().unwrap_or(&program_range.end);
256                let insn = ebpf::get_insn(prog, function_range.end.saturating_sub(1));
257                match insn.opc {
258                    ebpf::JA | ebpf::RETURN => {},
259                    _ => return Err(VerifierError::InvalidFunction(
260                        function_range.end.saturating_sub(1),
261                    )),
262                }
263            }
264
265            match insn.opc {
266                ebpf::LD_DW_IMM if !sbpf_version.disable_lddw() => {
267                    check_load_dw(prog, insn_ptr)?;
268                    insn_ptr += 1;
269                },
270
271                // BPF_LDX class
272                ebpf::LD_B_REG  if !sbpf_version.move_memory_instruction_classes() => {},
273                ebpf::LD_H_REG  if !sbpf_version.move_memory_instruction_classes() => {},
274                ebpf::LD_W_REG  if !sbpf_version.move_memory_instruction_classes() => {},
275                ebpf::LD_DW_REG if !sbpf_version.move_memory_instruction_classes() => {},
276
277                // BPF_ST class
278                ebpf::ST_B_IMM  if !sbpf_version.move_memory_instruction_classes() => store = true,
279                ebpf::ST_H_IMM  if !sbpf_version.move_memory_instruction_classes() => store = true,
280                ebpf::ST_W_IMM  if !sbpf_version.move_memory_instruction_classes() => store = true,
281                ebpf::ST_DW_IMM if !sbpf_version.move_memory_instruction_classes() => store = true,
282
283                // BPF_STX class
284                ebpf::ST_B_REG  if !sbpf_version.move_memory_instruction_classes() => store = true,
285                ebpf::ST_H_REG  if !sbpf_version.move_memory_instruction_classes() => store = true,
286                ebpf::ST_W_REG  if !sbpf_version.move_memory_instruction_classes() => store = true,
287                ebpf::ST_DW_REG if !sbpf_version.move_memory_instruction_classes() => store = true,
288
289                // BPF_ALU32_LOAD class
290                ebpf::ADD32_IMM  => {},
291                ebpf::ADD32_REG  => {},
292                ebpf::SUB32_IMM  => {},
293                ebpf::SUB32_REG  => {},
294                ebpf::MUL32_IMM  if !sbpf_version.enable_pqr() => {},
295                ebpf::MUL32_REG  if !sbpf_version.enable_pqr() => {},
296                ebpf::LD_1B_REG  if sbpf_version.move_memory_instruction_classes() => {},
297                ebpf::DIV32_IMM  if !sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
298                ebpf::DIV32_REG  if !sbpf_version.enable_pqr() => {},
299                ebpf::LD_2B_REG  if sbpf_version.move_memory_instruction_classes() => {},
300                ebpf::OR32_IMM   => {},
301                ebpf::OR32_REG   => {},
302                ebpf::AND32_IMM  => {},
303                ebpf::AND32_REG  => {},
304                ebpf::LSH32_IMM  => { check_imm_shift(&insn, insn_ptr, 32)?; },
305                ebpf::LSH32_REG  => {},
306                ebpf::RSH32_IMM  => { check_imm_shift(&insn, insn_ptr, 32)?; },
307                ebpf::RSH32_REG  => {},
308                ebpf::NEG32      if !sbpf_version.disable_neg() => {},
309                ebpf::LD_4B_REG  if sbpf_version.move_memory_instruction_classes() => {},
310                ebpf::MOD32_IMM  if !sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
311                ebpf::MOD32_REG  if !sbpf_version.enable_pqr() => {},
312                ebpf::LD_8B_REG  if sbpf_version.move_memory_instruction_classes() => {},
313                ebpf::XOR32_IMM  => {},
314                ebpf::XOR32_REG  => {},
315                ebpf::MOV32_IMM  => {},
316                ebpf::MOV32_REG  => {},
317                ebpf::ARSH32_IMM => { check_imm_shift(&insn, insn_ptr, 32)?; },
318                ebpf::ARSH32_REG => {},
319                ebpf::LE         if !sbpf_version.disable_le() => { check_imm_endian(&insn, insn_ptr)?; },
320                ebpf::BE         => { check_imm_endian(&insn, insn_ptr)?; },
321
322                // BPF_ALU64_STORE class
323                ebpf::ADD64_IMM  if insn.dst == ebpf::FRAME_PTR_REG as u8 && sbpf_version.dynamic_stack_frames() => {
324                    check_imm_aligned(&insn, insn_ptr, 64)?;
325                },
326                ebpf::ADD64_IMM  => {},
327                ebpf::ADD64_REG  => {},
328                ebpf::SUB64_IMM  => {},
329                ebpf::SUB64_REG  => {},
330                ebpf::MUL64_IMM  if !sbpf_version.enable_pqr() => {},
331                ebpf::ST_1B_IMM  if sbpf_version.move_memory_instruction_classes() => store = true,
332                ebpf::MUL64_REG  if !sbpf_version.enable_pqr() => {},
333                ebpf::ST_1B_REG  if sbpf_version.move_memory_instruction_classes() => store = true,
334                ebpf::DIV64_IMM  if !sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
335                ebpf::ST_2B_IMM  if sbpf_version.move_memory_instruction_classes() => store = true,
336                ebpf::DIV64_REG  if !sbpf_version.enable_pqr() => {},
337                ebpf::ST_2B_REG  if sbpf_version.move_memory_instruction_classes() => store = true,
338                ebpf::OR64_IMM   => {},
339                ebpf::OR64_REG   => {},
340                ebpf::AND64_IMM  => {},
341                ebpf::AND64_REG  => {},
342                ebpf::LSH64_IMM  => { check_imm_shift(&insn, insn_ptr, 64)?; },
343                ebpf::LSH64_REG  => {},
344                ebpf::RSH64_IMM  => { check_imm_shift(&insn, insn_ptr, 64)?; },
345                ebpf::RSH64_REG  => {},
346                ebpf::ST_4B_IMM  if sbpf_version.move_memory_instruction_classes() => store = true,
347                ebpf::NEG64      if !sbpf_version.disable_neg() => {},
348                ebpf::ST_4B_REG  if sbpf_version.move_memory_instruction_classes() => store = true,
349                ebpf::MOD64_IMM  if !sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
350                ebpf::ST_8B_IMM  if sbpf_version.move_memory_instruction_classes() => store = true,
351                ebpf::MOD64_REG  if !sbpf_version.enable_pqr() => {},
352                ebpf::ST_8B_REG  if sbpf_version.move_memory_instruction_classes() => store = true,
353                ebpf::XOR64_IMM  => {},
354                ebpf::XOR64_REG  => {},
355                ebpf::MOV64_IMM  => {},
356                ebpf::MOV64_REG  => {},
357                ebpf::ARSH64_IMM => { check_imm_shift(&insn, insn_ptr, 64)?; },
358                ebpf::ARSH64_REG => {},
359                ebpf::HOR64_IMM  if sbpf_version.disable_lddw() => {},
360
361                // BPF_PQR class
362                ebpf::LMUL32_IMM if sbpf_version.enable_pqr() => {},
363                ebpf::LMUL32_REG if sbpf_version.enable_pqr() => {},
364                ebpf::LMUL64_IMM if sbpf_version.enable_pqr() => {},
365                ebpf::LMUL64_REG if sbpf_version.enable_pqr() => {},
366                ebpf::UHMUL64_IMM if sbpf_version.enable_pqr() => {},
367                ebpf::UHMUL64_REG if sbpf_version.enable_pqr() => {},
368                ebpf::SHMUL64_IMM if sbpf_version.enable_pqr() => {},
369                ebpf::SHMUL64_REG if sbpf_version.enable_pqr() => {},
370                ebpf::UDIV32_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
371                ebpf::UDIV32_REG if sbpf_version.enable_pqr() => {},
372                ebpf::UDIV64_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
373                ebpf::UDIV64_REG if sbpf_version.enable_pqr() => {},
374                ebpf::UREM32_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
375                ebpf::UREM32_REG if sbpf_version.enable_pqr() => {},
376                ebpf::UREM64_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
377                ebpf::UREM64_REG if sbpf_version.enable_pqr() => {},
378                ebpf::SDIV32_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
379                ebpf::SDIV32_REG if sbpf_version.enable_pqr() => {},
380                ebpf::SDIV64_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
381                ebpf::SDIV64_REG if sbpf_version.enable_pqr() => {},
382                ebpf::SREM32_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
383                ebpf::SREM32_REG if sbpf_version.enable_pqr() => {},
384                ebpf::SREM64_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
385                ebpf::SREM64_REG if sbpf_version.enable_pqr() => {},
386
387                // BPF_JMP class
388                ebpf::JA         => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
389                ebpf::JEQ_IMM    => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
390                ebpf::JEQ_REG    => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
391                ebpf::JGT_IMM    => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
392                ebpf::JGT_REG    => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
393                ebpf::JGE_IMM    => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
394                ebpf::JGE_REG    => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
395                ebpf::JLT_IMM    => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
396                ebpf::JLT_REG    => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
397                ebpf::JLE_IMM    => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
398                ebpf::JLE_REG    => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
399                ebpf::JSET_IMM   => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
400                ebpf::JSET_REG   => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
401                ebpf::JNE_IMM    => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
402                ebpf::JNE_REG    => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
403                ebpf::JSGT_IMM   => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
404                ebpf::JSGT_REG   => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
405                ebpf::JSGE_IMM   => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
406                ebpf::JSGE_REG   => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
407                ebpf::JSLT_IMM   => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
408                ebpf::JSLT_REG   => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
409                ebpf::JSLE_IMM   => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
410                ebpf::JSLE_REG   => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
411                ebpf::CALL_IMM   if sbpf_version.static_syscalls() => {
412                    let target_pc = sbpf_version.calculate_call_imm_target_pc(insn_ptr, insn.imm);
413                    check_call_target(
414                        target_pc,
415                        function_registry,
416                        VerifierError::InvalidFunction(target_pc as usize)
417                    )?;
418                },
419                ebpf::CALL_IMM   => {},
420                ebpf::CALL_REG   => { check_callx_register(&insn, insn_ptr, sbpf_version)?; },
421                ebpf::EXIT       if !sbpf_version.static_syscalls()   => {},
422                ebpf::RETURN     if sbpf_version.static_syscalls()    => {},
423                ebpf::SYSCALL    if sbpf_version.static_syscalls()    => {
424                    check_call_target(
425                        insn.imm as u32,
426                        syscall_registry,
427                        VerifierError::InvalidSyscall(insn.imm as u32))?;
428                },
429
430                _                => {
431                    return Err(VerifierError::UnknownOpCode(insn.opc, insn_ptr));
432                }
433            }
434
435            check_registers(&insn, store, insn_ptr, sbpf_version)?;
436
437            insn_ptr += 1;
438        }
439
440        // insn_ptr should now be equal to number of instructions.
441        if insn_ptr != prog.len() / ebpf::INSN_SIZE {
442            return Err(VerifierError::JumpOutOfCode(insn_ptr, insn_ptr));
443        }
444
445        Ok(())
446    }
447}