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
reject<S: AsRef<str>>(msg: S) -> Result<(), Error>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
check_prog_len(prog: &[u8]) -> Result<(), Error>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
check_imm_endian(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), Error>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
check_load_dw(prog: &[u8], insn_ptr: usize) -> Result<(), Error>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
check_jmp_offset(prog: &[u8], insn_ptr: usize) -> Result<(), Error>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
check_registers(insn: &ebpf::Insn, store: bool, insn_ptr: usize) -> Result<(), Error>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
check(prog: &[u8]) -> Result<(), Error>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