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