1#![allow(clippy::arithmetic_side_effects)]
2use crate::{
16 ebpf,
17 program::{BuiltinFunction, FunctionRegistry, SBPFVersion},
18 vm::{Config, ContextObject},
19};
20use thiserror::Error;
21
22#[derive(Debug, Error, Eq, PartialEq)]
24pub enum VerifierError {
25 #[error("program length must be a multiple of {} octets", ebpf::INSN_SIZE)]
27 ProgramLengthNotMultiple,
28 #[error("Deprecated")]
30 ProgramTooLarge(usize),
31 #[error("no program set, call prog_set() to load one")]
33 NoProgram,
34 #[error("division by 0 (insn #{0})")]
36 DivisionByZero(usize),
37 #[error("unsupported argument for LE/BE (insn #{0})")]
39 UnsupportedLEBEArgument(usize),
40 #[error("LD_DW instruction cannot be last in program")]
42 LDDWCannotBeLast,
43 #[error("incomplete LD_DW instruction (insn #{0})")]
45 IncompleteLDDW(usize),
46 #[error("infinite loop (insn #{0})")]
48 InfiniteLoop(usize),
49 #[error("jump out of code to #{0} (insn #{1})")]
51 JumpOutOfCode(usize, usize),
52 #[error("jump to middle of LD_DW at #{0} (insn #{1})")]
54 JumpToMiddleOfLDDW(usize, usize),
55 #[error("invalid source register (insn #{0})")]
57 InvalidSourceRegister(usize),
58 #[error("cannot write into register r10 (insn #{0})")]
60 CannotWriteR10(usize),
61 #[error("invalid destination register (insn #{0})")]
63 InvalidDestinationRegister(usize),
64 #[error("unknown eBPF opcode {0:#2x} (insn #{1:?})")]
66 UnknownOpCode(u8, usize),
67 #[error("Shift with overflow of {0}-bit value by {1} (insn #{2:?})")]
69 ShiftWithOverflow(u64, u64, usize),
70 #[error("Invalid register specified at instruction {0}")]
72 InvalidRegister(usize),
73 #[error("Invalid function at instruction {0}")]
75 InvalidFunction(usize),
76 #[error("Invalid syscall code {0}")]
78 InvalidSyscall(u32),
79 #[error("Unaligned immediate (insn #{0})")]
81 UnalignedImmediate(usize),
82}
83
84pub trait Verifier {
86 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 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
208fn 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
219fn 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(®) {
231 return Err(VerifierError::InvalidRegister(insn_ptr));
232 }
233 Ok(())
234}
235
236#[derive(Debug)]
238pub struct RequisiteVerifier {}
239impl Verifier for RequisiteVerifier {
240 #[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 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 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 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 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 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 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 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 if insn_ptr != prog.len() / ebpf::INSN_SIZE {
442 return Err(VerifierError::JumpOutOfCode(insn_ptr, insn_ptr));
443 }
444
445 Ok(())
446 }
447}