Skip to main content

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::{ebpf, program::SBPFVersion, vm::Config};
16use thiserror::Error;
17
18/// Error definitions
19#[derive(Debug, Error, Eq, PartialEq)]
20pub enum VerifierError {
21    /// ProgramLengthNotMultiple
22    #[error("program length must be a multiple of {} octets", ebpf::INSN_SIZE)]
23    ProgramLengthNotMultiple,
24    /// Deprecated
25    #[error("Deprecated")]
26    ProgramTooLarge(usize),
27    /// NoProgram
28    #[error("no program set, call prog_set() to load one")]
29    NoProgram,
30    /// Division by zero
31    #[error("division by 0 (insn #{0})")]
32    DivisionByZero(usize),
33    /// UnsupportedLEBEArgument
34    #[error("unsupported argument for LE/BE (insn #{0})")]
35    UnsupportedLEBEArgument(usize),
36    /// LDDWCannotBeLast
37    #[error("LD_DW instruction cannot be last in program")]
38    LDDWCannotBeLast,
39    /// IncompleteLDDW
40    #[error("incomplete LD_DW instruction (insn #{0})")]
41    IncompleteLDDW(usize),
42    /// InfiniteLoop
43    #[error("infinite loop (insn #{0})")]
44    InfiniteLoop(usize),
45    /// JumpOutOfCode
46    #[error("jump out of code to #{0} (insn #{1})")]
47    JumpOutOfCode(usize, usize),
48    /// JumpToMiddleOfLDDW
49    #[error("jump to middle of LD_DW at #{0} (insn #{1})")]
50    JumpToMiddleOfLDDW(usize, usize),
51    /// InvalidSourceRegister
52    #[error("invalid source register (insn #{0})")]
53    InvalidSourceRegister(usize),
54    /// CannotWriteR10
55    #[error("cannot write into register r10 (insn #{0})")]
56    CannotWriteR10(usize),
57    /// InvalidDestinationRegister
58    #[error("invalid destination register (insn #{0})")]
59    InvalidDestinationRegister(usize),
60    /// UnknownOpCode
61    #[error("unknown eBPF opcode {0:#2x} (insn #{1:?})")]
62    UnknownOpCode(u8, usize),
63    /// Shift with overflow
64    #[error("Shift with overflow of {0}-bit value by {1} (insn #{2:?})")]
65    ShiftWithOverflow(u64, u64, usize),
66    /// Invalid register specified
67    #[error("Invalid register specified at instruction {0}")]
68    InvalidRegister(usize),
69    /// Invalid function
70    #[error("Invalid function at instruction {0}")]
71    InvalidFunction(usize),
72    /// Invalid syscall
73    #[error("Invalid syscall code {0}")]
74    InvalidSyscall(u32),
75    /// Unaligned immediate
76    #[error("Unaligned immediate (insn #{0})")]
77    UnalignedImmediate(usize),
78}
79
80/// eBPF Verifier
81pub trait Verifier {
82    /// eBPF verification function that returns an error if the program does not meet its requirements.
83    ///
84    /// Some examples of things the verifier may reject the program for:
85    ///
86    ///   - Program does not terminate.
87    ///   - Unknown instructions.
88    ///   - Bad formed instruction.
89    ///   - Unknown eBPF syscall index.
90    fn verify(prog: &[u8], config: &Config, sbpf_version: SBPFVersion)
91        -> Result<(), VerifierError>;
92}
93
94fn check_prog_len(prog: &[u8]) -> Result<(), VerifierError> {
95    if prog.len().checked_rem(ebpf::INSN_SIZE) != Some(0) {
96        return Err(VerifierError::ProgramLengthNotMultiple);
97    }
98    if prog.is_empty() {
99        return Err(VerifierError::NoProgram);
100    }
101    Ok(())
102}
103
104fn check_imm_nonzero(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), VerifierError> {
105    if insn.imm == 0 {
106        return Err(VerifierError::DivisionByZero(insn_ptr));
107    }
108    Ok(())
109}
110
111fn check_imm_endian(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), VerifierError> {
112    match insn.imm {
113        16 | 32 | 64 => Ok(()),
114        _ => Err(VerifierError::UnsupportedLEBEArgument(insn_ptr)),
115    }
116}
117
118fn check_imm_aligned(
119    insn: &ebpf::Insn,
120    insn_ptr: usize,
121    alignment: i64,
122) -> Result<(), VerifierError> {
123    if (insn.imm & (alignment - 1)) == 0 {
124        Ok(())
125    } else {
126        Err(VerifierError::UnalignedImmediate(insn_ptr))
127    }
128}
129
130fn check_load_dw(prog: &[u8], insn_ptr: usize) -> Result<(), VerifierError> {
131    if (insn_ptr + 1) * ebpf::INSN_SIZE >= prog.len() {
132        // Last instruction cannot be LD_DW because there would be no 2nd DW
133        return Err(VerifierError::LDDWCannotBeLast);
134    }
135    let next_insn = ebpf::get_insn(prog, insn_ptr + 1);
136    if next_insn.opc != 0 {
137        return Err(VerifierError::IncompleteLDDW(insn_ptr));
138    }
139    Ok(())
140}
141
142fn check_jmp_offset(
143    prog: &[u8],
144    insn_ptr: usize,
145    program_range: &std::ops::Range<usize>,
146) -> Result<(), VerifierError> {
147    let insn = ebpf::get_insn(prog, insn_ptr);
148
149    let dst_insn_ptr = insn_ptr as isize + 1 + insn.off as isize;
150    if dst_insn_ptr < 0 || !program_range.contains(&(dst_insn_ptr as usize)) {
151        return Err(VerifierError::JumpOutOfCode(
152            dst_insn_ptr as usize,
153            insn_ptr,
154        ));
155    }
156    let dst_insn = ebpf::get_insn(prog, dst_insn_ptr as usize);
157    if dst_insn.opc == 0 {
158        return Err(VerifierError::JumpToMiddleOfLDDW(
159            dst_insn_ptr as usize,
160            insn_ptr,
161        ));
162    }
163    Ok(())
164}
165
166fn check_registers(
167    insn: &ebpf::Insn,
168    store: bool,
169    insn_ptr: usize,
170    sbpf_version: SBPFVersion,
171) -> Result<(), VerifierError> {
172    if insn.src > 10 {
173        return Err(VerifierError::InvalidSourceRegister(insn_ptr));
174    }
175
176    match (insn.dst, store) {
177        (0..=9, _) | (10, true) => Ok(()),
178        (10, false) if sbpf_version.manual_stack_frame_bump() && insn.opc == ebpf::ADD64_IMM => {
179            Ok(())
180        }
181        (10, false) => Err(VerifierError::CannotWriteR10(insn_ptr)),
182        (_, _) => Err(VerifierError::InvalidDestinationRegister(insn_ptr)),
183    }
184}
185
186/// Check that the imm is a valid shift operand
187fn check_imm_shift(insn: &ebpf::Insn, insn_ptr: usize, imm_bits: u64) -> Result<(), VerifierError> {
188    let shift_by = insn.imm as u64;
189    if insn.imm < 0 || shift_by >= imm_bits {
190        return Err(VerifierError::ShiftWithOverflow(
191            shift_by, imm_bits, insn_ptr,
192        ));
193    }
194    Ok(())
195}
196
197/// Check that callx has a valid register number
198fn check_callx_register(
199    insn: &ebpf::Insn,
200    insn_ptr: usize,
201    sbpf_version: SBPFVersion,
202) -> Result<(), VerifierError> {
203    let reg = if sbpf_version.callx_uses_src_reg() {
204        insn.src as i64
205    } else if sbpf_version.callx_uses_dst_reg() {
206        insn.dst as i64
207    } else {
208        insn.imm
209    };
210    if !(0..10).contains(&reg) {
211        return Err(VerifierError::InvalidRegister(insn_ptr));
212    }
213    Ok(())
214}
215
216/// Mandatory verifier for solana programs to run on-chain
217#[derive(Debug)]
218pub struct RequisiteVerifier {}
219impl Verifier for RequisiteVerifier {
220    /// Check the program against the verifier's rules
221    #[rustfmt::skip]
222    fn verify(prog: &[u8], _config: &Config, sbpf_version: SBPFVersion) -> Result<(), VerifierError> {
223        check_prog_len(prog)?;
224
225        let program_range = 0..prog.len() / ebpf::INSN_SIZE;
226        let mut insn_ptr: usize = 0;
227        while (insn_ptr + 1) * ebpf::INSN_SIZE <= prog.len() {
228            let insn = ebpf::get_insn(prog, insn_ptr);
229            let mut store = false;
230
231            match insn.opc {
232                ebpf::LD_DW_IMM if !sbpf_version.disable_lddw() => {
233                    check_load_dw(prog, insn_ptr)?;
234                    insn_ptr += 1;
235                },
236
237                // BPF_LDX class
238                ebpf::LD_B_REG  if !sbpf_version.move_memory_instruction_classes() => {},
239                ebpf::LD_H_REG  if !sbpf_version.move_memory_instruction_classes() => {},
240                ebpf::LD_W_REG  if !sbpf_version.move_memory_instruction_classes() => {},
241                ebpf::LD_DW_REG if !sbpf_version.move_memory_instruction_classes() => {},
242
243                // BPF_ST class
244                ebpf::ST_B_IMM  if !sbpf_version.move_memory_instruction_classes() => store = true,
245                ebpf::ST_H_IMM  if !sbpf_version.move_memory_instruction_classes() => store = true,
246                ebpf::ST_W_IMM  if !sbpf_version.move_memory_instruction_classes() => store = true,
247                ebpf::ST_DW_IMM if !sbpf_version.move_memory_instruction_classes() => store = true,
248
249                // BPF_STX class
250                ebpf::ST_B_REG  if !sbpf_version.move_memory_instruction_classes() => store = true,
251                ebpf::ST_H_REG  if !sbpf_version.move_memory_instruction_classes() => store = true,
252                ebpf::ST_W_REG  if !sbpf_version.move_memory_instruction_classes() => store = true,
253                ebpf::ST_DW_REG if !sbpf_version.move_memory_instruction_classes() => store = true,
254
255                // BPF_ALU32_LOAD class
256                ebpf::ADD32_IMM  => {},
257                ebpf::ADD32_REG  => {},
258                ebpf::SUB32_IMM  => {},
259                ebpf::SUB32_REG  => {},
260                ebpf::MUL32_IMM  if !sbpf_version.enable_pqr() => {},
261                ebpf::MUL32_REG  if !sbpf_version.enable_pqr() => {},
262                ebpf::LD_1B_REG  if sbpf_version.move_memory_instruction_classes() => {},
263                ebpf::DIV32_IMM  if !sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
264                ebpf::DIV32_REG  if !sbpf_version.enable_pqr() => {},
265                ebpf::LD_2B_REG  if sbpf_version.move_memory_instruction_classes() => {},
266                ebpf::OR32_IMM   => {},
267                ebpf::OR32_REG   => {},
268                ebpf::AND32_IMM  => {},
269                ebpf::AND32_REG  => {},
270                ebpf::LSH32_IMM  => { check_imm_shift(&insn, insn_ptr, 32)?; },
271                ebpf::LSH32_REG  => {},
272                ebpf::RSH32_IMM  => { check_imm_shift(&insn, insn_ptr, 32)?; },
273                ebpf::RSH32_REG  => {},
274                ebpf::NEG32      if !sbpf_version.disable_neg() => {},
275                ebpf::LD_4B_REG  if sbpf_version.move_memory_instruction_classes() => {},
276                ebpf::MOD32_IMM  if !sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
277                ebpf::MOD32_REG  if !sbpf_version.enable_pqr() => {},
278                ebpf::LD_8B_REG  if sbpf_version.move_memory_instruction_classes() => {},
279                ebpf::XOR32_IMM  => {},
280                ebpf::XOR32_REG  => {},
281                ebpf::MOV32_IMM  => {},
282                ebpf::MOV32_REG  => {},
283                ebpf::ARSH32_IMM => { check_imm_shift(&insn, insn_ptr, 32)?; },
284                ebpf::ARSH32_REG => {},
285                ebpf::LE         if !sbpf_version.disable_le() => { check_imm_endian(&insn, insn_ptr)?; },
286                ebpf::BE         => { check_imm_endian(&insn, insn_ptr)?; },
287
288                // BPF_ALU64_STORE class
289                ebpf::ADD64_IMM  if insn.dst == ebpf::FRAME_PTR_REG as u8 && sbpf_version.manual_stack_frame_bump() => {
290                    check_imm_aligned(&insn, insn_ptr, 64)?;
291                },
292                ebpf::ADD64_IMM  => {},
293                ebpf::ADD64_REG  => {},
294                ebpf::SUB64_IMM  => {},
295                ebpf::SUB64_REG  => {},
296                ebpf::MUL64_IMM  if !sbpf_version.enable_pqr() => {},
297                ebpf::ST_1B_IMM  if sbpf_version.move_memory_instruction_classes() => store = true,
298                ebpf::MUL64_REG  if !sbpf_version.enable_pqr() => {},
299                ebpf::ST_1B_REG  if sbpf_version.move_memory_instruction_classes() => store = true,
300                ebpf::DIV64_IMM  if !sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
301                ebpf::ST_2B_IMM  if sbpf_version.move_memory_instruction_classes() => store = true,
302                ebpf::DIV64_REG  if !sbpf_version.enable_pqr() => {},
303                ebpf::ST_2B_REG  if sbpf_version.move_memory_instruction_classes() => store = true,
304                ebpf::OR64_IMM   => {},
305                ebpf::OR64_REG   => {},
306                ebpf::AND64_IMM  => {},
307                ebpf::AND64_REG  => {},
308                ebpf::LSH64_IMM  => { check_imm_shift(&insn, insn_ptr, 64)?; },
309                ebpf::LSH64_REG  => {},
310                ebpf::RSH64_IMM  => { check_imm_shift(&insn, insn_ptr, 64)?; },
311                ebpf::RSH64_REG  => {},
312                ebpf::ST_4B_IMM  if sbpf_version.move_memory_instruction_classes() => store = true,
313                ebpf::NEG64      if !sbpf_version.disable_neg() => {},
314                ebpf::ST_4B_REG  if sbpf_version.move_memory_instruction_classes() => store = true,
315                ebpf::MOD64_IMM  if !sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
316                ebpf::ST_8B_IMM  if sbpf_version.move_memory_instruction_classes() => store = true,
317                ebpf::MOD64_REG  if !sbpf_version.enable_pqr() => {},
318                ebpf::ST_8B_REG  if sbpf_version.move_memory_instruction_classes() => store = true,
319                ebpf::XOR64_IMM  => {},
320                ebpf::XOR64_REG  => {},
321                ebpf::MOV64_IMM  => {},
322                ebpf::MOV64_REG  => {},
323                ebpf::ARSH64_IMM => { check_imm_shift(&insn, insn_ptr, 64)?; },
324                ebpf::ARSH64_REG => {},
325                ebpf::HOR64_IMM  if sbpf_version.disable_lddw() => {},
326
327                // BPF_PQR class
328                ebpf::LMUL32_IMM if sbpf_version.enable_pqr() => {},
329                ebpf::LMUL32_REG if sbpf_version.enable_pqr() => {},
330                ebpf::LMUL64_IMM if sbpf_version.enable_pqr() => {},
331                ebpf::LMUL64_REG if sbpf_version.enable_pqr() => {},
332                ebpf::UHMUL64_IMM if sbpf_version.enable_pqr() => {},
333                ebpf::UHMUL64_REG if sbpf_version.enable_pqr() => {},
334                ebpf::SHMUL64_IMM if sbpf_version.enable_pqr() => {},
335                ebpf::SHMUL64_REG if sbpf_version.enable_pqr() => {},
336                ebpf::UDIV32_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
337                ebpf::UDIV32_REG if sbpf_version.enable_pqr() => {},
338                ebpf::UDIV64_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
339                ebpf::UDIV64_REG if sbpf_version.enable_pqr() => {},
340                ebpf::UREM32_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
341                ebpf::UREM32_REG if sbpf_version.enable_pqr() => {},
342                ebpf::UREM64_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
343                ebpf::UREM64_REG if sbpf_version.enable_pqr() => {},
344                ebpf::SDIV32_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
345                ebpf::SDIV32_REG if sbpf_version.enable_pqr() => {},
346                ebpf::SDIV64_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
347                ebpf::SDIV64_REG if sbpf_version.enable_pqr() => {},
348                ebpf::SREM32_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
349                ebpf::SREM32_REG if sbpf_version.enable_pqr() => {},
350                ebpf::SREM64_IMM if sbpf_version.enable_pqr() => { check_imm_nonzero(&insn, insn_ptr)?; },
351                ebpf::SREM64_REG if sbpf_version.enable_pqr() => {},
352
353                // BPF_JMP32 class
354                ebpf::JEQ32_IMM
355                | ebpf::JEQ32_REG
356                | ebpf::JGT32_IMM
357                | ebpf::JGT32_REG
358                | ebpf::JGE32_IMM
359                | ebpf::JGE32_REG
360                | ebpf::JLT32_IMM
361                | ebpf::JLT32_REG
362                | ebpf::JLE32_IMM
363                | ebpf::JLE32_REG
364                | ebpf::JSET32_IMM
365                | ebpf::JSET32_REG
366                | ebpf::JNE32_IMM
367                | ebpf::JNE32_REG
368                | ebpf::JSGT32_IMM
369                | ebpf::JSGT32_REG
370                | ebpf::JSGE32_IMM
371                | ebpf::JSGE32_REG
372                | ebpf::JSLT32_IMM
373                | ebpf::JSLT32_REG
374                | ebpf::JSLE32_IMM
375                | ebpf::JSLE32_REG if sbpf_version.enable_jmp32() => { check_jmp_offset(prog, insn_ptr, &program_range)?; },
376
377                // BPF_JMP64 class
378                ebpf::JA
379                | ebpf::JEQ64_IMM
380                | ebpf::JEQ64_REG
381                | ebpf::JGT64_IMM
382                | ebpf::JGT64_REG
383                | ebpf::JGE64_IMM
384                | ebpf::JGE64_REG
385                | ebpf::JLT64_IMM
386                | ebpf::JLT64_REG
387                | ebpf::JLE64_IMM
388                | ebpf::JLE64_REG
389                | ebpf::JSET64_IMM
390                | ebpf::JSET64_REG
391                | ebpf::JNE64_IMM
392                | ebpf::JNE64_REG
393                | ebpf::JSGT64_IMM
394                | ebpf::JSGT64_REG
395                | ebpf::JSGE64_IMM
396                | ebpf::JSGE64_REG
397                | ebpf::JSLT64_IMM
398                | ebpf::JSLT64_REG
399                | ebpf::JSLE64_IMM
400                | ebpf::JSLE64_REG   => { check_jmp_offset(prog, insn_ptr, &program_range)?; },
401                ebpf::CALL_IMM   => {},
402                ebpf::CALL_REG   => { check_callx_register(&insn, insn_ptr, sbpf_version)?; },
403                ebpf::EXIT       => {},
404
405                _                => {
406                    return Err(VerifierError::UnknownOpCode(insn.opc, insn_ptr));
407                }
408            }
409
410            check_registers(&insn, store, insn_ptr, sbpf_version)?;
411
412            insn_ptr += 1;
413        }
414
415        // insn_ptr should now be equal to number of instructions.
416        if insn_ptr != prog.len() / ebpf::INSN_SIZE {
417            return Err(VerifierError::JumpOutOfCode(insn_ptr, insn_ptr));
418        }
419
420        Ok(())
421    }
422}