Skip to main content

ebpf/
program.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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
18/// Trait that should be implemented for arguments passed to eBPF programs.
19pub trait ProgramArgument: Into<BpfValue> {
20    /// Returns eBPF type that corresponds to `Self`. Used when program argument types
21    /// are checked statically.
22    fn get_type() -> &'static Type;
23
24    /// Returns eBPF type for a specific value of `Self`. For most types this is the
25    /// same type that's returned by `get_type()`, but that's not always the case.
26    /// In particular for scalar values this will return `Type::ScalarValue` with
27    /// the actual value of the scalar and with `unknown_mask = 0`.
28    fn get_value_type(&self) -> Type {
29        Self::get_type().clone()
30    }
31
32    /// Returns the list of field mappings that should be applied with this argument.
33    /// If not empty then `get_type()` must be a `PtrToStruct`.
34    fn field_mappings() -> &'static [FieldMapping] {
35        static NO_MAPPINGS: [FieldMapping; 0] = [];
36        &NO_MAPPINGS
37    }
38
39    /// Returns the `StructMapping` that should be applied with this argument. Implementations
40    /// should override `field_mappings()` and keep default implementation of `struct_mapping()`.
41    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
56/// Trait that should be implemented for types that can be converted from `BpfValue`.
57/// Used to get a `Packet` when loading a value from the packet.
58pub trait FromBpfValue<C>: Sized {
59    /// # Safety
60    /// Should be called only by the eBPF interpreter when executing verified eBPF code.
61    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
86/// Implements `ProgramArgument` for `EbpfPtr` when it's implemented for
87/// `&'a mut T`.
88impl<'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
129/// A strong reference to an eBPF map held for the lifetime of an eBPF linked
130/// with the map. Can be converted to `BpfValue`, which is used by the program
131/// to identify the map when it calls map helpers.
132pub trait MapReference {
133    fn schema(&self) -> &MapSchema;
134    fn as_bpf_value(&self) -> BpfValue;
135
136    /// Returns the address of the first element of this map.
137    ///
138    /// This will only ever be called on a map of type `BPF_MAP_TYPE_ARRAY` with at least one
139    /// element.
140    fn get_data_ptr(&self) -> Option<BpfValue>;
141}
142
143/// `MapReference` for `EbpfProgramContext` where maps are not used.
144pub 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    /// Returns the set of helpers available to the program.
160    fn helpers() -> &'static HelperSet<Self>;
161}
162
163pub trait EbpfProgramContext: 'static + Sized {
164    /// Context for an invocation of an eBPF program.
165    type RunContext<'a>;
166
167    /// Packet used by the program.
168    type Packet<'a>: Packet + FromBpfValue<Self::RunContext<'a>>;
169
170    /// Arguments passed to the program
171    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 used to reference eBPF maps for the lifetime of a program.
178    type Map: MapReference;
179
180    /// Returns the set of struct mappings that should be applied when linking a program. The
181    /// default implementation collects mappings from all arguments.
182    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
203/// Trait that should be implemented by packets passed to eBPF programs.
204pub 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
214/// Simple `Packet` implementation for packets that can be accessed directly.
215impl<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
231/// A context for a BPF program that's compatible with eBPF and cBPF.
232pub 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/// Stores set of helpers for an eBPF program. The helpers are stored packed
421/// in a vector for fast access in the interpreter.
422#[derive(Derivative)]
423#[derivative(Clone(bound = ""), Default(bound = ""))]
424pub struct HelperSet<C: EbpfProgramContext> {
425    helpers: Vec<EbpfHelperImpl<C>>,
426
427    // Maps helper IDs used in eBPF to location of the helper in `helpers`.
428    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    /// Looks up a helper by ID, returns helper `index`.
453    pub fn get_index_by_id(&self, id: u32) -> Option<u32> {
454        self.indices.get(&id).cloned()
455    }
456
457    /// Returns the helper with the specified index.
458    pub fn get_by_index(&self, index: u32) -> Option<&EbpfHelperImpl<C>> {
459        self.helpers.get(index as usize)
460    }
461}
462
463/// A mapping for a field in a struct where the original ebpf program knows a different offset and
464/// data size than the one it receives from the kernel.
465#[derive(Clone, Copy, Debug, PartialEq)]
466pub struct FieldMapping {
467    /// The offset of the field as known by the original ebpf program.
468    pub source_offset: usize,
469    /// The actual offset of the field in the data provided by the kernel.
470    pub target_offset: usize,
471}
472
473pub type FieldMappings = smallvec::SmallVec<[FieldMapping; 2]>;
474
475#[derive(Clone, Debug)]
476pub struct StructMapping {
477    /// Memory ID used in the struct definition.
478    pub memory_id: MemoryId,
479
480    /// The list of mappings in the buffer. The verifier must rewrite the actual ebpf to ensure
481    /// the right offset and operand are use to access the mapped fields. Mappings are allowed
482    /// only for pointer fields.
483    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        // No-op since argument types were checked in `link()`.
535        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
579/// An abstraction over an eBPF program and its registered helper functions.
580pub struct EbpfProgram<C: EbpfProgramContext, T: ArgumentTypeChecker<C> = StaticTypeChecker> {
581    pub(crate) code: Vec<EbpfInstruction>,
582
583    /// List of references to the maps used by the program. This field is not used directly,
584    /// but it's kept here to ensure that the maps outlive the program.
585    #[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    /// Executes the current program on the specified `packet`.
644    /// The program receives a pointer to the `packet` and the size of the packet as the first
645    /// two arguments.
646    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
655/// Rewrites the code to ensure mapped fields are correctly handled. Returns
656/// runnable `EbpfProgram<C>`.
657pub 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    // Update offsets in the instructions that access structs.
671    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            // Note that `instruction.off` may be different from `field.source_offset`. It's adjuststed
683            // by the difference between `target_offset` and `source_offset` to ensure the instructions
684            // will access the right field.
685            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            // 32-bit pointer loads must be updated to 64-bit loads.
697            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        // Replace helper IDs with helper indices in call instructions.
716        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        // Link maps.
729        if instruction.code() == BPF_LDDW {
730            // If the instruction references BPF_PSEUDO_MAP_FD, then we need to look up the map fd
731            // and create a reference from this program to that object.
732            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                        // The code was verified, so this is not expected to overflow.
744                        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                        // BPF_PSEUDO_MAP_IDX_VALUE
752                        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
787/// Rewrites the code to ensure mapped fields are correctly handled. Returns
788/// runnable `EbpfProgram<C>`.
789pub 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
799/// Same as above, but allows to check argument types in runtime instead of in link time.
800pub 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        // A field that should not be writable by the program.
847        pub read_only_field: u32,
848        pub _padding1: u32,
849        /// Pointer to an array.
850        pub data: u64,
851        /// End of the array.
852        pub data_end: u64,
853        // A field that can be updated by the program.
854        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    // A version of TestArgument with 32-bit remapped pointers. It's used to define struct layout
921    // for eBPF programs, but not used in the Rust code directly.
922    #[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        // Verify that we can access middle of a remapped scalar field.
1100        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        // Load with a field size too large fails validation.
1221        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        // Load outside of the know fields fails validation.
1231        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        // Partial loads of ptr fields are not allowed.
1241        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        // Store to a read only field fails validation.
1251        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        // Store to a mutable field is allowed.
1261        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        // Verify that negative offsets in memory ptrs are handled properly and cannot be used to
1276        // bypass array bounds checks.
1277        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}