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(InvalidCbpfInstruction(bpf_instruction.code));
167 }
168 if (op == BPF_LSH || op == BPF_RSH) && bpf_instruction.k >= 32 {
170 return Err(InvalidCbpfInstruction(bpf_instruction.code));
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 Ok(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, PointerOrigin, 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_ALU | BPF_MOV | BPF_K, 9, 0, 0, 0),
494 EbpfInstruction::new(BPF_JMP | BPF_JA, 0, 0, 0, 0),
495 EbpfInstruction::new(BPF_JMP | BPF_EXIT, 0, 0, 0, 0),
496 ]),
497 );
498
499 assert_eq!(
501 cbpf_to_ebpf(
502 &vec![
503 sock_filter { code: (BPF_JMP | BPF_JA) as u16, jt: 0, jf: 0, k: 1 },
504 sock_filter { code: (BPF_RET | BPF_A) as u16, jt: 0, jf: 0, k: 0 },
505 ],
506 &TEST_CBPF_CONFIG
507 ),
508 Err(EbpfError::InvalidCbpfJumpOffset(1)),
509 );
510
511 assert_eq!(
513 cbpf_to_ebpf(
514 &vec![sock_filter { code: (BPF_JMP | BPF_JA) as u16, jt: 0, jf: 0, k: 0xffffffff }],
515 &TEST_CBPF_CONFIG
516 ),
517 Err(EbpfError::InvalidCbpfJumpOffset(0xffffffff)),
518 );
519
520 assert_eq!(
522 cbpf_to_ebpf(
523 &vec![
524 sock_filter { code: (BPF_JMP | BPF_JNE) as u16, jt: 0, jf: 0, k: 0 },
525 sock_filter { code: (BPF_RET | BPF_A) as u16, jt: 0, jf: 0, k: 0 },
526 ],
527 &TEST_CBPF_CONFIG
528 ),
529 Err(EbpfError::InvalidCbpfInstruction((BPF_JMP | BPF_JNE) as u16)),
530 );
531
532 assert_eq!(
534 cbpf_to_ebpf(
535 &vec![
536 sock_filter { code: (BPF_JMP | BPF_JEQ) as u16, jt: 1, jf: 0, k: 0 },
537 sock_filter { code: (BPF_RET | BPF_A) as u16, jt: 0, jf: 0, k: 0 },
538 sock_filter { code: (BPF_RET | BPF_A) as u16, jt: 0, jf: 0, k: 0 },
539 ],
540 &TEST_CBPF_CONFIG
541 ),
542 Ok(vec![
543 EbpfInstruction::new(BPF_ALU64 | BPF_MOV | BPF_X, 6, 1, 0, 0),
544 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, 0, 0, 0, 0),
545 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, 9, 0, 0, 0),
546 EbpfInstruction::new(BPF_JMP32 | BPF_JEQ, 0, 0, 1, 0),
547 EbpfInstruction::new(BPF_JMP | BPF_EXIT, 0, 0, 0, 0),
548 EbpfInstruction::new(BPF_JMP | BPF_EXIT, 0, 0, 0, 0),
549 ]),
550 );
551
552 assert_eq!(
554 cbpf_to_ebpf(
555 &vec![
556 sock_filter { code: (BPF_JMP | BPF_JA) as u16, jt: 0, jf: 0, k: 0 },
557 sock_filter { code: (BPF_RET | BPF_K) as u16, jt: 0, jf: 0, k: 1 },
558 ],
559 &TEST_CBPF_CONFIG
560 ),
561 Ok(vec![
562 EbpfInstruction::new(BPF_ALU64 | BPF_MOV | BPF_X, 6, 1, 0, 0),
563 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, 0, 0, 0, 0),
564 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, 9, 0, 0, 0),
565 EbpfInstruction::new(BPF_JMP | BPF_JA, 0, 0, 0, 0),
566 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_IMM, 0, 0, 0, 1),
567 EbpfInstruction::new(BPF_JMP | BPF_EXIT, 0, 0, 0, 0),
568 ]),
569 );
570
571 assert_eq!(
573 cbpf_to_ebpf(
574 &vec![
575 sock_filter { code: (BPF_LD | BPF_MEM) as u16, jt: 0, jf: 0, k: 0 },
576 sock_filter { code: (BPF_LDX | BPF_MEM) as u16, jt: 0, jf: 0, k: 15 },
577 sock_filter { code: BPF_ST as u16, jt: 0, jf: 0, k: 0 },
578 sock_filter { code: BPF_STX as u16, jt: 0, jf: 0, k: 15 },
579 ],
580 &TEST_CBPF_CONFIG
581 ),
582 Ok(vec![
583 EbpfInstruction::new(BPF_ALU64 | BPF_MOV | BPF_X, 6, 1, 0, 0),
584 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, 0, 0, 0, 0),
585 EbpfInstruction::new(BPF_ALU | BPF_MOV | BPF_K, 9, 0, 0, 0),
586 EbpfInstruction::new(BPF_LDX | BPF_MEM | BPF_W, 0, 10, -64, 0),
587 EbpfInstruction::new(BPF_LDX | BPF_MEM | BPF_W, 9, 10, -4, 0),
588 EbpfInstruction::new(BPF_STX | BPF_MEM | BPF_W, 10, 0, -64, 0),
589 EbpfInstruction::new(BPF_STX | BPF_MEM | BPF_W, 10, 9, -4, 0),
590 ]),
591 );
592
593 assert_eq!(
595 cbpf_to_ebpf(
596 &vec![sock_filter { code: (BPF_LD | BPF_MEM) as u16, jt: 0, jf: 0, k: 17 }],
597 &TEST_CBPF_CONFIG
598 ),
599 Err(EbpfError::InvalidCbpfScratchOffset(17)),
600 );
601 }
602
603 const BPF_ALU_ADD_K: u16 = (BPF_ALU | BPF_ADD | BPF_K) as u16;
604 const BPF_ALU_SUB_K: u16 = (BPF_ALU | BPF_SUB | BPF_K) as u16;
605 const BPF_ALU_MUL_K: u16 = (BPF_ALU | BPF_MUL | BPF_K) as u16;
606 const BPF_ALU_DIV_K: u16 = (BPF_ALU | BPF_DIV | BPF_K) as u16;
607 const BPF_ALU_AND_K: u16 = (BPF_ALU | BPF_AND | BPF_K) as u16;
608 const BPF_ALU_OR_K: u16 = (BPF_ALU | BPF_OR | BPF_K) as u16;
609 const BPF_ALU_XOR_K: u16 = (BPF_ALU | BPF_XOR | BPF_K) as u16;
610 const BPF_ALU_LSH_K: u16 = (BPF_ALU | BPF_LSH | BPF_K) as u16;
611 const BPF_ALU_RSH_K: u16 = (BPF_ALU | BPF_RSH | BPF_K) as u16;
612
613 const BPF_ALU_OR_X: u16 = (BPF_ALU | BPF_OR | BPF_X) as u16;
614
615 const BPF_LD_W_ABS: u16 = (BPF_LD | BPF_ABS | BPF_W) as u16;
616 const BPF_LD_W_MEM: u16 = (BPF_LD | BPF_MEM | BPF_W) as u16;
617 const BPF_JEQ_K: u16 = (BPF_JMP | BPF_JEQ | BPF_K) as u16;
618 const BPF_JSET_K: u16 = (BPF_JMP | BPF_JSET | BPF_K) as u16;
619 const BPF_RET_K: u16 = (BPF_RET | BPF_K) as u16;
620 const BPF_RET_A: u16 = (BPF_RET | BPF_A) as u16;
621 const BPF_ST_REG: u16 = BPF_ST as u16;
622 const BPF_MISC_TAX: u16 = (BPF_MISC | BPF_TAX) as u16;
623
624 struct TestProgramContext {}
625
626 impl BpfProgramContext for TestProgramContext {
627 type RunContext<'a> = ();
628 type Packet<'a> = &'a seccomp_data;
629 type Map = NoMap;
630 const CBPF_CONFIG: &'static CbpfConfig = &TEST_CBPF_CONFIG;
631 }
632
633 empty_static_helper_set!(TestProgramContext);
634
635 static SECCOMP_DATA_TYPE: LazyLock<Type> = LazyLock::new(|| Type::PtrToMemory {
636 id: MemoryId::new(),
637 offset: 0.into(),
638 buffer_size: 0,
639 origin: PointerOrigin::KernelReadOnly,
640 });
641
642 impl ProgramArgument for &'_ seccomp_data {
643 fn get_type() -> &'static Type {
644 &*SECCOMP_DATA_TYPE
645 }
646 }
647
648 fn with_prg_assert_result(
649 prg: &EbpfProgram<TestProgramContext>,
650 mut data: seccomp_data,
651 result: u32,
652 msg: &str,
653 ) {
654 let return_value = prg.run(&mut (), &mut data);
655 assert_eq!(return_value, result as u64, "{}: filter return value is {}", msg, return_value);
656 }
657
658 #[test]
659 fn test_filter_with_dw_load() {
660 let test_prg = [
661 sock_filter { code: BPF_LD_W_ABS, jt: 0, jf: 0, k: 4 },
663 sock_filter { code: BPF_JEQ_K, jt: 1, jf: 0, k: AUDIT_ARCH_X86_64 },
664 sock_filter { code: BPF_RET_K, jt: 0, jf: 0, k: 1 },
666 sock_filter { code: BPF_LD_W_ABS, jt: 0, jf: 0, k: 0 },
668 sock_filter { code: BPF_JEQ_K, jt: 0, jf: 1, k: 41 },
670 sock_filter { code: BPF_RET_K, jt: 0, jf: 0, k: SECCOMP_RET_ALLOW },
671 sock_filter { code: BPF_JEQ_K, jt: 0, jf: 1, k: 115 },
673 sock_filter { code: BPF_RET_K, jt: 0, jf: 0, k: SECCOMP_RET_TRAP },
674 sock_filter { code: BPF_LD_W_ABS, jt: 0, jf: 0, k: 16 },
679 sock_filter { code: BPF_ST_REG, jt: 0, jf: 0, k: 0 },
680 sock_filter { code: BPF_LD_W_ABS, jt: 0, jf: 0, k: 20 },
682 sock_filter { code: BPF_ST_REG, jt: 0, jf: 0, k: 1 },
683 sock_filter { code: BPF_LD_W_MEM, jt: 0, jf: 0, k: 0 },
686 sock_filter { code: BPF_JSET_K, jt: 2, jf: 0, k: 4294967295 },
687 sock_filter { code: BPF_LD_W_MEM, jt: 0, jf: 0, k: 1 },
688 sock_filter { code: BPF_JSET_K, jt: 0, jf: 1, k: 4294967292 },
689 sock_filter { code: BPF_RET_K, jt: 0, jf: 0, k: SECCOMP_RET_TRAP },
690 sock_filter { code: BPF_RET_K, jt: 0, jf: 0, k: SECCOMP_RET_ALLOW },
691 ];
692
693 let prg =
694 convert_and_link_cbpf::<TestProgramContext>(&test_prg).expect("Error parsing program");
695
696 with_prg_assert_result(
697 &prg,
698 seccomp_data { arch: AUDIT_ARCH_AARCH64, ..Default::default() },
699 1,
700 "Did not reject incorrect arch",
701 );
702
703 with_prg_assert_result(
704 &prg,
705 seccomp_data { arch: AUDIT_ARCH_X86_64, nr: 41, ..Default::default() },
706 SECCOMP_RET_ALLOW,
707 "Did not pass simple RET_ALLOW",
708 );
709
710 with_prg_assert_result(
711 &prg,
712 seccomp_data {
713 arch: AUDIT_ARCH_X86_64,
714 nr: 100,
715 args: [0xFF00000000, 0, 0, 0, 0, 0],
716 ..Default::default()
717 },
718 SECCOMP_RET_TRAP,
719 "Did not treat load of first 32 bits correctly",
720 );
721
722 with_prg_assert_result(
723 &prg,
724 seccomp_data {
725 arch: AUDIT_ARCH_X86_64,
726 nr: 100,
727 args: [0x4, 0, 0, 0, 0, 0],
728 ..Default::default()
729 },
730 SECCOMP_RET_TRAP,
731 "Did not correctly reject load of second 32 bits",
732 );
733
734 with_prg_assert_result(
735 &prg,
736 seccomp_data {
737 arch: AUDIT_ARCH_X86_64,
738 nr: 100,
739 args: [0x0, 0, 0, 0, 0, 0],
740 ..Default::default()
741 },
742 SECCOMP_RET_ALLOW,
743 "Did not correctly accept load of second 32 bits",
744 );
745 }
746
747 #[test]
748 fn test_alu_insns() {
749 {
750 let test_prg = [
751 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 },
766 ];
767
768 let prg = convert_and_link_cbpf::<TestProgramContext>(&test_prg)
769 .expect("Error parsing program");
770
771 with_prg_assert_result(
772 &prg,
773 seccomp_data { nr: 1, ..Default::default() },
774 63,
775 "BPF math does not work",
776 );
777
778 with_prg_assert_result(
779 &prg,
780 seccomp_data { nr: 11, ..Default::default() },
781 46,
782 "BPF math does not work",
783 );
784 }
785
786 {
787 let test_prg = [
789 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 },
793 ];
794
795 let prg = convert_and_link_cbpf::<TestProgramContext>(&test_prg)
796 .expect("Error parsing program");
797
798 with_prg_assert_result(
799 &prg,
800 seccomp_data { nr: -1, ..Default::default() },
801 u32::MAX - 2,
802 "BPF math does not work",
803 );
804 }
805 }
806
807 #[test]
809 fn test_ld_msh() {
810 let test_prg = [
811 sock_filter { code: (BPF_LDX | BPF_MSH | BPF_B) as u16, jt: 0, jf: 0, k: 0 },
813 sock_filter { code: (BPF_MISC | BPF_TXA) as u16, jt: 0, jf: 0, k: 0 },
815 sock_filter { code: BPF_RET_A, jt: 0, jf: 0, k: 0 },
817 ];
818
819 let prg =
820 convert_and_link_cbpf::<TestProgramContext>(&test_prg).expect("Error parsing program");
821
822 for i in [0x00, 0x01, 0x07, 0x15, 0xff].iter() {
823 with_prg_assert_result(
824 &prg,
825 seccomp_data { nr: *i, ..Default::default() },
826 4 * (*i & 0xf) as u32,
827 "BPF math does not work",
828 )
829 }
830 }
831
832 #[test]
833 fn test_static_packet_len() {
834 let test_prg = [
835 sock_filter { code: (BPF_LD | BPF_LEN | BPF_W) as u16, jt: 0, jf: 0, k: 0 },
837 sock_filter { code: BPF_RET_A, jt: 0, jf: 0, k: 0 },
839 ];
840
841 let prg =
842 convert_and_link_cbpf::<TestProgramContext>(&test_prg).expect("Error parsing program");
843
844 let data = seccomp_data::default();
845 assert_eq!(prg.run(&mut (), &data), size_of::<seccomp_data>() as u64);
846 }
847
848 #[repr(C)]
851 #[derive(Debug, Default, IntoBytes, Immutable, KnownLayout, FromBytes)]
852 struct VariableLengthPacket {
853 foo: u32,
854 len: i32,
855 bar: u64,
856 }
857
858 static VARIABLE_LENGTH_PACKET_TYPE: LazyLock<Type> = LazyLock::new(|| Type::PtrToMemory {
859 id: MemoryId::new(),
860 offset: 0.into(),
861 buffer_size: size_of::<VariableLengthPacket>() as u64,
862 origin: PointerOrigin::KernelReadOnly,
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}