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