1use linux_uapi::sock_filter;
6use std::collections::HashMap;
7
8use crate::EbpfError;
9use crate::EbpfError::*;
10use crate::api::{
11 BPF_A, BPF_ABS, BPF_ADD, BPF_ALU, BPF_ALU64, BPF_AND, BPF_B, BPF_DIV, BPF_EXIT, BPF_H, BPF_IMM,
12 BPF_IND, BPF_JA, BPF_JEQ, BPF_JGE, BPF_JGT, BPF_JLE, BPF_JLT, BPF_JMP, BPF_JMP32, BPF_JNE,
13 BPF_JSET, BPF_K, BPF_LD, BPF_LDX, BPF_LEN, BPF_LSH, BPF_MEM, BPF_MISC, BPF_MOD, BPF_MOV,
14 BPF_MSH, BPF_MUL, BPF_NEG, BPF_OR, BPF_RET, BPF_RSH, BPF_ST, BPF_STX, BPF_SUB, BPF_TAX,
15 BPF_TXA, BPF_W, BPF_X, BPF_XOR, EbpfInstruction,
16};
17use crate::program::{
18 BpfProgramContext, EbpfProgram, ProgramArgument, StaticHelperSet, link_program,
19};
20use crate::verifier::{
21 CallingContext, NullVerifierLogger, Type, VerifiedEbpfProgram, verify_program,
22};
23
24const CBPF_WORD_SIZE: u32 = 4;
25
26const CBPF_SCRATCH_SIZE: u32 = 16;
28
29pub enum CbpfLenInstruction {
30 Static { len: i32 },
31 ContextField { offset: i16 },
32}
33
34pub struct CbpfConfig {
35 pub len: CbpfLenInstruction,
36 pub allow_msh: bool,
37}
38
39pub fn bpf_class(filter: &sock_filter) -> u8 {
48 (filter.code & 0x07) as u8
49}
50
51pub fn bpf_size(filter: &sock_filter) -> u8 {
55 (filter.code & 0x18) as u8
56}
57
58pub fn bpf_addressing_mode(filter: &sock_filter) -> u8 {
62 (filter.code & 0xe0) as u8
63}
64
65fn bpf_op(filter: &sock_filter) -> u8 {
69 (filter.code & 0xf0) as u8
70}
71
72fn bpf_src(filter: &sock_filter) -> u8 {
74 (filter.code & 0x08) as u8
75}
76
77fn bpf_rval(filter: &sock_filter) -> u8 {
79 (filter.code & 0x18) as u8
80}
81
82fn cbpf_scratch_offset(addr: u32) -> Result<i16, EbpfError> {
84 if addr < CBPF_SCRATCH_SIZE {
85 Ok((-(CBPF_SCRATCH_SIZE as i16) + addr as i16) * CBPF_WORD_SIZE as i16)
86 } else {
87 Err(EbpfError::InvalidCbpfScratchOffset(addr))
88 }
89}
90
91fn cbpf_to_ebpf(
97 bpf_code: &[sock_filter],
98 config: &CbpfConfig,
99) -> Result<Vec<EbpfInstruction>, EbpfError> {
100 const REG_A: u8 = 0;
106
107 const REG_ARG1: u8 = 1;
109
110 const REG_CONTEXT: u8 = 6;
114
115 const REG_TMP: u8 = 7;
117
118 const REG_X: u8 = 9;
120
121 const REG_STACK: u8 = 10;
123
124 let mut to_be_patched: HashMap<usize, Vec<usize>> = HashMap::new();
128
129 let mut ebpf_code: Vec<EbpfInstruction> = vec![];
130 ebpf_code.reserve(bpf_code.len() * 2 + 3);
131
132 ebpf_code.push(EbpfInstruction::new(BPF_ALU64 | BPF_MOV | BPF_X, REG_CONTEXT, REG_ARG1, 0, 0));
134
135 ebpf_code.push(EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, REG_A, 0, 0, 0));
137 ebpf_code.push(EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, REG_X, 0, 0, 0));
139
140 for (i, bpf_instruction) in bpf_code.iter().enumerate() {
141 if let Some((_, entries)) = to_be_patched.remove_entry(&i) {
143 for index in entries {
144 let offset = (ebpf_code.len() - index - 1) as i16;
145 ebpf_code[index].set_offset(offset);
146 }
147 }
148
149 let mut prep_patch = |cbpf_offset: usize, ebpf_source: usize| -> Result<(), EbpfError> {
151 let cbpf_target = i + 1 + cbpf_offset;
152 if cbpf_target >= bpf_code.len() {
153 return Err(EbpfError::InvalidCbpfJumpOffset(cbpf_offset as u32));
154 }
155 to_be_patched.entry(cbpf_target).or_insert_with(Vec::new).push(ebpf_source);
156 Ok(())
157 };
158
159 match bpf_class(bpf_instruction) {
160 BPF_ALU => match bpf_op(bpf_instruction) {
161 op @ (BPF_ADD | BPF_SUB | BPF_MUL | BPF_DIV | BPF_MOD | BPF_AND | BPF_OR
162 | BPF_XOR | BPF_LSH | BPF_RSH) => {
163 if bpf_src(bpf_instruction) == BPF_K {
164 if (op == BPF_DIV || op == BPF_MOD) && bpf_instruction.k == 0 {
166 return Err(EbpfError::ProgramVerifyError("Division by 0".to_string()));
167 }
168 if (op == BPF_LSH || op == BPF_RSH) && bpf_instruction.k >= 32 {
170 return Err(EbpfError::ProgramVerifyError(
171 "Shift by 32 or more".to_string(),
172 ));
173 }
174 ebpf_code.push(EbpfInstruction::new(
175 bpf_instruction.code as u8,
176 REG_A,
177 0,
178 0,
179 bpf_instruction.k as i32,
180 ));
181 } else {
182 if op == BPF_DIV || op == BPF_MOD {
184 ebpf_code.push(EbpfInstruction::new(
186 BPF_JMP32 | BPF_JNE | BPF_K,
187 REG_X,
188 0,
189 2,
190 0,
191 ));
192 ebpf_code.push(EbpfInstruction::new(
194 BPF_ALU | BPF_MOV | BPF_IMM,
195 REG_A,
196 0,
197 0,
198 0,
199 ));
200 ebpf_code.push(EbpfInstruction::new(BPF_JMP | BPF_EXIT, 0, 0, 0, 0));
201 }
202
203 ebpf_code.push(EbpfInstruction::new(
204 bpf_instruction.code as u8,
205 REG_A,
206 REG_X,
207 0,
208 0,
209 ));
210 };
211 }
212 BPF_NEG => {
213 ebpf_code.push(EbpfInstruction::new(BPF_ALU | BPF_NEG, REG_A, REG_A, 0, 0));
214 }
215 _ => return Err(InvalidCbpfInstruction(bpf_instruction.code)),
216 },
217 class @ (BPF_LD | BPF_LDX) => {
218 let dst_reg = if class == BPF_LDX { REG_X } else { REG_A };
219
220 let mode = bpf_addressing_mode(bpf_instruction);
221 let size = bpf_size(bpf_instruction);
222
223 match (size, mode, class) {
228 (BPF_H | BPF_B | BPF_W, BPF_ABS | BPF_IND, BPF_LD) => (),
229 (BPF_W, BPF_LEN | BPF_IMM | BPF_MEM, BPF_LD | BPF_LDX) => (),
230 (BPF_B, BPF_MSH, BPF_LDX) if config.allow_msh => (),
231 _ => return Err(InvalidCbpfInstruction(bpf_instruction.code)),
232 };
233
234 let k = bpf_instruction.k;
235
236 match mode {
237 BPF_ABS => {
238 ebpf_code.push(EbpfInstruction::new(
239 BPF_LD | BPF_ABS | size,
240 REG_A,
241 0,
242 0,
243 k as i32,
244 ));
245 }
246 BPF_IND => {
247 ebpf_code.push(EbpfInstruction::new(
248 BPF_LD | BPF_IND | size,
249 REG_A,
250 REG_X,
251 0,
252 k as i32,
253 ));
254 }
255 BPF_IMM => {
256 let imm = k as i32;
257 ebpf_code.push(EbpfInstruction::new(
258 BPF_ALU | BPF_MOV | BPF_K,
259 dst_reg,
260 0,
261 0,
262 imm,
263 ));
264 }
265 BPF_MEM => {
266 let offset = cbpf_scratch_offset(k)?;
268 ebpf_code.push(EbpfInstruction::new(
269 BPF_LDX | BPF_MEM,
270 dst_reg,
271 REG_STACK,
272 offset,
273 0,
274 ));
275 }
276 BPF_LEN => {
277 ebpf_code.push(match config.len {
278 CbpfLenInstruction::Static { len } => {
279 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, dst_reg, 0, 0, len)
280 }
281 CbpfLenInstruction::ContextField { offset } => EbpfInstruction::new(
282 BPF_LDX | BPF_MEM | BPF_W,
283 dst_reg,
284 REG_CONTEXT,
285 offset,
286 0,
287 ),
288 });
289 }
290 BPF_MSH => {
291 ebpf_code.extend_from_slice(&[
293 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_X, REG_TMP, REG_A, 0, 0),
295 EbpfInstruction::new(BPF_LD | BPF_ABS | BPF_B, REG_A, 0, 0, k as i32),
297 EbpfInstruction::new(BPF_ALU | BPF_AND | BPF_K, REG_A, 0, 0, 0x0f),
299 EbpfInstruction::new(BPF_ALU | BPF_MUL | BPF_K, REG_A, 0, 0, 4),
301 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_X, REG_X, REG_A, 0, 0),
303 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_X, REG_A, REG_TMP, 0, 0),
305 ]);
306 }
307 _ => return Err(InvalidCbpfInstruction(bpf_instruction.code)),
308 }
309 }
310 BPF_JMP => {
311 match bpf_op(bpf_instruction) {
312 BPF_JA => {
313 ebpf_code.push(EbpfInstruction::new(BPF_JMP | BPF_JA, 0, 0, -1, 0));
314 prep_patch(bpf_instruction.k as usize, ebpf_code.len() - 1)?;
315 }
316 op @ (BPF_JGT | BPF_JGE | BPF_JEQ | BPF_JSET) => {
317 let src = bpf_src(bpf_instruction);
324 let sock_filter { k, jt, jf, .. } = *bpf_instruction;
325 let (src_reg, imm) = if src == BPF_K { (0, k as i32) } else { (REG_X, 0) };
326
327 if jt == 0 && op != BPF_JSET {
332 let op = match op {
333 BPF_JGT => BPF_JLE,
334 BPF_JGE => BPF_JLT,
335 BPF_JEQ => BPF_JNE,
336 _ => panic!("Unexpected operation: {op:?}"),
337 };
338
339 ebpf_code.push(EbpfInstruction::new(
340 BPF_JMP32 | op | src,
341 REG_A,
342 src_reg,
343 -1,
344 imm,
345 ));
346 prep_patch(jf as usize, ebpf_code.len() - 1)?;
347 } else {
348 ebpf_code.push(EbpfInstruction::new(
350 BPF_JMP32 | op | src,
351 REG_A,
352 src_reg,
353 -1,
354 imm,
355 ));
356 prep_patch(jt as usize, ebpf_code.len() - 1)?;
357
358 if jf > 0 {
360 ebpf_code.push(EbpfInstruction::new(BPF_JMP | BPF_JA, 0, 0, -1, 0));
361 prep_patch(jf as usize, ebpf_code.len() - 1)?;
362 }
363 }
364 }
365 _ => return Err(InvalidCbpfInstruction(bpf_instruction.code)),
366 }
367 }
368 BPF_MISC => match bpf_op(bpf_instruction) {
369 BPF_TAX => {
370 ebpf_code.push(EbpfInstruction::new(
371 BPF_ALU | BPF_MOV | BPF_X,
372 REG_X,
373 REG_A,
374 0,
375 0,
376 ));
377 }
378 BPF_TXA => {
379 ebpf_code.push(EbpfInstruction::new(
380 BPF_ALU | BPF_MOV | BPF_X,
381 REG_A,
382 REG_X,
383 0,
384 0,
385 ));
386 }
387 _ => return Err(InvalidCbpfInstruction(bpf_instruction.code)),
388 },
389
390 class @ (BPF_ST | BPF_STX) => {
391 if bpf_addressing_mode(bpf_instruction) != 0 || bpf_size(bpf_instruction) != 0 {
392 return Err(InvalidCbpfInstruction(bpf_instruction.code));
393 }
394
395 let src_reg = if class == BPF_STX { REG_X } else { REG_A };
397 let offset = cbpf_scratch_offset(bpf_instruction.k)?;
398 ebpf_code.push(EbpfInstruction::new(
399 BPF_STX | BPF_MEM | BPF_W,
400 REG_STACK,
401 src_reg,
402 offset,
403 0,
404 ));
405 }
406 BPF_RET => {
407 match bpf_rval(bpf_instruction) {
408 BPF_K => {
409 let imm = bpf_instruction.k as i32;
412 ebpf_code.push(EbpfInstruction::new(
413 BPF_ALU | BPF_MOV | BPF_IMM,
414 REG_A,
415 0,
416 0,
417 imm,
418 ));
419 }
420 BPF_A => (),
421 _ => return Err(InvalidCbpfInstruction(bpf_instruction.code)),
422 };
423
424 ebpf_code.push(EbpfInstruction::new(BPF_JMP | BPF_EXIT, 0, 0, 0, 0));
425 }
426 _ => return Err(InvalidCbpfInstruction(bpf_instruction.code)),
427 }
428 }
429
430 assert!(to_be_patched.is_empty());
431
432 Ok(ebpf_code)
433}
434
435pub fn convert_and_verify_cbpf(
438 bpf_code: &[sock_filter],
439 packet_type: Type,
440 config: &CbpfConfig,
441) -> Result<VerifiedEbpfProgram, EbpfError> {
442 let context = CallingContext {
443 maps: vec![],
444 helpers: HashMap::new(),
445 args: vec![packet_type.clone()],
446 packet_type: Some(packet_type),
447 };
448 let ebpf_code = cbpf_to_ebpf(bpf_code, config)?;
449 verify_program(ebpf_code, context, &mut NullVerifierLogger)
450}
451
452pub fn convert_and_link_cbpf<C: BpfProgramContext + StaticHelperSet>(
454 bpf_code: &[sock_filter],
455) -> Result<EbpfProgram<C>, EbpfError> {
456 let verified = convert_and_verify_cbpf(
457 bpf_code,
458 <C as BpfProgramContext>::Packet::get_type().clone(),
459 C::CBPF_CONFIG,
460 )?;
461 link_program(&verified, vec![])
462}
463
464#[cfg(test)]
465mod tests {
466 use super::*;
467 use crate::{MemoryId, NoMap, empty_static_helper_set};
468 use linux_uapi::{
469 AUDIT_ARCH_AARCH64, AUDIT_ARCH_X86_64, SECCOMP_RET_ALLOW, SECCOMP_RET_TRAP, seccomp_data,
470 sock_filter,
471 };
472 use std::mem::offset_of;
473 use std::sync::LazyLock;
474 use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
475
476 pub const TEST_CBPF_CONFIG: CbpfConfig = CbpfConfig {
477 len: CbpfLenInstruction::Static { len: size_of::<seccomp_data>() as i32 },
478 allow_msh: true,
479 };
480
481 #[test]
482 fn test_cbpf_to_ebpf() {
483 assert_eq!(
485 cbpf_to_ebpf(
486 &vec![
487 sock_filter { code: (BPF_JMP | BPF_JA) as u16, jt: 0, jf: 0, k: 0 },
488 sock_filter { code: (BPF_RET | BPF_A) as u16, jt: 0, jf: 0, k: 0 },
489 ],
490 &TEST_CBPF_CONFIG
491 ),
492 Ok(vec![
493 EbpfInstruction::new(BPF_ALU64 | BPF_MOV | BPF_X, 6, 1, 0, 0),
494 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, 0, 0, 0, 0),
495 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, 9, 0, 0, 0),
496 EbpfInstruction::new(BPF_JMP | BPF_JA, 0, 0, 0, 0),
497 EbpfInstruction::new(BPF_JMP | BPF_EXIT, 0, 0, 0, 0),
498 ]),
499 );
500
501 assert_eq!(
503 cbpf_to_ebpf(
504 &vec![
505 sock_filter { code: (BPF_JMP | BPF_JA) as u16, jt: 0, jf: 0, k: 1 },
506 sock_filter { code: (BPF_RET | BPF_A) as u16, jt: 0, jf: 0, k: 0 },
507 ],
508 &TEST_CBPF_CONFIG
509 ),
510 Err(EbpfError::InvalidCbpfJumpOffset(1)),
511 );
512
513 assert_eq!(
515 cbpf_to_ebpf(
516 &vec![sock_filter { code: (BPF_JMP | BPF_JA) as u16, jt: 0, jf: 0, k: 0xffffffff }],
517 &TEST_CBPF_CONFIG
518 ),
519 Err(EbpfError::InvalidCbpfJumpOffset(0xffffffff)),
520 );
521
522 assert_eq!(
524 cbpf_to_ebpf(
525 &vec![
526 sock_filter { code: (BPF_JMP | BPF_JNE) as u16, jt: 0, jf: 0, k: 0 },
527 sock_filter { code: (BPF_RET | BPF_A) as u16, jt: 0, jf: 0, k: 0 },
528 ],
529 &TEST_CBPF_CONFIG
530 ),
531 Err(EbpfError::InvalidCbpfInstruction((BPF_JMP | BPF_JNE) as u16)),
532 );
533
534 assert_eq!(
536 cbpf_to_ebpf(
537 &vec![
538 sock_filter { code: (BPF_JMP | BPF_JEQ) as u16, jt: 1, jf: 0, k: 0 },
539 sock_filter { code: (BPF_RET | BPF_A) as u16, jt: 0, jf: 0, k: 0 },
540 sock_filter { code: (BPF_RET | BPF_A) as u16, jt: 0, jf: 0, k: 0 },
541 ],
542 &TEST_CBPF_CONFIG
543 ),
544 Ok(vec![
545 EbpfInstruction::new(BPF_ALU64 | BPF_MOV | BPF_X, 6, 1, 0, 0),
546 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, 0, 0, 0, 0),
547 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, 9, 0, 0, 0),
548 EbpfInstruction::new(BPF_JMP32 | BPF_JEQ, 0, 0, 1, 0),
549 EbpfInstruction::new(BPF_JMP | BPF_EXIT, 0, 0, 0, 0),
550 EbpfInstruction::new(BPF_JMP | BPF_EXIT, 0, 0, 0, 0),
551 ]),
552 );
553
554 assert_eq!(
556 cbpf_to_ebpf(
557 &vec![
558 sock_filter { code: (BPF_JMP | BPF_JA) as u16, jt: 0, jf: 0, k: 0 },
559 sock_filter { code: (BPF_RET | BPF_K) as u16, jt: 0, jf: 0, k: 1 },
560 ],
561 &TEST_CBPF_CONFIG
562 ),
563 Ok(vec![
564 EbpfInstruction::new(BPF_ALU64 | BPF_MOV | BPF_X, 6, 1, 0, 0),
565 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, 0, 0, 0, 0),
566 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, 9, 0, 0, 0),
567 EbpfInstruction::new(BPF_JMP | BPF_JA, 0, 0, 0, 0),
568 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_IMM, 0, 0, 0, 1),
569 EbpfInstruction::new(BPF_JMP | BPF_EXIT, 0, 0, 0, 0),
570 ]),
571 );
572
573 assert_eq!(
575 cbpf_to_ebpf(
576 &vec![
577 sock_filter { code: (BPF_LD | BPF_MEM) as u16, jt: 0, jf: 0, k: 0 },
578 sock_filter { code: (BPF_LDX | BPF_MEM) as u16, jt: 0, jf: 0, k: 15 },
579 sock_filter { code: BPF_ST as u16, jt: 0, jf: 0, k: 0 },
580 sock_filter { code: BPF_STX as u16, jt: 0, jf: 0, k: 15 },
581 ],
582 &TEST_CBPF_CONFIG
583 ),
584 Ok(vec![
585 EbpfInstruction::new(BPF_ALU64 | BPF_MOV | BPF_X, 6, 1, 0, 0),
586 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, 0, 0, 0, 0),
587 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, 9, 0, 0, 0),
588 EbpfInstruction::new(BPF_LDX | BPF_MEM | BPF_W, 0, 10, -64, 0),
589 EbpfInstruction::new(BPF_LDX | BPF_MEM | BPF_W, 9, 10, -4, 0),
590 EbpfInstruction::new(BPF_STX | BPF_MEM | BPF_W, 10, 0, -64, 0),
591 EbpfInstruction::new(BPF_STX | BPF_MEM | BPF_W, 10, 9, -4, 0),
592 ]),
593 );
594
595 assert_eq!(
597 cbpf_to_ebpf(
598 &vec![sock_filter { code: (BPF_LD | BPF_MEM) as u16, jt: 0, jf: 0, k: 17 }],
599 &TEST_CBPF_CONFIG
600 ),
601 Err(EbpfError::InvalidCbpfScratchOffset(17)),
602 );
603 }
604
605 const BPF_ALU_ADD_K: u16 = (BPF_ALU | BPF_ADD | BPF_K) as u16;
606 const BPF_ALU_SUB_K: u16 = (BPF_ALU | BPF_SUB | BPF_K) as u16;
607 const BPF_ALU_MUL_K: u16 = (BPF_ALU | BPF_MUL | BPF_K) as u16;
608 const BPF_ALU_DIV_K: u16 = (BPF_ALU | BPF_DIV | BPF_K) as u16;
609 const BPF_ALU_AND_K: u16 = (BPF_ALU | BPF_AND | BPF_K) as u16;
610 const BPF_ALU_OR_K: u16 = (BPF_ALU | BPF_OR | BPF_K) as u16;
611 const BPF_ALU_XOR_K: u16 = (BPF_ALU | BPF_XOR | BPF_K) as u16;
612 const BPF_ALU_LSH_K: u16 = (BPF_ALU | BPF_LSH | BPF_K) as u16;
613 const BPF_ALU_RSH_K: u16 = (BPF_ALU | BPF_RSH | BPF_K) as u16;
614
615 const BPF_ALU_OR_X: u16 = (BPF_ALU | BPF_OR | BPF_X) as u16;
616
617 const BPF_LD_W_ABS: u16 = (BPF_LD | BPF_ABS | BPF_W) as u16;
618 const BPF_LD_W_MEM: u16 = (BPF_LD | BPF_MEM | BPF_W) as u16;
619 const BPF_JEQ_K: u16 = (BPF_JMP | BPF_JEQ | BPF_K) as u16;
620 const BPF_JSET_K: u16 = (BPF_JMP | BPF_JSET | BPF_K) as u16;
621 const BPF_RET_K: u16 = (BPF_RET | BPF_K) as u16;
622 const BPF_RET_A: u16 = (BPF_RET | BPF_A) as u16;
623 const BPF_ST_REG: u16 = BPF_ST as u16;
624 const BPF_MISC_TAX: u16 = (BPF_MISC | BPF_TAX) as u16;
625
626 struct TestProgramContext {}
627
628 impl BpfProgramContext for TestProgramContext {
629 type RunContext<'a> = ();
630 type Packet<'a> = &'a seccomp_data;
631 type Map = NoMap;
632 const CBPF_CONFIG: &'static CbpfConfig = &TEST_CBPF_CONFIG;
633 }
634
635 empty_static_helper_set!(TestProgramContext);
636
637 static SECCOMP_DATA_TYPE: LazyLock<Type> = LazyLock::new(|| Type::PtrToMemory {
638 id: MemoryId::new(),
639 offset: 0.into(),
640 buffer_size: 0,
641 });
642
643 impl ProgramArgument for &'_ seccomp_data {
644 fn get_type() -> &'static Type {
645 &*SECCOMP_DATA_TYPE
646 }
647 }
648
649 fn with_prg_assert_result(
650 prg: &EbpfProgram<TestProgramContext>,
651 mut data: seccomp_data,
652 result: u32,
653 msg: &str,
654 ) {
655 let return_value = prg.run(&mut (), &mut data);
656 assert_eq!(return_value, result as u64, "{}: filter return value is {}", msg, return_value);
657 }
658
659 #[test]
660 fn test_filter_with_dw_load() {
661 let test_prg = [
662 sock_filter { code: BPF_LD_W_ABS, jt: 0, jf: 0, k: 4 },
664 sock_filter { code: BPF_JEQ_K, jt: 1, jf: 0, k: AUDIT_ARCH_X86_64 },
665 sock_filter { code: BPF_RET_K, jt: 0, jf: 0, k: 1 },
667 sock_filter { code: BPF_LD_W_ABS, jt: 0, jf: 0, k: 0 },
669 sock_filter { code: BPF_JEQ_K, jt: 0, jf: 1, k: 41 },
671 sock_filter { code: BPF_RET_K, jt: 0, jf: 0, k: SECCOMP_RET_ALLOW },
672 sock_filter { code: BPF_JEQ_K, jt: 0, jf: 1, k: 115 },
674 sock_filter { code: BPF_RET_K, jt: 0, jf: 0, k: SECCOMP_RET_TRAP },
675 sock_filter { code: BPF_LD_W_ABS, jt: 0, jf: 0, k: 16 },
680 sock_filter { code: BPF_ST_REG, jt: 0, jf: 0, k: 0 },
681 sock_filter { code: BPF_LD_W_ABS, jt: 0, jf: 0, k: 20 },
683 sock_filter { code: BPF_ST_REG, jt: 0, jf: 0, k: 1 },
684 sock_filter { code: BPF_LD_W_MEM, jt: 0, jf: 0, k: 0 },
687 sock_filter { code: BPF_JSET_K, jt: 2, jf: 0, k: 4294967295 },
688 sock_filter { code: BPF_LD_W_MEM, jt: 0, jf: 0, k: 1 },
689 sock_filter { code: BPF_JSET_K, jt: 0, jf: 1, k: 4294967292 },
690 sock_filter { code: BPF_RET_K, jt: 0, jf: 0, k: SECCOMP_RET_TRAP },
691 sock_filter { code: BPF_RET_K, jt: 0, jf: 0, k: SECCOMP_RET_ALLOW },
692 ];
693
694 let prg =
695 convert_and_link_cbpf::<TestProgramContext>(&test_prg).expect("Error parsing program");
696
697 with_prg_assert_result(
698 &prg,
699 seccomp_data { arch: AUDIT_ARCH_AARCH64, ..Default::default() },
700 1,
701 "Did not reject incorrect arch",
702 );
703
704 with_prg_assert_result(
705 &prg,
706 seccomp_data { arch: AUDIT_ARCH_X86_64, nr: 41, ..Default::default() },
707 SECCOMP_RET_ALLOW,
708 "Did not pass simple RET_ALLOW",
709 );
710
711 with_prg_assert_result(
712 &prg,
713 seccomp_data {
714 arch: AUDIT_ARCH_X86_64,
715 nr: 100,
716 args: [0xFF00000000, 0, 0, 0, 0, 0],
717 ..Default::default()
718 },
719 SECCOMP_RET_TRAP,
720 "Did not treat load of first 32 bits correctly",
721 );
722
723 with_prg_assert_result(
724 &prg,
725 seccomp_data {
726 arch: AUDIT_ARCH_X86_64,
727 nr: 100,
728 args: [0x4, 0, 0, 0, 0, 0],
729 ..Default::default()
730 },
731 SECCOMP_RET_TRAP,
732 "Did not correctly reject load of second 32 bits",
733 );
734
735 with_prg_assert_result(
736 &prg,
737 seccomp_data {
738 arch: AUDIT_ARCH_X86_64,
739 nr: 100,
740 args: [0x0, 0, 0, 0, 0, 0],
741 ..Default::default()
742 },
743 SECCOMP_RET_ALLOW,
744 "Did not correctly accept load of second 32 bits",
745 );
746 }
747
748 #[test]
749 fn test_alu_insns() {
750 {
751 let test_prg = [
752 sock_filter { code: BPF_LD_W_ABS, jt: 0, jf: 0, k: 0 }, sock_filter { code: BPF_ALU_ADD_K, jt: 0, jf: 0, k: 3 }, sock_filter { code: BPF_ALU_SUB_K, jt: 0, jf: 0, k: 2 }, sock_filter { code: BPF_MISC_TAX, jt: 0, jf: 0, k: 0 }, sock_filter { code: BPF_ALU_MUL_K, jt: 0, jf: 0, k: 8 }, sock_filter { code: BPF_ALU_DIV_K, jt: 0, jf: 0, k: 2 }, sock_filter { code: BPF_ALU_AND_K, jt: 0, jf: 0, k: 15 }, sock_filter { code: BPF_ALU_OR_K, jt: 0, jf: 0, k: 16 }, sock_filter { code: BPF_ALU_XOR_K, jt: 0, jf: 0, k: 7 }, sock_filter { code: BPF_ALU_LSH_K, jt: 0, jf: 0, k: 2 }, sock_filter { code: BPF_ALU_OR_X, jt: 0, jf: 0, k: 1 }, sock_filter { code: BPF_ALU_RSH_K, jt: 0, jf: 0, k: 1 }, sock_filter { code: BPF_RET_A, jt: 0, jf: 0, k: 0 },
767 ];
768
769 let prg = convert_and_link_cbpf::<TestProgramContext>(&test_prg)
770 .expect("Error parsing program");
771
772 with_prg_assert_result(
773 &prg,
774 seccomp_data { nr: 1, ..Default::default() },
775 63,
776 "BPF math does not work",
777 );
778
779 with_prg_assert_result(
780 &prg,
781 seccomp_data { nr: 11, ..Default::default() },
782 46,
783 "BPF math does not work",
784 );
785 }
786
787 {
788 let test_prg = [
790 sock_filter { code: BPF_LD_W_ABS, jt: 0, jf: 0, k: 0 }, sock_filter { code: BPF_ALU_SUB_K, jt: 0, jf: 0, k: 2 }, sock_filter { code: BPF_RET_A, jt: 0, jf: 0, k: 0 },
794 ];
795
796 let prg = convert_and_link_cbpf::<TestProgramContext>(&test_prg)
797 .expect("Error parsing program");
798
799 with_prg_assert_result(
800 &prg,
801 seccomp_data { nr: -1, ..Default::default() },
802 u32::MAX - 2,
803 "BPF math does not work",
804 );
805 }
806 }
807
808 #[test]
810 fn test_ld_msh() {
811 let test_prg = [
812 sock_filter { code: (BPF_LDX | BPF_MSH | BPF_B) as u16, jt: 0, jf: 0, k: 0 },
814 sock_filter { code: (BPF_MISC | BPF_TXA) as u16, jt: 0, jf: 0, k: 0 },
816 sock_filter { code: BPF_RET_A, jt: 0, jf: 0, k: 0 },
818 ];
819
820 let prg =
821 convert_and_link_cbpf::<TestProgramContext>(&test_prg).expect("Error parsing program");
822
823 for i in [0x00, 0x01, 0x07, 0x15, 0xff].iter() {
824 with_prg_assert_result(
825 &prg,
826 seccomp_data { nr: *i, ..Default::default() },
827 4 * (*i & 0xf) as u32,
828 "BPF math does not work",
829 )
830 }
831 }
832
833 #[test]
834 fn test_static_packet_len() {
835 let test_prg = [
836 sock_filter { code: (BPF_LD | BPF_LEN | BPF_W) as u16, jt: 0, jf: 0, k: 0 },
838 sock_filter { code: BPF_RET_A, jt: 0, jf: 0, k: 0 },
840 ];
841
842 let prg =
843 convert_and_link_cbpf::<TestProgramContext>(&test_prg).expect("Error parsing program");
844
845 let data = seccomp_data::default();
846 assert_eq!(prg.run(&mut (), &data), size_of::<seccomp_data>() as u64);
847 }
848
849 #[repr(C)]
852 #[derive(Debug, Default, IntoBytes, Immutable, KnownLayout, FromBytes)]
853 struct VariableLengthPacket {
854 foo: u32,
855 len: i32,
856 bar: u64,
857 }
858
859 static VARIABLE_LENGTH_PACKET_TYPE: LazyLock<Type> = LazyLock::new(|| Type::PtrToMemory {
860 id: MemoryId::new(),
861 offset: 0.into(),
862 buffer_size: size_of::<VariableLengthPacket>() as u64,
863 });
864
865 impl ProgramArgument for &'_ VariableLengthPacket {
866 fn get_type() -> &'static Type {
867 &*VARIABLE_LENGTH_PACKET_TYPE
868 }
869 }
870
871 pub const VARIABLE_LENGTH_CBPF_CONFIG: CbpfConfig = CbpfConfig {
872 len: CbpfLenInstruction::ContextField {
873 offset: offset_of!(VariableLengthPacket, len) as i16,
874 },
875 allow_msh: true,
876 };
877
878 struct VariableLengthPacketContext {}
879
880 impl BpfProgramContext for VariableLengthPacketContext {
881 type RunContext<'a> = ();
882 type Packet<'a> = &'a VariableLengthPacket;
883 type Map = NoMap;
884 const CBPF_CONFIG: &'static CbpfConfig = &VARIABLE_LENGTH_CBPF_CONFIG;
885 }
886
887 empty_static_helper_set!(VariableLengthPacketContext);
888
889 #[test]
890 fn test_variable_packet_len() {
891 let test_prg = [
892 sock_filter { code: (BPF_LD | BPF_LEN | BPF_W) as u16, jt: 0, jf: 0, k: 0 },
894 sock_filter { code: BPF_RET_A, jt: 0, jf: 0, k: 0 },
896 ];
897
898 let prg = convert_and_link_cbpf::<VariableLengthPacketContext>(&test_prg)
899 .expect("Error parsing program");
900 let data = VariableLengthPacket { len: 42, ..VariableLengthPacket::default() };
901 assert_eq!(prg.run(&mut (), &data), data.len as u64);
902 }
903}