1 // SPDX-License-Identifier: (Apache-2.0 OR MIT) 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 8 // This “verifier” performs simple checks when the eBPF program is loaded into the VM (before it is 9 // interpreted or JIT-compiled). It has nothing to do with the much more elaborated verifier inside 10 // Linux kernel. There is no verification regarding the program flow control (should be a Direct 11 // Acyclic Graph) or the consistency for registers usage (the verifier of the kernel assigns types 12 // to the registers and is much stricter). 13 // 14 // On the other hand, rbpf is not expected to run in kernel space. 15 // 16 // Improving the verifier would be nice, but this is not trivial (and Linux kernel is under GPL 17 // license, so we cannot copy it). 18 // 19 // Contrary to the verifier of the Linux kernel, this one does not modify the bytecode at all. 20 21 use alloc::format; 22 23 use crate::{ebpf, Error, ErrorKind}; 24 25 fn reject<S: AsRef<str>>(msg: S) -> Result<(), Error> { 26 let full_msg = format!("[Verifier] Error: {}", msg.as_ref()); 27 Err(Error::new(ErrorKind::Other, full_msg)) 28 } 29 30 fn check_prog_len(prog: &[u8]) -> Result<(), Error> { 31 if prog.len() % ebpf::INSN_SIZE != 0 { 32 reject(format!( 33 "eBPF program length must be a multiple of {:?} octets", 34 ebpf::INSN_SIZE 35 ))?; 36 } 37 if prog.len() > ebpf::PROG_MAX_SIZE { 38 reject(format!( 39 "eBPF program length limited to {:?}, here {:?}", 40 ebpf::PROG_MAX_INSNS, 41 prog.len() / ebpf::INSN_SIZE 42 ))?; 43 } 44 45 if prog.is_empty() { 46 reject("no program set, call set_program() to load one")?; 47 } 48 let last_opc = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1).opc; 49 if last_opc & ebpf::BPF_CLS_MASK != ebpf::BPF_JMP { 50 reject("program does not end with “EXIT” instruction")?; 51 } 52 53 Ok(()) 54 } 55 56 fn check_imm_endian(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), Error> { 57 match insn.imm { 58 16 | 32 | 64 => Ok(()), 59 _ => reject(format!( 60 "unsupported argument for LE/BE (insn #{insn_ptr:?})" 61 )), 62 } 63 } 64 65 fn check_load_dw(prog: &[u8], insn_ptr: usize) -> Result<(), Error> { 66 // We know we can reach next insn since we enforce an EXIT insn at the end of program, while 67 // this function should be called only for LD_DW insn, that cannot be last in program. 68 let next_insn = ebpf::get_insn(prog, insn_ptr + 1); 69 if next_insn.opc != 0 { 70 reject(format!("incomplete LD_DW instruction (insn #{insn_ptr:?})"))?; 71 } 72 73 Ok(()) 74 } 75 76 fn check_jmp_offset(prog: &[u8], insn_ptr: usize) -> Result<(), Error> { 77 let insn = ebpf::get_insn(prog, insn_ptr); 78 if insn.off == -1 { 79 reject(format!("infinite loop (insn #{insn_ptr:?})"))?; 80 } 81 82 let dst_insn_ptr = insn_ptr as isize + 1 + insn.off as isize; 83 if dst_insn_ptr < 0 || dst_insn_ptr as usize >= (prog.len() / ebpf::INSN_SIZE) { 84 reject(format!( 85 "jump out of code to #{dst_insn_ptr:?} (insn #{insn_ptr:?})" 86 ))?; 87 } 88 89 let dst_insn = ebpf::get_insn(prog, dst_insn_ptr as usize); 90 if dst_insn.opc == 0 { 91 reject(format!( 92 "jump to middle of LD_DW at #{dst_insn_ptr:?} (insn #{insn_ptr:?})" 93 ))?; 94 } 95 96 Ok(()) 97 } 98 99 fn check_registers(insn: &ebpf::Insn, store: bool, insn_ptr: usize) -> Result<(), Error> { 100 if insn.src > 10 { 101 reject(format!("invalid source register (insn #{insn_ptr:?})"))?; 102 } 103 104 match (insn.dst, store) { 105 (0..=9, _) | (10, true) => Ok(()), 106 (10, false) => reject(format!( 107 "cannot write into register r10 (insn #{insn_ptr:?})" 108 )), 109 (_, _) => reject(format!("invalid destination register (insn #{insn_ptr:?})")), 110 } 111 } 112 113 pub fn check(prog: &[u8]) -> Result<(), Error> { 114 check_prog_len(prog)?; 115 116 let mut insn_ptr: usize = 0; 117 while insn_ptr * ebpf::INSN_SIZE < prog.len() { 118 let insn = ebpf::get_insn(prog, insn_ptr); 119 let mut store = false; 120 121 match insn.opc { 122 // BPF_LD class 123 ebpf::LD_ABS_B => {} 124 ebpf::LD_ABS_H => {} 125 ebpf::LD_ABS_W => {} 126 ebpf::LD_ABS_DW => {} 127 ebpf::LD_IND_B => {} 128 ebpf::LD_IND_H => {} 129 ebpf::LD_IND_W => {} 130 ebpf::LD_IND_DW => {} 131 132 ebpf::LD_DW_IMM => { 133 store = true; 134 check_load_dw(prog, insn_ptr)?; 135 insn_ptr += 1; 136 } 137 138 // BPF_LDX class 139 ebpf::LD_B_REG => {} 140 ebpf::LD_H_REG => {} 141 ebpf::LD_W_REG => {} 142 ebpf::LD_DW_REG => {} 143 144 // BPF_ST class 145 ebpf::ST_B_IMM => store = true, 146 ebpf::ST_H_IMM => store = true, 147 ebpf::ST_W_IMM => store = true, 148 ebpf::ST_DW_IMM => store = true, 149 150 // BPF_STX class 151 ebpf::ST_B_REG => store = true, 152 ebpf::ST_H_REG => store = true, 153 ebpf::ST_W_REG => store = true, 154 ebpf::ST_DW_REG => store = true, 155 ebpf::ST_W_XADD => { 156 unimplemented!(); 157 } 158 ebpf::ST_DW_XADD => { 159 unimplemented!(); 160 } 161 162 // BPF_ALU class 163 ebpf::ADD32_IMM => {} 164 ebpf::ADD32_REG => {} 165 ebpf::SUB32_IMM => {} 166 ebpf::SUB32_REG => {} 167 ebpf::MUL32_IMM => {} 168 ebpf::MUL32_REG => {} 169 ebpf::DIV32_IMM => {} 170 ebpf::DIV32_REG => {} 171 ebpf::OR32_IMM => {} 172 ebpf::OR32_REG => {} 173 ebpf::AND32_IMM => {} 174 ebpf::AND32_REG => {} 175 ebpf::LSH32_IMM => {} 176 ebpf::LSH32_REG => {} 177 ebpf::RSH32_IMM => {} 178 ebpf::RSH32_REG => {} 179 ebpf::NEG32 => {} 180 ebpf::MOD32_IMM => {} 181 ebpf::MOD32_REG => {} 182 ebpf::XOR32_IMM => {} 183 ebpf::XOR32_REG => {} 184 ebpf::MOV32_IMM => {} 185 ebpf::MOV32_REG => {} 186 ebpf::ARSH32_IMM => {} 187 ebpf::ARSH32_REG => {} 188 ebpf::LE => { 189 check_imm_endian(&insn, insn_ptr)?; 190 } 191 ebpf::BE => { 192 check_imm_endian(&insn, insn_ptr)?; 193 } 194 195 // BPF_ALU64 class 196 ebpf::ADD64_IMM => {} 197 ebpf::ADD64_REG => {} 198 ebpf::SUB64_IMM => {} 199 ebpf::SUB64_REG => {} 200 ebpf::MUL64_IMM => {} 201 ebpf::MUL64_REG => {} 202 ebpf::DIV64_IMM => {} 203 ebpf::DIV64_REG => {} 204 ebpf::OR64_IMM => {} 205 ebpf::OR64_REG => {} 206 ebpf::AND64_IMM => {} 207 ebpf::AND64_REG => {} 208 ebpf::LSH64_IMM => {} 209 ebpf::LSH64_REG => {} 210 ebpf::RSH64_IMM => {} 211 ebpf::RSH64_REG => {} 212 ebpf::NEG64 => {} 213 ebpf::MOD64_IMM => {} 214 ebpf::MOD64_REG => {} 215 ebpf::XOR64_IMM => {} 216 ebpf::XOR64_REG => {} 217 ebpf::MOV64_IMM => {} 218 ebpf::MOV64_REG => {} 219 ebpf::ARSH64_IMM => {} 220 ebpf::ARSH64_REG => {} 221 222 // BPF_JMP class 223 ebpf::JA => { 224 check_jmp_offset(prog, insn_ptr)?; 225 } 226 ebpf::JEQ_IMM => { 227 check_jmp_offset(prog, insn_ptr)?; 228 } 229 ebpf::JEQ_REG => { 230 check_jmp_offset(prog, insn_ptr)?; 231 } 232 ebpf::JGT_IMM => { 233 check_jmp_offset(prog, insn_ptr)?; 234 } 235 ebpf::JGT_REG => { 236 check_jmp_offset(prog, insn_ptr)?; 237 } 238 ebpf::JGE_IMM => { 239 check_jmp_offset(prog, insn_ptr)?; 240 } 241 ebpf::JGE_REG => { 242 check_jmp_offset(prog, insn_ptr)?; 243 } 244 ebpf::JLT_IMM => { 245 check_jmp_offset(prog, insn_ptr)?; 246 } 247 ebpf::JLT_REG => { 248 check_jmp_offset(prog, insn_ptr)?; 249 } 250 ebpf::JLE_IMM => { 251 check_jmp_offset(prog, insn_ptr)?; 252 } 253 ebpf::JLE_REG => { 254 check_jmp_offset(prog, insn_ptr)?; 255 } 256 ebpf::JSET_IMM => { 257 check_jmp_offset(prog, insn_ptr)?; 258 } 259 ebpf::JSET_REG => { 260 check_jmp_offset(prog, insn_ptr)?; 261 } 262 ebpf::JNE_IMM => { 263 check_jmp_offset(prog, insn_ptr)?; 264 } 265 ebpf::JNE_REG => { 266 check_jmp_offset(prog, insn_ptr)?; 267 } 268 ebpf::JSGT_IMM => { 269 check_jmp_offset(prog, insn_ptr)?; 270 } 271 ebpf::JSGT_REG => { 272 check_jmp_offset(prog, insn_ptr)?; 273 } 274 ebpf::JSGE_IMM => { 275 check_jmp_offset(prog, insn_ptr)?; 276 } 277 ebpf::JSGE_REG => { 278 check_jmp_offset(prog, insn_ptr)?; 279 } 280 ebpf::JSLT_IMM => { 281 check_jmp_offset(prog, insn_ptr)?; 282 } 283 ebpf::JSLT_REG => { 284 check_jmp_offset(prog, insn_ptr)?; 285 } 286 ebpf::JSLE_IMM => { 287 check_jmp_offset(prog, insn_ptr)?; 288 } 289 ebpf::JSLE_REG => { 290 check_jmp_offset(prog, insn_ptr)?; 291 } 292 293 // BPF_JMP32 class 294 ebpf::JEQ_IMM32 => { 295 check_jmp_offset(prog, insn_ptr)?; 296 } 297 ebpf::JEQ_REG32 => { 298 check_jmp_offset(prog, insn_ptr)?; 299 } 300 ebpf::JGT_IMM32 => { 301 check_jmp_offset(prog, insn_ptr)?; 302 } 303 ebpf::JGT_REG32 => { 304 check_jmp_offset(prog, insn_ptr)?; 305 } 306 ebpf::JGE_IMM32 => { 307 check_jmp_offset(prog, insn_ptr)?; 308 } 309 ebpf::JGE_REG32 => { 310 check_jmp_offset(prog, insn_ptr)?; 311 } 312 ebpf::JLT_IMM32 => { 313 check_jmp_offset(prog, insn_ptr)?; 314 } 315 ebpf::JLT_REG32 => { 316 check_jmp_offset(prog, insn_ptr)?; 317 } 318 ebpf::JLE_IMM32 => { 319 check_jmp_offset(prog, insn_ptr)?; 320 } 321 ebpf::JLE_REG32 => { 322 check_jmp_offset(prog, insn_ptr)?; 323 } 324 ebpf::JSET_IMM32 => { 325 check_jmp_offset(prog, insn_ptr)?; 326 } 327 ebpf::JSET_REG32 => { 328 check_jmp_offset(prog, insn_ptr)?; 329 } 330 ebpf::JNE_IMM32 => { 331 check_jmp_offset(prog, insn_ptr)?; 332 } 333 ebpf::JNE_REG32 => { 334 check_jmp_offset(prog, insn_ptr)?; 335 } 336 ebpf::JSGT_IMM32 => { 337 check_jmp_offset(prog, insn_ptr)?; 338 } 339 ebpf::JSGT_REG32 => { 340 check_jmp_offset(prog, insn_ptr)?; 341 } 342 ebpf::JSGE_IMM32 => { 343 check_jmp_offset(prog, insn_ptr)?; 344 } 345 ebpf::JSGE_REG32 => { 346 check_jmp_offset(prog, insn_ptr)?; 347 } 348 ebpf::JSLT_IMM32 => { 349 check_jmp_offset(prog, insn_ptr)?; 350 } 351 ebpf::JSLT_REG32 => { 352 check_jmp_offset(prog, insn_ptr)?; 353 } 354 ebpf::JSLE_IMM32 => { 355 check_jmp_offset(prog, insn_ptr)?; 356 } 357 ebpf::JSLE_REG32 => { 358 check_jmp_offset(prog, insn_ptr)?; 359 } 360 361 ebpf::CALL => {} 362 ebpf::TAIL_CALL => { 363 unimplemented!() 364 } 365 ebpf::EXIT => {} 366 367 _ => { 368 reject(format!( 369 "unknown eBPF opcode {:#2x} (insn #{insn_ptr:?})", 370 insn.opc 371 ))?; 372 } 373 } 374 375 check_registers(&insn, store, insn_ptr)?; 376 377 insn_ptr += 1; 378 } 379 380 // insn_ptr should now be equal to number of instructions. 381 if insn_ptr != prog.len() / ebpf::INSN_SIZE { 382 reject(format!("jumped out of code to #{insn_ptr:?}"))?; 383 } 384 385 Ok(()) 386 } 387