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