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