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 TryFrom<BpfValue> for u8 {
342 type Error = core::num::TryFromIntError;
343 fn try_from(v: BpfValue) -> Result<Self, Self::Error> {
344 v.0.try_into()
345 }
346}
347
348impl TryFrom<BpfValue> for u16 {
349 type Error = core::num::TryFromIntError;
350 fn try_from(v: BpfValue) -> Result<Self, Self::Error> {
351 v.0.try_into()
352 }
353}
354
355impl TryFrom<BpfValue> for u32 {
356 type Error = core::num::TryFromIntError;
357 fn try_from(v: BpfValue) -> Result<Self, Self::Error> {
358 v.0.try_into()
359 }
360}
361
362impl From<BpfValue> for u64 {
363 fn from(v: BpfValue) -> u64 {
364 v.0
365 }
366}
367
368impl From<BpfValue> for usize {
369 fn from(v: BpfValue) -> usize {
370 v.0 as usize
371 }
372}
373
374impl BpfValue {
375 pub fn is_zero(&self) -> bool {
376 self.0 == 0
377 }
378
379 pub fn as_u8(&self) -> u8 {
380 self.0 as u8
381 }
382
383 pub fn as_u16(&self) -> u16 {
384 self.0 as u16
385 }
386
387 pub fn as_u32(&self) -> u32 {
388 self.0 as u32
389 }
390
391 pub fn as_i32(&self) -> i32 {
392 self.0 as i32
393 }
394
395 pub fn as_u64(&self) -> u64 {
396 self.0
397 }
398
399 pub fn as_usize(&self) -> usize {
400 self.0 as usize
401 }
402
403 pub fn as_ptr<T>(&self) -> *mut T {
404 self.0 as *mut T
405 }
406}
407
408impl From<BpfValue> for () {
409 fn from(_v: BpfValue) -> Self {
410 ()
411 }
412}
413
414#[derive(Derivative)]
415#[derivative(Clone(bound = ""))]
416pub struct EbpfHelperImpl<C: EbpfProgramContext>(
417 pub for<'a> fn(
418 &mut C::RunContext<'a>,
419 BpfValue,
420 BpfValue,
421 BpfValue,
422 BpfValue,
423 BpfValue,
424 ) -> BpfValue,
425);
426
427#[derive(Derivative)]
430#[derivative(Clone(bound = ""), Default(bound = ""))]
431pub struct HelperSet<C: EbpfProgramContext> {
432 helpers: Vec<EbpfHelperImpl<C>>,
433
434 indices: HashMap<u32, u32>,
436}
437
438impl<C: EbpfProgramContext> FromIterator<(u32, EbpfHelperImpl<C>)> for HelperSet<C> {
439 fn from_iter<T>(iter: T) -> Self
440 where
441 T: IntoIterator<Item = (u32, EbpfHelperImpl<C>)>,
442 {
443 let mut helpers = Vec::new();
444 let mut indices = HashMap::new();
445 for (helper_id, helper) in iter {
446 let helper_index = helpers.len();
447 helpers.push(helper);
448 indices.insert(helper_id, helper_index as u32);
449 }
450 Self { helpers, indices }
451 }
452}
453
454impl<C: EbpfProgramContext> HelperSet<C> {
455 pub fn new(iter: impl IntoIterator<Item = (u32, EbpfHelperImpl<C>)>) -> Self {
456 Self::from_iter(iter)
457 }
458
459 pub fn get_index_by_id(&self, id: u32) -> Option<u32> {
461 self.indices.get(&id).cloned()
462 }
463
464 pub fn get_by_index(&self, index: u32) -> Option<&EbpfHelperImpl<C>> {
466 self.helpers.get(index as usize)
467 }
468}
469
470#[derive(Clone, Copy, Debug, PartialEq)]
473pub struct FieldMapping {
474 pub source_offset: usize,
476 pub target_offset: usize,
478}
479
480pub type FieldMappings = smallvec::SmallVec<[FieldMapping; 2]>;
481
482#[derive(Clone, Debug)]
483pub struct StructMapping {
484 pub memory_id: MemoryId,
486
487 pub fields: FieldMappings,
491}
492
493pub type StructMappings = smallvec::SmallVec<[StructMapping; 1]>;
494
495pub trait ArgumentTypeChecker<C: EbpfProgramContext>: Sized {
496 fn link(program: &VerifiedEbpfProgram) -> Result<Self, EbpfError>;
497 fn run_time_check<'a>(
498 &self,
499 arg1: &C::Arg1<'a>,
500 arg2: &C::Arg2<'a>,
501 arg3: &C::Arg3<'a>,
502 arg4: &C::Arg4<'a>,
503 arg5: &C::Arg5<'a>,
504 ) -> Result<(), EbpfError>;
505}
506
507pub struct StaticTypeChecker();
508
509impl<C: EbpfProgramContext> ArgumentTypeChecker<C> for StaticTypeChecker {
510 fn link(program: &VerifiedEbpfProgram) -> Result<Self, EbpfError> {
511 let arg_types = [
512 C::Arg1::get_type(),
513 C::Arg2::get_type(),
514 C::Arg3::get_type(),
515 C::Arg4::get_type(),
516 C::Arg5::get_type(),
517 ];
518 for i in 0..5 {
519 let verified_type = program.args.get(i).unwrap_or(&Type::UNINITIALIZED);
520 if !arg_types[i].is_subtype(verified_type) {
521 return Err(EbpfError::ProgramLinkError(format!(
522 "Type of argument {} doesn't match. Verified type: {:?}. Context type: {:?}",
523 i + 1,
524 verified_type,
525 arg_types[i],
526 )));
527 }
528 }
529
530 Ok(Self())
531 }
532
533 fn run_time_check<'a>(
534 &self,
535 _arg1: &C::Arg1<'a>,
536 _arg2: &C::Arg2<'a>,
537 _arg3: &C::Arg3<'a>,
538 _arg4: &C::Arg4<'a>,
539 _arg5: &C::Arg5<'a>,
540 ) -> Result<(), EbpfError> {
541 Ok(())
543 }
544}
545
546pub struct DynamicTypeChecker {
547 types: Vec<Type>,
548}
549
550impl<C: EbpfProgramContext> ArgumentTypeChecker<C> for DynamicTypeChecker {
551 fn link(program: &VerifiedEbpfProgram) -> Result<Self, EbpfError> {
552 Ok(Self { types: program.args.clone() })
553 }
554
555 fn run_time_check<'a>(
556 &self,
557 arg1: &C::Arg1<'a>,
558 arg2: &C::Arg2<'a>,
559 arg3: &C::Arg3<'a>,
560 arg4: &C::Arg4<'a>,
561 arg5: &C::Arg5<'a>,
562 ) -> Result<(), EbpfError> {
563 let arg_types = [
564 arg1.get_value_type(),
565 arg2.get_value_type(),
566 arg3.get_value_type(),
567 arg4.get_value_type(),
568 arg5.get_value_type(),
569 ];
570 for i in 0..5 {
571 let verified_type = self.types.get(i).unwrap_or(&Type::UNINITIALIZED);
572 if !&arg_types[i].is_subtype(verified_type) {
573 return Err(EbpfError::ProgramLinkError(format!(
574 "Type of argument {} doesn't match. Verified type: {:?}. Value type: {:?}",
575 i + 1,
576 verified_type,
577 arg_types[i],
578 )));
579 }
580 }
581
582 Ok(())
583 }
584}
585
586pub struct EbpfProgram<C: EbpfProgramContext, T: ArgumentTypeChecker<C> = StaticTypeChecker> {
588 pub(crate) code: Vec<EbpfInstruction>,
589
590 #[allow(dead_code)]
593 pub(crate) maps: Vec<C::Map>,
594
595 type_checker: T,
596}
597
598impl<C: EbpfProgramContext, T: ArgumentTypeChecker<C>> std::fmt::Debug for EbpfProgram<C, T> {
599 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
600 f.debug_struct("EbpfProgram").field("code", &self.code).finish()
601 }
602}
603
604impl<C: EbpfProgramContext, T: ArgumentTypeChecker<C>> EbpfProgram<C, T> {
605 pub fn code(&self) -> &[EbpfInstruction] {
606 &self.code[..]
607 }
608}
609
610impl<C, T: ArgumentTypeChecker<C>> EbpfProgram<C, T>
611where
612 C: StaticHelperSet,
613 C: for<'a> EbpfProgramContext<Arg2<'a> = (), Arg3<'a> = (), Arg4<'a> = (), Arg5<'a> = ()>,
614{
615 pub fn run_with_1_argument<'a>(
616 &self,
617 run_context: &mut C::RunContext<'a>,
618 arg1: C::Arg1<'a>,
619 ) -> u64 {
620 self.type_checker
621 .run_time_check(&arg1, &(), &(), &(), &())
622 .expect("Failed argument type check");
623 execute(&self.code[..], C::helpers(), run_context, &[arg1.into()])
624 }
625}
626
627impl<C, T: ArgumentTypeChecker<C>> EbpfProgram<C, T>
628where
629 C: StaticHelperSet,
630 C: for<'a> EbpfProgramContext<Arg3<'a> = (), Arg4<'a> = (), Arg5<'a> = ()>,
631{
632 pub fn run_with_2_arguments<'a>(
633 &self,
634 run_context: &mut C::RunContext<'a>,
635 arg1: C::Arg1<'a>,
636 arg2: C::Arg2<'a>,
637 ) -> u64 {
638 self.type_checker
639 .run_time_check(&arg1, &arg2, &(), &(), &())
640 .expect("Failed argument type check");
641 execute(&self.code[..], C::helpers(), run_context, &[arg1.into(), arg2.into()])
642 }
643}
644
645impl<C: BpfProgramContext, T: ArgumentTypeChecker<C>> EbpfProgram<C, T>
646where
647 C: BpfProgramContext + StaticHelperSet,
648 C: for<'a> EbpfProgramContext<Arg2<'a> = (), Arg3<'a> = (), Arg4<'a> = (), Arg5<'a> = ()>,
649{
650 pub fn run<'a>(
654 &self,
655 run_context: &mut <C as EbpfProgramContext>::RunContext<'a>,
656 packet: <C as EbpfProgramContext>::Arg1<'a>,
657 ) -> u64 {
658 self.run_with_1_argument(run_context, packet)
659 }
660}
661
662pub fn link_program_internal<C, T>(
665 program: &VerifiedEbpfProgram,
666 maps: Vec<C::Map>,
667) -> Result<EbpfProgram<C, T>, EbpfError>
668where
669 C: EbpfProgramContext + StaticHelperSet,
670 T: ArgumentTypeChecker<C>,
671{
672 let type_checker = T::link(program)?;
673
674 let mut code = program.code.clone();
675 let struct_mappings = C::struct_mappings();
676
677 for StructAccess { pc, memory_id, field_offset, is_32_bit_ptr_load } in
679 program.struct_access_instructions.iter()
680 {
681 let field_mapping =
682 struct_mappings.iter().find(|m| m.memory_id == *memory_id).and_then(|struct_map| {
683 struct_map.fields.iter().find(|m| m.source_offset == *field_offset)
684 });
685
686 if let Some(field_mapping) = field_mapping {
687 let instruction = &mut code[*pc];
688
689 let offset_diff = i16::try_from(
693 i64::try_from(field_mapping.target_offset).unwrap()
694 - i64::try_from(field_mapping.source_offset).unwrap(),
695 )
696 .unwrap();
697
698 let new_offset = instruction.offset().checked_add(offset_diff).ok_or_else(|| {
699 EbpfError::ProgramLinkError(format!("Struct field offset overflow at PC {}", *pc))
700 })?;
701 instruction.set_offset(new_offset);
702
703 if *is_32_bit_ptr_load {
705 instruction.set_code((instruction.code() & !BPF_SIZE_MASK) | BPF_DW);
706 }
707 } else {
708 if *is_32_bit_ptr_load {
709 return Err(EbpfError::ProgramLinkError(format!(
710 "32-bit field isn't mapped at pc {}",
711 *pc,
712 )));
713 }
714 }
715 }
716
717 let helpers = C::helpers();
718
719 for pc in 0..code.len() {
720 let instruction = &mut code[pc];
721
722 if instruction.code() == (BPF_JMP | BPF_CALL) {
724 assert!(instruction.src_reg() == 0);
725 let helper_id = instruction.imm() as u32;
726 let Some(index) = helpers.get_index_by_id(helper_id) else {
727 return Err(EbpfError::ProgramLinkError(format!(
728 "Missing implementation for helper with id={}",
729 helper_id,
730 )));
731 };
732 instruction.set_imm(index as i32);
733 }
734
735 if instruction.code() == BPF_LDDW {
737 match instruction.src_reg() {
740 0 => (),
741 BPF_PSEUDO_MAP_IDX | BPF_PSEUDO_MAP_IDX_VALUE => {
742 let map_index = usize::try_from(instruction.imm())
743 .expect("negative map index in a verified program");
744 let map = maps.get(map_index).ok_or_else(|| {
745 EbpfError::ProgramLinkError(format!("Invalid map_index: {}", map_index))
746 })?;
747 assert!(*map.schema() == program.maps[map_index]);
748
749 let (instruction, next_instruction) = {
750 let (a1, a2) = code.split_at_mut(pc + 1);
752 (&mut a1[pc], &mut a2[0])
753 };
754
755 let value = if instruction.src_reg() == BPF_PSEUDO_MAP_IDX {
756 map.as_bpf_value().as_u64()
757 } else {
758 let map_value = map
760 .get_data_ptr()
761 .ok_or_else(|| {
762 EbpfError::ProgramLinkError(format!(
763 "Unable to get value at 0 for map at index {map_index}"
764 ))
765 })?
766 .as_u64();
767 map_value.checked_add_signed(next_instruction.imm().into()).ok_or_else(
768 || {
769 EbpfError::ProgramLinkError(format!(
770 "Unable to use offset for map access"
771 ))
772 },
773 )?
774 };
775
776 let (high, low) = ((value >> 32) as i32, value as i32);
777 instruction.set_src_reg(0);
778 instruction.set_imm(low);
779 next_instruction.set_imm(high);
780 }
781 value => {
782 return Err(EbpfError::ProgramLinkError(format!(
783 "Unsupported value for src_reg in lddw: {}",
784 value,
785 )));
786 }
787 }
788 }
789 }
790
791 Ok(EbpfProgram { code, maps, type_checker })
792}
793
794pub fn link_program<C>(
797 program: &VerifiedEbpfProgram,
798 maps: Vec<C::Map>,
799) -> Result<EbpfProgram<C>, EbpfError>
800where
801 C: EbpfProgramContext + StaticHelperSet,
802{
803 link_program_internal::<C, StaticTypeChecker>(program, maps)
804}
805
806pub fn link_program_dynamic<C>(
808 program: &VerifiedEbpfProgram,
809 maps: Vec<C::Map>,
810) -> Result<EbpfProgram<C, DynamicTypeChecker>, EbpfError>
811where
812 C: EbpfProgramContext + StaticHelperSet,
813{
814 link_program_internal::<C, DynamicTypeChecker>(program, maps)
815}
816
817#[macro_export]
818macro_rules! static_helper_set {
819 ($context:ty, $value:expr) => {
820 impl $crate::StaticHelperSet for $context {
821 fn helpers() -> &'static $crate::HelperSet<$context> {
822 static HELPERS: std::sync::LazyLock<$crate::HelperSet<$context>> =
823 std::sync::LazyLock::new(|| $value);
824 &HELPERS
825 }
826 }
827 };
828}
829
830#[macro_export]
831macro_rules! empty_static_helper_set {
832 ($context:ty) => {
833 $crate::static_helper_set!($context, $crate::HelperSet::default());
834 };
835}
836
837#[cfg(test)]
838mod test {
839 use super::*;
840 use crate::api::*;
841 use crate::conformance::test::parse_asm;
842 use crate::{
843 CallingContext, FieldDescriptor, FieldMapping, FieldType, NullVerifierLogger,
844 ProgramArgument, StructDescriptor, Type, verify_program,
845 };
846 use std::mem::offset_of;
847 use std::sync::{Arc, LazyLock};
848 use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
849
850 #[repr(C)]
851 #[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
852 struct TestArgument {
853 pub read_only_field: u32,
855 pub _padding1: u32,
856 pub data: u64,
858 pub data_end: u64,
860 pub mutable_field: u32,
862 pub _padding2: u32,
863 }
864
865 static TEST_ARG_TYPE: LazyLock<Type> = LazyLock::new(|| {
866 let data_memory_id = MemoryId::new();
867 let descriptor = Arc::new(StructDescriptor {
868 fields: vec![
869 FieldDescriptor {
870 offset: offset_of!(TestArgument, read_only_field),
871 field_type: FieldType::Scalar { size: 4 },
872 },
873 FieldDescriptor {
874 offset: offset_of!(TestArgument, data),
875 field_type: FieldType::PtrToArray {
876 is_32_bit: false,
877 id: data_memory_id.clone(),
878 },
879 },
880 FieldDescriptor {
881 offset: offset_of!(TestArgument, data_end),
882 field_type: FieldType::PtrToEndArray {
883 is_32_bit: false,
884 id: data_memory_id.clone(),
885 },
886 },
887 FieldDescriptor {
888 offset: offset_of!(TestArgument, mutable_field),
889 field_type: FieldType::MutableScalar { size: 4 },
890 },
891 ],
892 });
893
894 Type::PtrToStruct { id: MemoryId::new(), offset: 0.into(), descriptor }
895 });
896
897 impl Default for TestArgument {
898 fn default() -> Self {
899 Self {
900 read_only_field: 1,
901 _padding1: 0,
902 data: 0,
903 data_end: 0,
904 mutable_field: 2,
905 _padding2: 0,
906 }
907 }
908 }
909
910 impl TestArgument {
911 fn from_data(data: &[u64]) -> Self {
912 let ptr_range = data.as_ptr_range();
913 Self {
914 data: ptr_range.start as u64,
915 data_end: ptr_range.end as u64,
916 ..Default::default()
917 }
918 }
919 }
920
921 impl ProgramArgument for &'_ mut TestArgument {
922 fn get_type() -> &'static Type {
923 &*TEST_ARG_TYPE
924 }
925 }
926
927 #[repr(C)]
930 struct TestArgument32 {
931 pub read_only_field: u32,
932 pub data: u32,
933 pub data_end: u32,
934 pub mutable_field: u32,
935 }
936
937 #[repr(C)]
938 struct TestArgument32BitMapped(TestArgument);
939
940 static TEST_ARG_32_BIT_MEMORY_ID: LazyLock<MemoryId> = LazyLock::new(|| MemoryId::new());
941 static TEST_ARG_32_BIT_TYPE: LazyLock<Type> = LazyLock::new(|| {
942 let data_memory_id = MemoryId::new();
943 let descriptor = Arc::new(StructDescriptor {
944 fields: vec![
945 FieldDescriptor {
946 offset: offset_of!(TestArgument32, read_only_field),
947 field_type: FieldType::Scalar { size: 4 },
948 },
949 FieldDescriptor {
950 offset: offset_of!(TestArgument32, data),
951 field_type: FieldType::PtrToArray {
952 is_32_bit: true,
953 id: data_memory_id.clone(),
954 },
955 },
956 FieldDescriptor {
957 offset: offset_of!(TestArgument32, data_end),
958 field_type: FieldType::PtrToEndArray {
959 is_32_bit: true,
960 id: data_memory_id.clone(),
961 },
962 },
963 FieldDescriptor {
964 offset: offset_of!(TestArgument32, mutable_field),
965 field_type: FieldType::MutableScalar { size: 4 },
966 },
967 ],
968 });
969
970 Type::PtrToStruct { id: TEST_ARG_32_BIT_MEMORY_ID.clone(), offset: 0.into(), descriptor }
971 });
972
973 impl ProgramArgument for &'_ TestArgument32BitMapped {
974 fn get_type() -> &'static Type {
975 &*TEST_ARG_32_BIT_TYPE
976 }
977
978 fn field_mappings() -> &'static [FieldMapping] {
979 static FIELD_MAPPINGS: [FieldMapping; 3] = [
980 FieldMapping {
981 source_offset: offset_of!(TestArgument32, data),
982 target_offset: offset_of!(TestArgument, data),
983 },
984 FieldMapping {
985 source_offset: offset_of!(TestArgument32, data_end),
986 target_offset: offset_of!(TestArgument, data_end),
987 },
988 FieldMapping {
989 source_offset: offset_of!(TestArgument32, mutable_field),
990 target_offset: offset_of!(TestArgument, mutable_field),
991 },
992 ];
993 &FIELD_MAPPINGS
994 }
995 }
996
997 struct TestEbpfProgramContext {}
998
999 impl EbpfProgramContext for TestEbpfProgramContext {
1000 type RunContext<'a> = ();
1001
1002 type Packet<'a> = ();
1003 type Arg1<'a> = &'a mut TestArgument;
1004 type Arg2<'a> = ();
1005 type Arg3<'a> = ();
1006 type Arg4<'a> = ();
1007 type Arg5<'a> = ();
1008
1009 type Map = NoMap;
1010 }
1011
1012 empty_static_helper_set!(TestEbpfProgramContext);
1013
1014 fn initialize_test_program(
1015 code: Vec<EbpfInstruction>,
1016 ) -> Result<EbpfProgram<TestEbpfProgramContext>, EbpfError> {
1017 let verified_program = verify_program(
1018 code,
1019 CallingContext { args: vec![TEST_ARG_TYPE.clone()], ..Default::default() },
1020 &mut NullVerifierLogger,
1021 )?;
1022 link_program(&verified_program, vec![])
1023 }
1024
1025 struct TestEbpfProgramContext32BitMapped {}
1026
1027 impl EbpfProgramContext for TestEbpfProgramContext32BitMapped {
1028 type RunContext<'a> = ();
1029
1030 type Packet<'a> = ();
1031 type Arg1<'a> = &'a TestArgument32BitMapped;
1032 type Arg2<'a> = ();
1033 type Arg3<'a> = ();
1034 type Arg4<'a> = ();
1035 type Arg5<'a> = ();
1036
1037 type Map = NoMap;
1038 }
1039
1040 empty_static_helper_set!(TestEbpfProgramContext32BitMapped);
1041
1042 fn initialize_test_program_for_32bit_arg(
1043 code: Vec<EbpfInstruction>,
1044 ) -> Result<EbpfProgram<TestEbpfProgramContext32BitMapped>, EbpfError> {
1045 let verified_program = verify_program(
1046 code,
1047 CallingContext { args: vec![TEST_ARG_32_BIT_TYPE.clone()], ..Default::default() },
1048 &mut NullVerifierLogger,
1049 )?;
1050 link_program(&verified_program, vec![])
1051 }
1052
1053 #[test]
1054 fn test_data_end() {
1055 let program = r#"
1056 mov %r0, 0
1057 ldxdw %r2, [%r1+16]
1058 ldxdw %r1, [%r1+8]
1059 # ensure data contains at least 8 bytes
1060 mov %r3, %r1
1061 add %r3, 0x8
1062 jgt %r3, %r2, +1
1063 # read 8 bytes from data
1064 ldxdw %r0, [%r1]
1065 exit
1066 "#;
1067 let program = initialize_test_program(parse_asm(program)).expect("load");
1068
1069 let v = [42];
1070 let mut data = TestArgument::from_data(&v[..]);
1071 assert_eq!(program.run_with_1_argument(&mut (), &mut data), v[0]);
1072 }
1073
1074 #[test]
1075 fn test_past_data_end() {
1076 let program = r#"
1077 mov %r0, 0
1078 ldxdw %r2, [%r1+16]
1079 ldxdw %r1, [%r1+6]
1080 # ensure data contains at least 4 bytes
1081 mov %r3, %r1
1082 add %r3, 0x4
1083 jgt %r3, %r2, +1
1084 # read 8 bytes from data
1085 ldxdw %r0, [%r1]
1086 exit
1087 "#;
1088 initialize_test_program(parse_asm(program)).expect_err("incorrect program");
1089 }
1090
1091 #[test]
1092 fn test_mapping() {
1093 let program = r#"
1094 # Return `TestArgument32.mutable_field`
1095 ldxw %r0, [%r1+12]
1096 exit
1097 "#;
1098 let program = initialize_test_program_for_32bit_arg(parse_asm(program)).expect("load");
1099
1100 let mut data = TestArgument32BitMapped(TestArgument::default());
1101 assert_eq!(program.run_with_1_argument(&mut (), &mut data), data.0.mutable_field as u64);
1102 }
1103
1104 #[test]
1105 fn test_mapping_partial_load() {
1106 let program = r#"
1108 # Returns two upper bytes of `TestArgument32.mutable_filed`
1109 ldxh %r0, [%r1+14]
1110 exit
1111 "#;
1112 let program = initialize_test_program_for_32bit_arg(parse_asm(program)).expect("load");
1113
1114 let mut data = TestArgument32BitMapped(TestArgument::default());
1115 data.0.mutable_field = 0x12345678;
1116 assert_eq!(program.run_with_1_argument(&mut (), &mut data), 0x1234 as u64);
1117 }
1118
1119 #[test]
1120 fn test_mapping_ptr() {
1121 let program = r#"
1122 mov %r0, 0
1123 # Load data and data_end as 32 bits pointers in TestArgument32
1124 ldxw %r2, [%r1+8]
1125 ldxw %r1, [%r1+4]
1126 # ensure data contains at least 8 bytes
1127 mov %r3, %r1
1128 add %r3, 0x8
1129 jgt %r3, %r2, +1
1130 # read 8 bytes from data
1131 ldxdw %r0, [%r1]
1132 exit
1133 "#;
1134 let program = initialize_test_program_for_32bit_arg(parse_asm(program)).expect("load");
1135
1136 let v = [42];
1137 let mut data = TestArgument32BitMapped(TestArgument::from_data(&v[..]));
1138 assert_eq!(program.run_with_1_argument(&mut (), &mut data), v[0]);
1139 }
1140
1141 #[test]
1142 fn test_mapping_with_offset() {
1143 let program = r#"
1144 mov %r0, 0
1145 add %r1, 0x8
1146 # Load data and data_end as 32 bits pointers in TestArgument32
1147 ldxw %r2, [%r1]
1148 ldxw %r1, [%r1-4]
1149 # ensure data contains at least 8 bytes
1150 mov %r3, %r1
1151 add %r3, 0x8
1152 jgt %r3, %r2, +1
1153 # read 8 bytes from data
1154 ldxdw %r0, [%r1]
1155 exit
1156 "#;
1157 let program = initialize_test_program_for_32bit_arg(parse_asm(program)).expect("load");
1158
1159 let v = [42];
1160 let mut data = TestArgument32BitMapped(TestArgument::from_data(&v[..]));
1161 assert_eq!(program.run_with_1_argument(&mut (), &mut data), v[0]);
1162 }
1163
1164 #[test]
1165 fn test_ptr_diff() {
1166 let program = r#"
1167 mov %r0, %r1
1168 add %r0, 0x2
1169 # Substract 2 ptr to memory
1170 sub %r0, %r1
1171
1172 mov %r2, %r10
1173 add %r2, 0x3
1174 # Substract 2 ptr to stack
1175 sub %r2, %r10
1176 add %r0, %r2
1177
1178 ldxdw %r2, [%r1+16]
1179 ldxdw %r1, [%r1+8]
1180 # Substract ptr to array and ptr to array end
1181 sub %r2, %r1
1182 add %r0, %r2
1183
1184 mov %r2, %r1
1185 add %r2, 0x4
1186 # Substract 2 ptr to array
1187 sub %r2, %r1
1188 add %r0, %r2
1189
1190 exit
1191 "#;
1192 let code = parse_asm(program);
1193
1194 let program = initialize_test_program(code).expect("load");
1195
1196 let v = [42];
1197 let mut data = TestArgument::from_data(&v[..]);
1198 assert_eq!(program.run_with_1_argument(&mut (), &mut data), 17);
1199 }
1200
1201 #[test]
1202 fn test_invalid_packet_load() {
1203 let program = r#"
1204 mov %r6, %r2
1205 mov %r0, 0
1206 ldpw
1207 exit
1208 "#;
1209 let args = vec![
1210 Type::PtrToMemory { id: MemoryId::new(), offset: 0.into(), buffer_size: 16 },
1211 Type::PtrToMemory { id: MemoryId::new(), offset: 0.into(), buffer_size: 16 },
1212 ];
1213 let verify_result = verify_program(
1214 parse_asm(program),
1215 CallingContext { args, ..Default::default() },
1216 &mut NullVerifierLogger,
1217 );
1218
1219 assert_eq!(
1220 verify_result.expect_err("validation should fail"),
1221 EbpfError::ProgramVerifyError("at PC 2: R6 is not a packet".to_string())
1222 );
1223 }
1224
1225 #[test]
1226 fn test_invalid_field_size() {
1227 let program = r#"
1229 ldxdw %r0, [%r1]
1230 exit
1231 "#;
1232 initialize_test_program(parse_asm(program)).expect_err("incorrect program");
1233 }
1234
1235 #[test]
1236 fn test_unknown_field() {
1237 let program = r#"
1239 ldxw %r0, [%r1 + 4]
1240 exit
1241 "#;
1242 initialize_test_program(parse_asm(program)).expect_err("incorrect program");
1243 }
1244
1245 #[test]
1246 fn test_partial_ptr_field() {
1247 let program = r#"
1249 ldxw %r0, [%r1 + 8]
1250 exit
1251 "#;
1252 initialize_test_program(parse_asm(program)).expect_err("incorrect program");
1253 }
1254
1255 #[test]
1256 fn test_readonly_field() {
1257 let program = r#"
1259 stw [%r1], 0x42
1260 exit
1261 "#;
1262 initialize_test_program(parse_asm(program)).expect_err("incorrect program");
1263 }
1264
1265 #[test]
1266 fn test_store_mutable_field() {
1267 let program = r#"
1269 stw [%r1 + 24], 0x42
1270 mov %r0, 1
1271 exit
1272 "#;
1273 let program = initialize_test_program(parse_asm(program)).expect("load");
1274
1275 let mut data = TestArgument::default();
1276 assert_eq!(program.run_with_1_argument(&mut (), &mut data), 1);
1277 assert_eq!(data.mutable_field, 0x42);
1278 }
1279
1280 #[test]
1281 fn test_fake_array_bounds_check() {
1282 let program = r#"
1285 mov %r0, 0
1286 ldxdw %r2, [%r1+16]
1287 ldxdw %r1, [%r1+8]
1288 # Subtract 8 from `data` and pretend checking array bounds.
1289 mov %r3, %r1
1290 sub %r3, 0x8
1291 jgt %r3, %r2, +1
1292 # Read 8 bytes from `data`. This should be rejected by the verifier.
1293 ldxdw %r0, [%r1]
1294 exit
1295 "#;
1296 initialize_test_program(parse_asm(program)).expect_err("incorrect program");
1297 }
1298}