1use crate::executor::execute;
6use crate::verifier::VerifiedEbpfProgram;
7use crate::{
8 BPF_CALL, BPF_DW, BPF_JMP, BPF_LDDW, BPF_PSEUDO_MAP_IDX, BPF_PSEUDO_MAP_IDX_VALUE,
9 BPF_SIZE_MASK, CbpfConfig, DataWidth, EbpfError, EbpfInstruction, EbpfPtr, MapSchema, MemoryId,
10 StructAccess, Type,
11};
12use derivative::Derivative;
13use std::collections::HashMap;
14use std::fmt::Formatter;
15use std::mem::size_of;
16use zerocopy::{FromBytes, Immutable, IntoBytes};
17
18pub trait ProgramArgument: Into<BpfValue> {
20 fn get_type() -> &'static Type;
23
24 fn get_value_type(&self) -> Type {
29 Self::get_type().clone()
30 }
31
32 fn field_mappings() -> &'static [FieldMapping] {
35 static NO_MAPPINGS: [FieldMapping; 0] = [];
36 &NO_MAPPINGS
37 }
38
39 fn struct_mapping() -> Option<StructMapping> {
42 let fields = Self::field_mappings();
43 if fields.is_empty() {
44 return None;
45 }
46 Some(StructMapping {
47 memory_id: match Self::get_type() {
48 Type::PtrToStruct { id, .. } => id.clone(),
49 _ => panic!("type must be PtrToStruct"),
50 },
51 fields: fields.iter().cloned().collect(),
52 })
53 }
54}
55
56pub trait FromBpfValue<C>: Sized {
59 unsafe fn from_bpf_value(context: &mut C, v: BpfValue) -> Self;
62}
63
64impl ProgramArgument for () {
65 fn get_type() -> &'static Type {
66 &Type::UNINITIALIZED
67 }
68}
69
70impl<C> FromBpfValue<C> for () {
71 unsafe fn from_bpf_value(_context: &mut C, _v: BpfValue) -> Self {
72 unreachable!();
73 }
74}
75
76impl ProgramArgument for usize {
77 fn get_type() -> &'static Type {
78 &Type::UNKNOWN_SCALAR
79 }
80
81 fn get_value_type(&self) -> Type {
82 Type::from(*self as u64)
83 }
84}
85
86impl<'a, T> ProgramArgument for EbpfPtr<'a, T>
89where
90 &'a mut T: ProgramArgument,
91{
92 fn get_type() -> &'static Type {
93 <&'a mut T as ProgramArgument>::get_type()
94 }
95
96 fn field_mappings() -> &'static [FieldMapping] {
97 <&'a mut T as ProgramArgument>::field_mappings()
98 }
99
100 fn struct_mapping() -> Option<StructMapping> {
101 <&'a mut T as ProgramArgument>::struct_mapping()
102 }
103}
104
105impl<'a, T, C> FromBpfValue<C> for &'a mut T
106where
107 &'a mut T: ProgramArgument,
108{
109 unsafe fn from_bpf_value(_context: &mut C, v: BpfValue) -> Self {
110 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
111 unsafe {
112 &mut *v.as_ptr::<T>()
113 }
114 }
115}
116
117impl<'a, T, C> FromBpfValue<C> for &'a T
118where
119 &'a T: ProgramArgument,
120{
121 unsafe fn from_bpf_value(_context: &mut C, v: BpfValue) -> Self {
122 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
123 unsafe {
124 &*v.as_ptr::<T>()
125 }
126 }
127}
128
129pub trait MapReference {
133 fn schema(&self) -> &MapSchema;
134 fn as_bpf_value(&self) -> BpfValue;
135
136 fn get_data_ptr(&self) -> Option<BpfValue>;
141}
142
143pub enum NoMap {}
145
146impl MapReference for NoMap {
147 fn schema(&self) -> &MapSchema {
148 unreachable!()
149 }
150 fn as_bpf_value(&self) -> BpfValue {
151 unreachable!()
152 }
153 fn get_data_ptr(&self) -> Option<BpfValue> {
154 unreachable!()
155 }
156}
157
158pub trait StaticHelperSet: EbpfProgramContext {
159 fn helpers() -> &'static HelperSet<Self>;
161}
162
163pub trait EbpfProgramContext: 'static + Sized {
164 type RunContext<'a>;
166
167 type Packet<'a>: Packet + FromBpfValue<Self::RunContext<'a>>;
169
170 type Arg1<'a>: ProgramArgument;
172 type Arg2<'a>: ProgramArgument;
173 type Arg3<'a>: ProgramArgument;
174 type Arg4<'a>: ProgramArgument;
175 type Arg5<'a>: ProgramArgument;
176
177 type Map: MapReference;
179
180 fn struct_mappings() -> StructMappings {
183 let mut mappings = StructMappings::new();
184 if let Some(mapping) = Self::Arg1::struct_mapping() {
185 mappings.push(mapping);
186 }
187 if let Some(mapping) = Self::Arg2::struct_mapping() {
188 mappings.push(mapping);
189 }
190 if let Some(mapping) = Self::Arg3::struct_mapping() {
191 mappings.push(mapping);
192 }
193 if let Some(mapping) = Self::Arg4::struct_mapping() {
194 mappings.push(mapping);
195 }
196 if let Some(mapping) = Self::Arg5::struct_mapping() {
197 mappings.push(mapping);
198 }
199 mappings
200 }
201}
202
203pub trait Packet {
205 fn load(&self, offset: i32, width: DataWidth) -> Option<BpfValue>;
206}
207
208impl Packet for () {
209 fn load(&self, _offset: i32, _width: DataWidth) -> Option<BpfValue> {
210 None
211 }
212}
213
214impl<P: IntoBytes + Immutable> Packet for &P {
216 fn load(&self, offset: i32, width: DataWidth) -> Option<BpfValue> {
217 let data = (*self).as_bytes();
218 if offset < 0 || offset as usize >= data.len() {
219 return None;
220 }
221 let slice = &data[(offset as usize)..];
222 match width {
223 DataWidth::U8 => u8::read_from_prefix(slice).ok().map(|(v, _)| v.into()),
224 DataWidth::U16 => u16::read_from_prefix(slice).ok().map(|(v, _)| v.into()),
225 DataWidth::U32 => u32::read_from_prefix(slice).ok().map(|(v, _)| v.into()),
226 DataWidth::U64 => u64::read_from_prefix(slice).ok().map(|(v, _)| v.into()),
227 }
228 }
229}
230
231pub trait BpfProgramContext: 'static + Sized {
233 type RunContext<'a>;
234 type Packet<'a>: ProgramArgument + Packet + FromBpfValue<Self::RunContext<'a>>;
235 type Map: MapReference;
236 const CBPF_CONFIG: &'static CbpfConfig;
237
238 fn get_arg_types() -> Vec<Type> {
239 vec![<Self::Packet<'_> as ProgramArgument>::get_type().clone()]
240 }
241}
242
243impl<T: BpfProgramContext> EbpfProgramContext for T {
244 type RunContext<'a> = <T as BpfProgramContext>::RunContext<'a>;
245 type Packet<'a> = T::Packet<'a>;
246 type Arg1<'a> = T::Packet<'a>;
247 type Arg2<'a> = ();
248 type Arg3<'a> = ();
249 type Arg4<'a> = ();
250 type Arg5<'a> = ();
251 type Map = T::Map;
252}
253
254#[derive(Clone, Copy, Debug)]
255pub struct BpfValue(u64);
256
257static_assertions::const_assert_eq!(size_of::<BpfValue>(), size_of::<*const u8>());
258
259impl Default for BpfValue {
260 fn default() -> Self {
261 Self::from(0)
262 }
263}
264
265impl From<()> for BpfValue {
266 fn from(_v: ()) -> Self {
267 Self(0)
268 }
269}
270
271impl From<i32> for BpfValue {
272 fn from(v: i32) -> Self {
273 Self((v as u32) as u64)
274 }
275}
276
277impl From<u8> for BpfValue {
278 fn from(v: u8) -> Self {
279 Self::from(v as u64)
280 }
281}
282
283impl From<u16> for BpfValue {
284 fn from(v: u16) -> Self {
285 Self::from(v as u64)
286 }
287}
288
289impl From<u32> for BpfValue {
290 fn from(v: u32) -> Self {
291 Self::from(v as u64)
292 }
293}
294impl From<u64> for BpfValue {
295 fn from(v: u64) -> Self {
296 Self(v)
297 }
298}
299impl From<i64> for BpfValue {
300 fn from(v: i64) -> Self {
301 Self(v as u64)
302 }
303}
304
305impl From<usize> for BpfValue {
306 fn from(v: usize) -> Self {
307 Self(v as u64)
308 }
309}
310
311impl<T> From<*const T> for BpfValue {
312 fn from(v: *const T) -> Self {
313 Self(v as u64)
314 }
315}
316
317impl<T> From<*mut T> for BpfValue {
318 fn from(v: *mut T) -> Self {
319 Self(v as u64)
320 }
321}
322
323impl<T> From<&'_ T> for BpfValue {
324 fn from(v: &'_ T) -> Self {
325 Self((v as *const T) as u64)
326 }
327}
328
329impl<T> From<&'_ mut T> for BpfValue {
330 fn from(v: &'_ mut T) -> Self {
331 Self((v as *const T) as u64)
332 }
333}
334
335impl<'a, T> From<EbpfPtr<'a, T>> for BpfValue {
336 fn from(value: EbpfPtr<'a, T>) -> Self {
337 value.ptr().into()
338 }
339}
340
341impl From<BpfValue> for u8 {
342 fn from(v: BpfValue) -> u8 {
343 v.0 as u8
344 }
345}
346
347impl From<BpfValue> for u16 {
348 fn from(v: BpfValue) -> u16 {
349 v.0 as u16
350 }
351}
352
353impl From<BpfValue> for u32 {
354 fn from(v: BpfValue) -> u32 {
355 v.0 as u32
356 }
357}
358
359impl From<BpfValue> for u64 {
360 fn from(v: BpfValue) -> u64 {
361 v.0
362 }
363}
364
365impl From<BpfValue> for usize {
366 fn from(v: BpfValue) -> usize {
367 v.0 as usize
368 }
369}
370
371impl BpfValue {
372 pub fn is_zero(&self) -> bool {
373 self.0 == 0
374 }
375
376 pub fn as_u8(&self) -> u8 {
377 self.0 as u8
378 }
379
380 pub fn as_u16(&self) -> u16 {
381 self.0 as u16
382 }
383
384 pub fn as_u32(&self) -> u32 {
385 self.0 as u32
386 }
387
388 pub fn as_i32(&self) -> i32 {
389 self.0 as i32
390 }
391
392 pub fn as_u64(&self) -> u64 {
393 self.0
394 }
395
396 pub fn as_usize(&self) -> usize {
397 self.0 as usize
398 }
399
400 pub fn as_ptr<T>(&self) -> *mut T {
401 self.0 as *mut T
402 }
403}
404
405impl From<BpfValue> for () {
406 fn from(_v: BpfValue) -> Self {
407 ()
408 }
409}
410
411#[derive(Derivative)]
412#[derivative(Clone(bound = ""))]
413pub struct EbpfHelperImpl<C: EbpfProgramContext>(
414 pub for<'a> fn(
415 &mut C::RunContext<'a>,
416 BpfValue,
417 BpfValue,
418 BpfValue,
419 BpfValue,
420 BpfValue,
421 ) -> BpfValue,
422);
423
424#[derive(Derivative)]
427#[derivative(Clone(bound = ""), Default(bound = ""))]
428pub struct HelperSet<C: EbpfProgramContext> {
429 helpers: Vec<EbpfHelperImpl<C>>,
430
431 indices: HashMap<u32, u32>,
433}
434
435impl<C: EbpfProgramContext> FromIterator<(u32, EbpfHelperImpl<C>)> for HelperSet<C> {
436 fn from_iter<T>(iter: T) -> Self
437 where
438 T: IntoIterator<Item = (u32, EbpfHelperImpl<C>)>,
439 {
440 let mut helpers = Vec::new();
441 let mut indices = HashMap::new();
442 for (helper_id, helper) in iter {
443 let helper_index = helpers.len();
444 helpers.push(helper);
445 indices.insert(helper_id, helper_index as u32);
446 }
447 Self { helpers, indices }
448 }
449}
450
451impl<C: EbpfProgramContext> HelperSet<C> {
452 pub fn new(iter: impl IntoIterator<Item = (u32, EbpfHelperImpl<C>)>) -> Self {
453 Self::from_iter(iter)
454 }
455
456 pub fn get_index_by_id(&self, id: u32) -> Option<u32> {
458 self.indices.get(&id).cloned()
459 }
460
461 pub fn get_by_index(&self, index: u32) -> Option<&EbpfHelperImpl<C>> {
463 self.helpers.get(index as usize)
464 }
465}
466
467#[derive(Clone, Copy, Debug, PartialEq)]
470pub struct FieldMapping {
471 pub source_offset: usize,
473 pub target_offset: usize,
475}
476
477pub type FieldMappings = smallvec::SmallVec<[FieldMapping; 2]>;
478
479#[derive(Clone, Debug)]
480pub struct StructMapping {
481 pub memory_id: MemoryId,
483
484 pub fields: FieldMappings,
488}
489
490pub type StructMappings = smallvec::SmallVec<[StructMapping; 1]>;
491
492pub trait ArgumentTypeChecker<C: EbpfProgramContext>: Sized {
493 fn link(program: &VerifiedEbpfProgram) -> Result<Self, EbpfError>;
494 fn run_time_check<'a>(
495 &self,
496 arg1: &C::Arg1<'a>,
497 arg2: &C::Arg2<'a>,
498 arg3: &C::Arg3<'a>,
499 arg4: &C::Arg4<'a>,
500 arg5: &C::Arg5<'a>,
501 ) -> Result<(), EbpfError>;
502}
503
504pub struct StaticTypeChecker();
505
506impl<C: EbpfProgramContext> ArgumentTypeChecker<C> for StaticTypeChecker {
507 fn link(program: &VerifiedEbpfProgram) -> Result<Self, EbpfError> {
508 let arg_types = [
509 C::Arg1::get_type(),
510 C::Arg2::get_type(),
511 C::Arg3::get_type(),
512 C::Arg4::get_type(),
513 C::Arg5::get_type(),
514 ];
515 for i in 0..5 {
516 let verified_type = program.args.get(i).unwrap_or(&Type::UNINITIALIZED);
517 if !arg_types[i].is_subtype(verified_type) {
518 return Err(EbpfError::ProgramLinkError(format!(
519 "Type of argument {} doesn't match. Verified type: {:?}. Context type: {:?}",
520 i + 1,
521 verified_type,
522 arg_types[i],
523 )));
524 }
525 }
526
527 Ok(Self())
528 }
529
530 fn run_time_check<'a>(
531 &self,
532 _arg1: &C::Arg1<'a>,
533 _arg2: &C::Arg2<'a>,
534 _arg3: &C::Arg3<'a>,
535 _arg4: &C::Arg4<'a>,
536 _arg5: &C::Arg5<'a>,
537 ) -> Result<(), EbpfError> {
538 Ok(())
540 }
541}
542
543pub struct DynamicTypeChecker {
544 types: Vec<Type>,
545}
546
547impl<C: EbpfProgramContext> ArgumentTypeChecker<C> for DynamicTypeChecker {
548 fn link(program: &VerifiedEbpfProgram) -> Result<Self, EbpfError> {
549 Ok(Self { types: program.args.clone() })
550 }
551
552 fn run_time_check<'a>(
553 &self,
554 arg1: &C::Arg1<'a>,
555 arg2: &C::Arg2<'a>,
556 arg3: &C::Arg3<'a>,
557 arg4: &C::Arg4<'a>,
558 arg5: &C::Arg5<'a>,
559 ) -> Result<(), EbpfError> {
560 let arg_types = [
561 arg1.get_value_type(),
562 arg2.get_value_type(),
563 arg3.get_value_type(),
564 arg4.get_value_type(),
565 arg5.get_value_type(),
566 ];
567 for i in 0..5 {
568 let verified_type = self.types.get(i).unwrap_or(&Type::UNINITIALIZED);
569 if !&arg_types[i].is_subtype(verified_type) {
570 return Err(EbpfError::ProgramLinkError(format!(
571 "Type of argument {} doesn't match. Verified type: {:?}. Value type: {:?}",
572 i + 1,
573 verified_type,
574 arg_types[i],
575 )));
576 }
577 }
578
579 Ok(())
580 }
581}
582
583pub struct EbpfProgram<C: EbpfProgramContext, T: ArgumentTypeChecker<C> = StaticTypeChecker> {
585 pub(crate) code: Vec<EbpfInstruction>,
586
587 #[allow(dead_code)]
590 pub(crate) maps: Vec<C::Map>,
591
592 type_checker: T,
593}
594
595impl<C: EbpfProgramContext, T: ArgumentTypeChecker<C>> std::fmt::Debug for EbpfProgram<C, T> {
596 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
597 f.debug_struct("EbpfProgram").field("code", &self.code).finish()
598 }
599}
600
601impl<C: EbpfProgramContext, T: ArgumentTypeChecker<C>> EbpfProgram<C, T> {
602 pub fn code(&self) -> &[EbpfInstruction] {
603 &self.code[..]
604 }
605}
606
607impl<C, T: ArgumentTypeChecker<C>> EbpfProgram<C, T>
608where
609 C: StaticHelperSet,
610 C: for<'a> EbpfProgramContext<Arg2<'a> = (), Arg3<'a> = (), Arg4<'a> = (), Arg5<'a> = ()>,
611{
612 pub fn run_with_1_argument<'a>(
613 &self,
614 run_context: &mut C::RunContext<'a>,
615 arg1: C::Arg1<'a>,
616 ) -> u64 {
617 self.type_checker
618 .run_time_check(&arg1, &(), &(), &(), &())
619 .expect("Failed argument type check");
620 execute(&self.code[..], C::helpers(), run_context, &[arg1.into()])
621 }
622}
623
624impl<C, T: ArgumentTypeChecker<C>> EbpfProgram<C, T>
625where
626 C: StaticHelperSet,
627 C: for<'a> EbpfProgramContext<Arg3<'a> = (), Arg4<'a> = (), Arg5<'a> = ()>,
628{
629 pub fn run_with_2_arguments<'a>(
630 &self,
631 run_context: &mut C::RunContext<'a>,
632 arg1: C::Arg1<'a>,
633 arg2: C::Arg2<'a>,
634 ) -> u64 {
635 self.type_checker
636 .run_time_check(&arg1, &arg2, &(), &(), &())
637 .expect("Failed argument type check");
638 execute(&self.code[..], C::helpers(), run_context, &[arg1.into(), arg2.into()])
639 }
640}
641
642impl<C: BpfProgramContext, T: ArgumentTypeChecker<C>> EbpfProgram<C, T>
643where
644 C: BpfProgramContext + StaticHelperSet,
645 C: for<'a> EbpfProgramContext<Arg2<'a> = (), Arg3<'a> = (), Arg4<'a> = (), Arg5<'a> = ()>,
646{
647 pub fn run<'a>(
651 &self,
652 run_context: &mut <C as EbpfProgramContext>::RunContext<'a>,
653 packet: <C as EbpfProgramContext>::Arg1<'a>,
654 ) -> u64 {
655 self.run_with_1_argument(run_context, packet)
656 }
657}
658
659pub fn link_program_internal<C, T>(
662 program: &VerifiedEbpfProgram,
663 maps: Vec<C::Map>,
664) -> Result<EbpfProgram<C, T>, EbpfError>
665where
666 C: EbpfProgramContext + StaticHelperSet,
667 T: ArgumentTypeChecker<C>,
668{
669 let type_checker = T::link(program)?;
670
671 let mut code = program.code.clone();
672 let struct_mappings = C::struct_mappings();
673
674 for StructAccess { pc, memory_id, field_offset, is_32_bit_ptr_load } in
676 program.struct_access_instructions.iter()
677 {
678 let field_mapping =
679 struct_mappings.iter().find(|m| m.memory_id == *memory_id).and_then(|struct_map| {
680 struct_map.fields.iter().find(|m| m.source_offset == *field_offset)
681 });
682
683 if let Some(field_mapping) = field_mapping {
684 let instruction = &mut code[*pc];
685
686 let offset_diff = i16::try_from(
690 i64::try_from(field_mapping.target_offset).unwrap()
691 - i64::try_from(field_mapping.source_offset).unwrap(),
692 )
693 .unwrap();
694
695 let new_offset = instruction.offset().checked_add(offset_diff).ok_or_else(|| {
696 EbpfError::ProgramLinkError(format!("Struct field offset overflow at PC {}", *pc))
697 })?;
698 instruction.set_offset(new_offset);
699
700 if *is_32_bit_ptr_load {
702 instruction.set_code((instruction.code() & !BPF_SIZE_MASK) | BPF_DW);
703 }
704 } else {
705 if *is_32_bit_ptr_load {
706 return Err(EbpfError::ProgramLinkError(format!(
707 "32-bit field isn't mapped at pc {}",
708 *pc,
709 )));
710 }
711 }
712 }
713
714 let helpers = C::helpers();
715
716 for pc in 0..code.len() {
717 let instruction = &mut code[pc];
718
719 if instruction.code() == (BPF_JMP | BPF_CALL) {
721 assert!(instruction.src_reg() == 0);
722 let helper_id = instruction.imm() as u32;
723 let Some(index) = helpers.get_index_by_id(helper_id) else {
724 return Err(EbpfError::ProgramLinkError(format!(
725 "Missing implementation for helper with id={}",
726 helper_id,
727 )));
728 };
729 instruction.set_imm(index as i32);
730 }
731
732 if instruction.code() == BPF_LDDW {
734 match instruction.src_reg() {
737 0 => (),
738 BPF_PSEUDO_MAP_IDX | BPF_PSEUDO_MAP_IDX_VALUE => {
739 let map_index = usize::try_from(instruction.imm())
740 .expect("negative map index in a verified program");
741 let map = maps.get(map_index).ok_or_else(|| {
742 EbpfError::ProgramLinkError(format!("Invalid map_index: {}", map_index))
743 })?;
744 assert!(*map.schema() == program.maps[map_index]);
745
746 let (instruction, next_instruction) = {
747 let (a1, a2) = code.split_at_mut(pc + 1);
749 (&mut a1[pc], &mut a2[0])
750 };
751
752 let value = if instruction.src_reg() == BPF_PSEUDO_MAP_IDX {
753 map.as_bpf_value().as_u64()
754 } else {
755 let map_value = map
757 .get_data_ptr()
758 .ok_or_else(|| {
759 EbpfError::ProgramLinkError(format!(
760 "Unable to get value at 0 for map at index {map_index}"
761 ))
762 })?
763 .as_u64();
764 map_value.checked_add_signed(next_instruction.imm().into()).ok_or_else(
765 || {
766 EbpfError::ProgramLinkError(format!(
767 "Unable to use offset for map access"
768 ))
769 },
770 )?
771 };
772
773 let (high, low) = ((value >> 32) as i32, value as i32);
774 instruction.set_src_reg(0);
775 instruction.set_imm(low);
776 next_instruction.set_imm(high);
777 }
778 value => {
779 return Err(EbpfError::ProgramLinkError(format!(
780 "Unsupported value for src_reg in lddw: {}",
781 value,
782 )));
783 }
784 }
785 }
786 }
787
788 Ok(EbpfProgram { code, maps, type_checker })
789}
790
791pub fn link_program<C>(
794 program: &VerifiedEbpfProgram,
795 maps: Vec<C::Map>,
796) -> Result<EbpfProgram<C>, EbpfError>
797where
798 C: EbpfProgramContext + StaticHelperSet,
799{
800 link_program_internal::<C, StaticTypeChecker>(program, maps)
801}
802
803pub fn link_program_dynamic<C>(
805 program: &VerifiedEbpfProgram,
806 maps: Vec<C::Map>,
807) -> Result<EbpfProgram<C, DynamicTypeChecker>, EbpfError>
808where
809 C: EbpfProgramContext + StaticHelperSet,
810{
811 link_program_internal::<C, DynamicTypeChecker>(program, maps)
812}
813
814#[macro_export]
815macro_rules! static_helper_set {
816 ($context:ty, $value:expr) => {
817 impl $crate::StaticHelperSet for $context {
818 fn helpers() -> &'static $crate::HelperSet<$context> {
819 static HELPERS: std::sync::LazyLock<$crate::HelperSet<$context>> =
820 std::sync::LazyLock::new(|| $value);
821 &HELPERS
822 }
823 }
824 };
825}
826
827#[macro_export]
828macro_rules! empty_static_helper_set {
829 ($context:ty) => {
830 $crate::static_helper_set!($context, $crate::HelperSet::default());
831 };
832}
833
834#[cfg(test)]
835mod test {
836 use super::*;
837 use crate::api::*;
838 use crate::conformance::test::parse_asm;
839 use crate::{
840 CallingContext, FieldDescriptor, FieldMapping, FieldType, NullVerifierLogger,
841 ProgramArgument, StructDescriptor, Type, verify_program,
842 };
843 use std::mem::offset_of;
844 use std::sync::{Arc, LazyLock};
845 use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
846
847 #[repr(C)]
848 #[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
849 struct TestArgument {
850 pub read_only_field: u32,
852 pub _padding1: u32,
853 pub data: u64,
855 pub data_end: u64,
857 pub mutable_field: u32,
859 pub _padding2: u32,
860 }
861
862 static TEST_ARG_TYPE: LazyLock<Type> = LazyLock::new(|| {
863 let data_memory_id = MemoryId::new();
864 let descriptor = Arc::new(StructDescriptor {
865 fields: vec![
866 FieldDescriptor {
867 offset: offset_of!(TestArgument, read_only_field),
868 field_type: FieldType::Scalar { size: 4 },
869 },
870 FieldDescriptor {
871 offset: offset_of!(TestArgument, data),
872 field_type: FieldType::PtrToArray {
873 is_32_bit: false,
874 id: data_memory_id.clone(),
875 },
876 },
877 FieldDescriptor {
878 offset: offset_of!(TestArgument, data_end),
879 field_type: FieldType::PtrToEndArray {
880 is_32_bit: false,
881 id: data_memory_id.clone(),
882 },
883 },
884 FieldDescriptor {
885 offset: offset_of!(TestArgument, mutable_field),
886 field_type: FieldType::MutableScalar { size: 4 },
887 },
888 ],
889 });
890
891 Type::PtrToStruct { id: MemoryId::new(), offset: 0.into(), descriptor }
892 });
893
894 impl Default for TestArgument {
895 fn default() -> Self {
896 Self {
897 read_only_field: 1,
898 _padding1: 0,
899 data: 0,
900 data_end: 0,
901 mutable_field: 2,
902 _padding2: 0,
903 }
904 }
905 }
906
907 impl TestArgument {
908 fn from_data(data: &[u64]) -> Self {
909 let ptr_range = data.as_ptr_range();
910 Self {
911 data: ptr_range.start as u64,
912 data_end: ptr_range.end as u64,
913 ..Default::default()
914 }
915 }
916 }
917
918 impl ProgramArgument for &'_ mut TestArgument {
919 fn get_type() -> &'static Type {
920 &*TEST_ARG_TYPE
921 }
922 }
923
924 #[repr(C)]
927 struct TestArgument32 {
928 pub read_only_field: u32,
929 pub data: u32,
930 pub data_end: u32,
931 pub mutable_field: u32,
932 }
933
934 #[repr(C)]
935 struct TestArgument32BitMapped(TestArgument);
936
937 static TEST_ARG_32_BIT_MEMORY_ID: LazyLock<MemoryId> = LazyLock::new(|| MemoryId::new());
938 static TEST_ARG_32_BIT_TYPE: LazyLock<Type> = LazyLock::new(|| {
939 let data_memory_id = MemoryId::new();
940 let descriptor = Arc::new(StructDescriptor {
941 fields: vec![
942 FieldDescriptor {
943 offset: offset_of!(TestArgument32, read_only_field),
944 field_type: FieldType::Scalar { size: 4 },
945 },
946 FieldDescriptor {
947 offset: offset_of!(TestArgument32, data),
948 field_type: FieldType::PtrToArray {
949 is_32_bit: true,
950 id: data_memory_id.clone(),
951 },
952 },
953 FieldDescriptor {
954 offset: offset_of!(TestArgument32, data_end),
955 field_type: FieldType::PtrToEndArray {
956 is_32_bit: true,
957 id: data_memory_id.clone(),
958 },
959 },
960 FieldDescriptor {
961 offset: offset_of!(TestArgument32, mutable_field),
962 field_type: FieldType::MutableScalar { size: 4 },
963 },
964 ],
965 });
966
967 Type::PtrToStruct { id: TEST_ARG_32_BIT_MEMORY_ID.clone(), offset: 0.into(), descriptor }
968 });
969
970 impl ProgramArgument for &'_ TestArgument32BitMapped {
971 fn get_type() -> &'static Type {
972 &*TEST_ARG_32_BIT_TYPE
973 }
974
975 fn field_mappings() -> &'static [FieldMapping] {
976 static FIELD_MAPPINGS: [FieldMapping; 3] = [
977 FieldMapping {
978 source_offset: offset_of!(TestArgument32, data),
979 target_offset: offset_of!(TestArgument, data),
980 },
981 FieldMapping {
982 source_offset: offset_of!(TestArgument32, data_end),
983 target_offset: offset_of!(TestArgument, data_end),
984 },
985 FieldMapping {
986 source_offset: offset_of!(TestArgument32, mutable_field),
987 target_offset: offset_of!(TestArgument, mutable_field),
988 },
989 ];
990 &FIELD_MAPPINGS
991 }
992 }
993
994 struct TestEbpfProgramContext {}
995
996 impl EbpfProgramContext for TestEbpfProgramContext {
997 type RunContext<'a> = ();
998
999 type Packet<'a> = ();
1000 type Arg1<'a> = &'a mut TestArgument;
1001 type Arg2<'a> = ();
1002 type Arg3<'a> = ();
1003 type Arg4<'a> = ();
1004 type Arg5<'a> = ();
1005
1006 type Map = NoMap;
1007 }
1008
1009 empty_static_helper_set!(TestEbpfProgramContext);
1010
1011 fn initialize_test_program(
1012 code: Vec<EbpfInstruction>,
1013 ) -> Result<EbpfProgram<TestEbpfProgramContext>, EbpfError> {
1014 let verified_program = verify_program(
1015 code,
1016 CallingContext { args: vec![TEST_ARG_TYPE.clone()], ..Default::default() },
1017 &mut NullVerifierLogger,
1018 )?;
1019 link_program(&verified_program, vec![])
1020 }
1021
1022 struct TestEbpfProgramContext32BitMapped {}
1023
1024 impl EbpfProgramContext for TestEbpfProgramContext32BitMapped {
1025 type RunContext<'a> = ();
1026
1027 type Packet<'a> = ();
1028 type Arg1<'a> = &'a TestArgument32BitMapped;
1029 type Arg2<'a> = ();
1030 type Arg3<'a> = ();
1031 type Arg4<'a> = ();
1032 type Arg5<'a> = ();
1033
1034 type Map = NoMap;
1035 }
1036
1037 empty_static_helper_set!(TestEbpfProgramContext32BitMapped);
1038
1039 fn initialize_test_program_for_32bit_arg(
1040 code: Vec<EbpfInstruction>,
1041 ) -> Result<EbpfProgram<TestEbpfProgramContext32BitMapped>, EbpfError> {
1042 let verified_program = verify_program(
1043 code,
1044 CallingContext { args: vec![TEST_ARG_32_BIT_TYPE.clone()], ..Default::default() },
1045 &mut NullVerifierLogger,
1046 )?;
1047 link_program(&verified_program, vec![])
1048 }
1049
1050 #[test]
1051 fn test_data_end() {
1052 let program = r#"
1053 mov %r0, 0
1054 ldxdw %r2, [%r1+16]
1055 ldxdw %r1, [%r1+8]
1056 # ensure data contains at least 8 bytes
1057 mov %r3, %r1
1058 add %r3, 0x8
1059 jgt %r3, %r2, +1
1060 # read 8 bytes from data
1061 ldxdw %r0, [%r1]
1062 exit
1063 "#;
1064 let program = initialize_test_program(parse_asm(program)).expect("load");
1065
1066 let v = [42];
1067 let mut data = TestArgument::from_data(&v[..]);
1068 assert_eq!(program.run_with_1_argument(&mut (), &mut data), v[0]);
1069 }
1070
1071 #[test]
1072 fn test_past_data_end() {
1073 let program = r#"
1074 mov %r0, 0
1075 ldxdw %r2, [%r1+16]
1076 ldxdw %r1, [%r1+6]
1077 # ensure data contains at least 4 bytes
1078 mov %r3, %r1
1079 add %r3, 0x4
1080 jgt %r3, %r2, +1
1081 # read 8 bytes from data
1082 ldxdw %r0, [%r1]
1083 exit
1084 "#;
1085 initialize_test_program(parse_asm(program)).expect_err("incorrect program");
1086 }
1087
1088 #[test]
1089 fn test_mapping() {
1090 let program = r#"
1091 # Return `TestArgument32.mutable_field`
1092 ldxw %r0, [%r1+12]
1093 exit
1094 "#;
1095 let program = initialize_test_program_for_32bit_arg(parse_asm(program)).expect("load");
1096
1097 let mut data = TestArgument32BitMapped(TestArgument::default());
1098 assert_eq!(program.run_with_1_argument(&mut (), &mut data), data.0.mutable_field as u64);
1099 }
1100
1101 #[test]
1102 fn test_mapping_partial_load() {
1103 let program = r#"
1105 # Returns two upper bytes of `TestArgument32.mutable_filed`
1106 ldxh %r0, [%r1+14]
1107 exit
1108 "#;
1109 let program = initialize_test_program_for_32bit_arg(parse_asm(program)).expect("load");
1110
1111 let mut data = TestArgument32BitMapped(TestArgument::default());
1112 data.0.mutable_field = 0x12345678;
1113 assert_eq!(program.run_with_1_argument(&mut (), &mut data), 0x1234 as u64);
1114 }
1115
1116 #[test]
1117 fn test_mapping_ptr() {
1118 let program = r#"
1119 mov %r0, 0
1120 # Load data and data_end as 32 bits pointers in TestArgument32
1121 ldxw %r2, [%r1+8]
1122 ldxw %r1, [%r1+4]
1123 # ensure data contains at least 8 bytes
1124 mov %r3, %r1
1125 add %r3, 0x8
1126 jgt %r3, %r2, +1
1127 # read 8 bytes from data
1128 ldxdw %r0, [%r1]
1129 exit
1130 "#;
1131 let program = initialize_test_program_for_32bit_arg(parse_asm(program)).expect("load");
1132
1133 let v = [42];
1134 let mut data = TestArgument32BitMapped(TestArgument::from_data(&v[..]));
1135 assert_eq!(program.run_with_1_argument(&mut (), &mut data), v[0]);
1136 }
1137
1138 #[test]
1139 fn test_mapping_with_offset() {
1140 let program = r#"
1141 mov %r0, 0
1142 add %r1, 0x8
1143 # Load data and data_end as 32 bits pointers in TestArgument32
1144 ldxw %r2, [%r1]
1145 ldxw %r1, [%r1-4]
1146 # ensure data contains at least 8 bytes
1147 mov %r3, %r1
1148 add %r3, 0x8
1149 jgt %r3, %r2, +1
1150 # read 8 bytes from data
1151 ldxdw %r0, [%r1]
1152 exit
1153 "#;
1154 let program = initialize_test_program_for_32bit_arg(parse_asm(program)).expect("load");
1155
1156 let v = [42];
1157 let mut data = TestArgument32BitMapped(TestArgument::from_data(&v[..]));
1158 assert_eq!(program.run_with_1_argument(&mut (), &mut data), v[0]);
1159 }
1160
1161 #[test]
1162 fn test_ptr_diff() {
1163 let program = r#"
1164 mov %r0, %r1
1165 add %r0, 0x2
1166 # Substract 2 ptr to memory
1167 sub %r0, %r1
1168
1169 mov %r2, %r10
1170 add %r2, 0x3
1171 # Substract 2 ptr to stack
1172 sub %r2, %r10
1173 add %r0, %r2
1174
1175 ldxdw %r2, [%r1+16]
1176 ldxdw %r1, [%r1+8]
1177 # Substract ptr to array and ptr to array end
1178 sub %r2, %r1
1179 add %r0, %r2
1180
1181 mov %r2, %r1
1182 add %r2, 0x4
1183 # Substract 2 ptr to array
1184 sub %r2, %r1
1185 add %r0, %r2
1186
1187 exit
1188 "#;
1189 let code = parse_asm(program);
1190
1191 let program = initialize_test_program(code).expect("load");
1192
1193 let v = [42];
1194 let mut data = TestArgument::from_data(&v[..]);
1195 assert_eq!(program.run_with_1_argument(&mut (), &mut data), 17);
1196 }
1197
1198 #[test]
1199 fn test_invalid_packet_load() {
1200 let program = r#"
1201 mov %r6, %r2
1202 mov %r0, 0
1203 ldpw
1204 exit
1205 "#;
1206 let args = vec![
1207 Type::PtrToMemory { id: MemoryId::new(), offset: 0.into(), buffer_size: 16 },
1208 Type::PtrToMemory { id: MemoryId::new(), offset: 0.into(), buffer_size: 16 },
1209 ];
1210 let verify_result = verify_program(
1211 parse_asm(program),
1212 CallingContext { args, ..Default::default() },
1213 &mut NullVerifierLogger,
1214 );
1215
1216 assert_eq!(
1217 verify_result.expect_err("validation should fail"),
1218 EbpfError::ProgramVerifyError("at PC 2: R6 is not a packet".to_string())
1219 );
1220 }
1221
1222 #[test]
1223 fn test_invalid_field_size() {
1224 let program = r#"
1226 ldxdw %r0, [%r1]
1227 exit
1228 "#;
1229 initialize_test_program(parse_asm(program)).expect_err("incorrect program");
1230 }
1231
1232 #[test]
1233 fn test_unknown_field() {
1234 let program = r#"
1236 ldxw %r0, [%r1 + 4]
1237 exit
1238 "#;
1239 initialize_test_program(parse_asm(program)).expect_err("incorrect program");
1240 }
1241
1242 #[test]
1243 fn test_partial_ptr_field() {
1244 let program = r#"
1246 ldxw %r0, [%r1 + 8]
1247 exit
1248 "#;
1249 initialize_test_program(parse_asm(program)).expect_err("incorrect program");
1250 }
1251
1252 #[test]
1253 fn test_readonly_field() {
1254 let program = r#"
1256 stw [%r1], 0x42
1257 exit
1258 "#;
1259 initialize_test_program(parse_asm(program)).expect_err("incorrect program");
1260 }
1261
1262 #[test]
1263 fn test_store_mutable_field() {
1264 let program = r#"
1266 stw [%r1 + 24], 0x42
1267 mov %r0, 1
1268 exit
1269 "#;
1270 let program = initialize_test_program(parse_asm(program)).expect("load");
1271
1272 let mut data = TestArgument::default();
1273 assert_eq!(program.run_with_1_argument(&mut (), &mut data), 1);
1274 assert_eq!(data.mutable_field, 0x42);
1275 }
1276
1277 #[test]
1278 fn test_fake_array_bounds_check() {
1279 let program = r#"
1282 mov %r0, 0
1283 ldxdw %r2, [%r1+16]
1284 ldxdw %r1, [%r1+8]
1285 # Subtract 8 from `data` and pretend checking array bounds.
1286 mov %r3, %r1
1287 sub %r3, 0x8
1288 jgt %r3, %r2, +1
1289 # Read 8 bytes from `data`. This should be rejected by the verifier.
1290 ldxdw %r0, [%r1]
1291 exit
1292 "#;
1293 initialize_test_program(parse_asm(program)).expect_err("incorrect program");
1294 }
1295}