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, 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
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
110/// A strong reference to an eBPF map held for the lifetime of an eBPF linked
111/// with the map. Can be converted to `BpfValue`, which is used by the program
112/// to identify the map when it calls map helpers.
113pub trait MapReference {
114    fn schema(&self) -> &MapSchema;
115    fn as_bpf_value(&self) -> BpfValue;
116
117    /// Returns the address of the first element of this map.
118    ///
119    /// This will only ever be called on a map of type `BPF_MAP_TYPE_ARRAY` with at least one
120    /// element.
121    fn get_data_ptr(&self) -> Option<BpfValue>;
122}
123
124/// `MapReference` for `EbpfProgramContext` where maps are not used.
125pub 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    /// Returns the set of helpers available to the program.
141    fn helpers() -> &'static HelperSet<Self>;
142}
143
144pub trait EbpfProgramContext: 'static + Sized {
145    /// Context for an invocation of an eBPF program.
146    type RunContext<'a>;
147
148    /// Packet used by the program.
149    type Packet<'a>: Packet + FromBpfValue<Self::RunContext<'a>>;
150
151    /// Arguments passed to the program
152    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 used to reference eBPF maps for the lifetime of a program.
159    type Map: MapReference;
160
161    /// Returns the set of struct mappings that should be applied when linking a program. The
162    /// default implementation collects mappings from all arguments.
163    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
184/// Trait that should be implemented by packets passed to eBPF programs.
185pub 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
195/// Simple `Packet` implementation for packets that can be accessed directly.
196impl<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
212/// A context for a BPF program that's compatible with eBPF and cBPF.
213pub 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/// Stores set of helpers for an eBPF program. The helpers are stored packed
395/// in a vector for fast access in the interpreter.
396#[derive(Derivative)]
397#[derivative(Clone(bound = ""), Default(bound = ""))]
398pub struct HelperSet<C: EbpfProgramContext> {
399    helpers: Vec<EbpfHelperImpl<C>>,
400
401    // Maps helper IDs used in eBPF to location of the helper in `helpers`.
402    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    /// Looks up a helper by ID, returns helper `index`.
427    pub fn get_index_by_id(&self, id: u32) -> Option<u32> {
428        self.indices.get(&id).cloned()
429    }
430
431    /// Returns the helper with the specified index.
432    pub fn get_by_index(&self, index: u32) -> Option<&EbpfHelperImpl<C>> {
433        self.helpers.get(index as usize)
434    }
435}
436
437/// A mapping for a field in a struct where the original ebpf program knows a different offset and
438/// data size than the one it receives from the kernel.
439#[derive(Clone, Copy, Debug, PartialEq)]
440pub struct FieldMapping {
441    /// The offset of the field as known by the original ebpf program.
442    pub source_offset: usize,
443    /// The actual offset of the field in the data provided by the kernel.
444    pub target_offset: usize,
445}
446
447pub type FieldMappings = smallvec::SmallVec<[FieldMapping; 2]>;
448
449#[derive(Clone, Debug)]
450pub struct StructMapping {
451    /// Memory ID used in the struct definition.
452    pub memory_id: MemoryId,
453
454    /// The list of mappings in the buffer. The verifier must rewrite the actual ebpf to ensure
455    /// the right offset and operand are use to access the mapped fields. Mappings are allowed
456    /// only for pointer fields.
457    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        // No-op since argument types were checked in `link()`.
509        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
553/// An abstraction over an eBPF program and its registered helper functions.
554pub struct EbpfProgram<C: EbpfProgramContext, T: ArgumentTypeChecker<C> = StaticTypeChecker> {
555    pub(crate) code: Vec<EbpfInstruction>,
556
557    /// List of references to the maps used by the program. This field is not used directly,
558    /// but it's kept here to ensure that the maps outlive the program.
559    #[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    /// Executes the current program on the specified `packet`.
618    /// The program receives a pointer to the `packet` and the size of the packet as the first
619    /// two arguments.
620    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
629/// Rewrites the code to ensure mapped fields are correctly handled. Returns
630/// runnable `EbpfProgram<C>`.
631pub 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    // Update offsets in the instructions that access structs.
645    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            // Note that `instruction.off` may be different from `field.source_offset`. It's adjuststed
657            // by the difference between `target_offset` and `source_offset` to ensure the instructions
658            // will access the right field.
659            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            // 32-bit pointer loads must be updated to 64-bit loads.
671            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        // Replace helper IDs with helper indices in call instructions.
690        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        // Link maps.
703        if instruction.code() == BPF_LDDW {
704            // If the instruction references BPF_PSEUDO_MAP_FD, then we need to look up the map fd
705            // and create a reference from this program to that object.
706            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                        // The code was verified, so this is not expected to overflow.
718                        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                        // BPF_PSEUDO_MAP_IDX_VALUE
726                        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
761/// Rewrites the code to ensure mapped fields are correctly handled. Returns
762/// runnable `EbpfProgram<C>`.
763pub 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
773/// Same as above, but allows to check argument types in runtime instead of in link time.
774pub 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        // A field that should not be writable by the program.
821        pub read_only_field: u32,
822        pub _padding1: u32,
823        /// Pointer to an array.
824        pub data: u64,
825        /// End of the array.
826        pub data_end: u64,
827        // A field that can be updated by the program.
828        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    // A version of TestArgument with 32-bit remapped pointers. It's used to define struct layout
895    // for eBPF programs, but not used in the Rust code directly.
896    #[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        // Verify that we can access middle of a remapped scalar field.
1074        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        // Load with a field size too large fails validation.
1195        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        // Load outside of the know fields fails validation.
1205        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        // Partial loads of ptr fields are not allowed.
1215        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        // Store to a read only field fails validation.
1225        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        // Store to a mutable field is allowed.
1235        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        // Verify that negative offsets in memory ptrs are handled properly and cannot be used to
1250        // bypass array bounds checks.
1251        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}