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