Skip to main content

ebpf/
verifier.rs

1// Copyright 2024 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
5#![allow(unused_variables)]
6
7use crate::scalar_value::{ScalarValueData, U32Range, U32ScalarValueData, U64Range};
8use crate::visitor::{BpfVisitor, ProgramCounter, Register, Source};
9use crate::{
10    BPF_LDDW, BPF_MAX_INSTS, BPF_PSEUDO_MAP_IDX, BPF_PSEUDO_MAP_IDX_VALUE, BPF_STACK_SIZE,
11    DataWidth, EbpfError, EbpfInstruction, GENERAL_REGISTER_COUNT, MapSchema, REGISTER_COUNT,
12};
13use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian};
14use fuchsia_sync::Mutex;
15use linux_uapi::{bpf_map_type, bpf_map_type_BPF_MAP_TYPE_ARRAY};
16use std::cmp::Ordering;
17use std::collections::{BTreeMap, HashMap, HashSet};
18use std::sync::Arc;
19use zerocopy::IntoBytes;
20
21const U32_MAX: u64 = u32::MAX as u64;
22
23/// A trait to receive the log from the verifier.
24pub trait VerifierLogger {
25    /// Log a line. The line is always a correct encoded ASCII string.
26    fn log(&mut self, line: &[u8]);
27}
28
29/// A `VerifierLogger` that drops all its content.
30pub struct NullVerifierLogger;
31
32impl VerifierLogger for NullVerifierLogger {
33    fn log(&mut self, line: &[u8]) {
34        debug_assert!(line.is_ascii());
35    }
36}
37
38/// An identifier for a memory buffer accessible by an ebpf program. The identifiers are built as a
39/// chain of unique identifier so that a buffer can contain multiple pointers to the same type and
40/// the verifier can distinguish between the different instances.
41/// The namespace of a `MemoryId`, used to prevent collisions between different
42/// sources of identifiers.
43#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Ord, PartialOrd)]
44pub enum Namespace {
45    /// For static structure identifiers (e.g., `SkBuff`, `BpfSock`) defined in `program_type.rs`.
46    Hardcoded,
47    /// For IDs generated by `MemoryId::new()` using the global counter.
48    Generated,
49    /// For IDs generated by `VerificationContext::next_id()` using the local counter.
50    Verification,
51}
52
53#[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
54pub struct MemoryId {
55    namespace: Namespace,
56    id: u64,
57    parent: Option<Box<MemoryId>>,
58}
59
60impl MemoryId {
61    pub fn id(&self) -> u64 {
62        self.id
63    }
64}
65
66impl From<u64> for MemoryId {
67    fn from(id: u64) -> Self {
68        Self { namespace: Namespace::Hardcoded, id, parent: None }
69    }
70}
71
72/// A counter that allows to generate new ids for parameters. The namespace is the same as for id
73/// generated for types while verifying an ebpf program. Because different sources of identifiers
74/// use different namespaces, this counter can start at 0.
75static BPF_TYPE_IDENTIFIER_COUNTER: std::sync::atomic::AtomicU64 =
76    std::sync::atomic::AtomicU64::new(0);
77
78impl MemoryId {
79    /// Build a `MemoryId` from a raw u64.
80    ///
81    /// This is supposed to be used by the `ebpf_api` crate to build static identifiers (e.g.
82    /// for hardcoded program contexts like `SkBuff`), but not elsewhere.
83    pub const fn from_raw(id: u64) -> MemoryId {
84        Self { namespace: Namespace::Hardcoded, id, parent: None }
85    }
86
87    pub fn new() -> MemoryId {
88        Self {
89            namespace: Namespace::Generated,
90            id: BPF_TYPE_IDENTIFIER_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
91            parent: None,
92        }
93    }
94
95    /// Build a new id such that `other` is prepended to the chain of parent of `self`.
96    fn prepended(&self, other: MemoryId) -> Self {
97        match &self.parent {
98            None => {
99                MemoryId { namespace: self.namespace, id: self.id, parent: Some(Box::new(other)) }
100            }
101            Some(parent) => MemoryId {
102                namespace: self.namespace,
103                id: self.id,
104                parent: Some(Box::new(parent.prepended(other))),
105            },
106        }
107    }
108
109    /// Returns whether the parent of `self` is `parent`
110    fn has_parent(&self, parent: &MemoryId) -> bool {
111        match &self.parent {
112            None => false,
113            Some(p) => p.as_ref() == parent,
114        }
115    }
116
117    /// Returns whether `self` and `other` represent the same type. This is true if both have the
118    /// same id and if they both have parent, parents must also match.
119    fn matches(&self, other: &MemoryId) -> bool {
120        if self.namespace != other.namespace || self.id != other.id {
121            return false;
122        };
123        match (&self.parent, &other.parent) {
124            (Some(p1), Some(p2)) => p1.matches(p2.as_ref()),
125            _ => true,
126        }
127    }
128}
129
130/// The type of a field in a struct pointed by `Type::PtrToStruct`.
131#[derive(Clone, Debug, PartialEq)]
132pub enum FieldType {
133    /// Read-only scalar value.
134    Scalar { size: usize },
135
136    /// Mutable scalar value.
137    MutableScalar { size: usize },
138
139    /// A pointer to the kernel memory. The full buffer is `buffer_size` bytes long.
140    PtrToMemory { is_32_bit: bool, id: MemoryId, buffer_size: usize },
141
142    /// A nullable pointer to the kernel memory. The full buffer is `buffer_size` bytes long.
143    NullablePtrToMemory { is_32_bit: bool, id: MemoryId, buffer_size: usize },
144
145    /// A pointer to the kernel memory. The full buffer size is determined by an instance of
146    /// `PtrToEndArray` with the same `id`.
147    PtrToArray { is_32_bit: bool, id: MemoryId },
148
149    /// A pointer to the kernel memory that represents the first non accessible byte of a
150    /// `PtrToArray` with the same `id`.
151    PtrToEndArray { is_32_bit: bool, id: MemoryId },
152}
153
154/// Definition of a field in a `Type::PtrToStruct`.
155#[derive(Clone, Debug, PartialEq)]
156pub struct FieldDescriptor {
157    /// The offset at which the field is located.
158    pub offset: usize,
159
160    /// The type of the pointed memory. Currently the verifier supports only `PtrToArray`,
161    /// `PtrToStruct`, `PtrToEndArray` and `ScalarValue`.
162    pub field_type: FieldType,
163}
164
165impl FieldDescriptor {
166    fn size(&self) -> usize {
167        match self.field_type {
168            FieldType::Scalar { size } | FieldType::MutableScalar { size } => size,
169            FieldType::PtrToMemory { is_32_bit, .. }
170            | FieldType::NullablePtrToMemory { is_32_bit, .. }
171            | FieldType::PtrToArray { is_32_bit, .. }
172            | FieldType::PtrToEndArray { is_32_bit, .. } => {
173                if is_32_bit {
174                    4
175                } else {
176                    8
177                }
178            }
179        }
180    }
181}
182
183/// The offset and width of a field in a struct.
184#[derive(Clone, Copy, Debug, Eq, PartialEq)]
185struct Field {
186    offset: i16,
187    width: DataWidth,
188}
189
190impl Field {
191    fn new(offset: i16, width: DataWidth) -> Self {
192        Self { offset, width }
193    }
194}
195
196/// Defines field layout in a struct pointed by `Type::PtrToStruct`.
197#[derive(Debug, PartialEq, Default)]
198pub struct StructDescriptor {
199    /// The list of fields.
200    pub fields: Vec<FieldDescriptor>,
201}
202
203impl StructDescriptor {
204    /// Return true if `self` is a subtype of `super_struct`.
205    fn is_subtype(&self, super_struct: &StructDescriptor) -> bool {
206        // Check that `super_struct.fields` is a subset of `fields`.
207        for super_field in super_struct.fields.iter() {
208            if self.fields.iter().find(|field| *field == super_field).is_none() {
209                return false;
210            }
211        }
212        true
213    }
214
215    /// Finds the field type for load/store at the specified location. None is returned if the
216    /// access is invalid and the program must be rejected.
217    fn find_field(&self, base_offset: ScalarValueData, field: Field) -> Option<&FieldDescriptor> {
218        let offset = base_offset + field.offset;
219        let field_desc = self.fields.iter().find(|f| {
220            f.offset <= offset.min() as usize && (offset.max() as usize) < f.offset + f.size()
221        })?;
222        let is_valid_load = match field_desc.field_type {
223            FieldType::Scalar { size } | FieldType::MutableScalar { size } => {
224                // For scalars check that the access is within the bounds of the field.
225                ((offset + field.width.bytes()).max() as usize) <= field_desc.offset + size
226            }
227            FieldType::PtrToMemory { is_32_bit, .. }
228            | FieldType::NullablePtrToMemory { is_32_bit, .. }
229            | FieldType::PtrToArray { is_32_bit, .. }
230            | FieldType::PtrToEndArray { is_32_bit, .. } => {
231                let expected_width = if is_32_bit { DataWidth::U32 } else { DataWidth::U64 };
232                // Pointer loads are expected to load the whole field.
233                offset.is_known()
234                    && offset.value as usize == field_desc.offset
235                    && field.width == expected_width
236            }
237        };
238
239        is_valid_load.then_some(field_desc)
240    }
241}
242
243#[derive(Clone, Debug, PartialEq)]
244pub enum MemoryParameterSize {
245    /// The memory buffer have the given size.
246    Value(u64),
247    /// The memory buffer size is given by the parameter in the given index.
248    Reference { index: u8 },
249}
250
251impl MemoryParameterSize {
252    fn size(&self, context: &ComputationContext) -> Result<u64, String> {
253        match self {
254            Self::Value(size) => Ok(*size),
255            Self::Reference { index } => {
256                let size_type = context.reg(index + 1)?;
257                match size_type {
258                    Type::ScalarValue(data) if data.is_known() => Ok(data.value),
259                    _ => Err("cannot know buffer size".to_string()),
260                }
261            }
262        }
263    }
264}
265
266#[derive(Clone, Debug, PartialEq)]
267pub enum MapTypeFilter {
268    AllowList(&'static [bpf_map_type]),
269    DenyList(&'static [bpf_map_type]),
270}
271
272impl MapTypeFilter {
273    pub fn is_allowed(&self, map_type: bpf_map_type) -> bool {
274        match self {
275            MapTypeFilter::AllowList(types) => types.contains(&map_type),
276            MapTypeFilter::DenyList(types) => !types.contains(&map_type),
277        }
278    }
279}
280
281#[derive(Clone, Debug, PartialEq)]
282pub enum Type {
283    /// A number.
284    ScalarValue(ScalarValueData),
285    /// A pointer to a map object.
286    ConstPtrToMap { id: u64, schema: MapSchema },
287    /// A pointer into the stack.
288    PtrToStack { offset: StackOffset },
289    /// A pointer to the kernel memory. The full buffer is `buffer_size` bytes long. The pointer is
290    /// situated at `offset` from the start of the buffer.
291    PtrToMemory { id: MemoryId, offset: ScalarValueData, buffer_size: u64 },
292    /// A pointer to a struct with the specified `StructDescriptor`.
293    PtrToStruct { id: MemoryId, offset: ScalarValueData, descriptor: Arc<StructDescriptor> },
294    /// A pointer to the kernel memory. The full buffer size is determined by an instance of
295    /// `PtrToEndArray` with the same `id`. The pointer is situadted at `offset` from the start of
296    /// the buffer.
297    PtrToArray { id: MemoryId, offset: ScalarValueData },
298    /// A pointer to the kernel memory that represents the first non accessible byte of a
299    /// `PtrToArray` with the same `id`.
300    PtrToEndArray { id: MemoryId },
301    /// A pointer that might be null and must be validated before being referenced.
302    NullOr { id: MemoryId, inner: Box<Type> },
303    /// An object that must be passed to a method with an associated `ReleaseParameter` before the
304    /// end of the program.
305    Releasable { id: MemoryId, inner: Box<Type> },
306    /// A function parameter that must be a `ScalarValue` when called.
307    ScalarValueParameter,
308    /// A function parameter that must be a `ConstPtrToMap` when called.
309    ConstPtrToMapParameter { filter: MapTypeFilter },
310    /// A function parameter that must be a key of a map.
311    MapKeyParameter {
312        /// The index in the arguments list that contains a `ConstPtrToMap` for the map this key is
313        /// associated with.
314        map_ptr_index: u8,
315    },
316    /// A function parameter that must be a value of a map.
317    MapValueParameter {
318        /// The index in the arguments list that contains a `ConstPtrToMap` for the map this key is
319        /// associated with.
320        map_ptr_index: u8,
321    },
322    /// A function parameter that must be a pointer to memory.
323    MemoryParameter {
324        /// The index in the arguments list that contains a scalar value containing the size of the
325        /// memory.
326        size: MemoryParameterSize,
327        /// Whether this memory is read by the function.
328        input: bool,
329        /// Whether this memory is written by the function.
330        output: bool,
331    },
332    /// A function return value that is the same type as a parameter.
333    AliasParameter {
334        /// The index in the argument list of the parameter that has the type of this return value.
335        parameter_index: u8,
336    },
337    /// A function return value that is either null, or the given type.
338    NullOrParameter(Box<Type>),
339    /// A function parameter that must be a pointer to memory with the given id.
340    StructParameter { id: MemoryId },
341    /// The parameter should be the same as the specified argument passed to the program.
342    ContextParameter { parameter_index: u8 },
343    /// A function return value that must be passed to a method with an associated
344    /// `ReleaseParameter` before the end of the program.
345    ReleasableParameter { id: MemoryId, inner: Box<Type> },
346    /// A function parameter that will release the value.
347    ReleaseParameter { id: MemoryId },
348    /// A function parameter that is not checked.
349    AnyParameter,
350}
351
352/// Defines a partial ordering on `Type` instances, capturing the notion of how "broad"
353/// a type is in terms of the set of potential values it represents.
354///
355/// The ordering is defined such that `t1 > t2` if a proof that an eBPF program terminates
356/// in a state where a register or memory location has type `t1` is also a proof that
357/// the program terminates in a state where that location has type `t2`.
358///
359/// In other words, a "broader" type represents a larger set of possible values, and
360/// proving termination with a broader type implies termination with any narrower type.
361///
362/// Examples:
363/// * `Type::ScalarValue(ScalarValueData({ unknown_mask: 0, .. }))` (a known scalar value) is less than
364///   `Type::ScalarValue(ScalarValueData({ unknown_mask: u64::MAX, .. }))` (an unknown scalar value).
365impl PartialOrd for Type {
366    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
367        // If the values are equals, return the known result.
368        if self == other {
369            return Some(Ordering::Equal);
370        }
371
372        // If one value is not initialized, the types are ordered.
373        // UNINITIALIZED is treated as the Top element (Greater than all other types).
374        // If a path is proven safe when a register is UNINITIALIZED, it means the path
375        // does not read that register. Consequently, it is safe to prune any state
376        // that has a more specific type (like a pointer) in that register, because
377        // the program will still not read it.
378        if self == &Type::UNINITIALIZED {
379            return Some(Ordering::Greater);
380        }
381        if other == &Type::UNINITIALIZED {
382            return Some(Ordering::Less);
383        }
384
385        // Otherwise, only scalars are comparables.
386        match (self, other) {
387            (Self::ScalarValue(data1), Self::ScalarValue(data2)) => data1.partial_cmp(data2),
388            _ => None,
389        }
390    }
391}
392
393impl From<ScalarValueData> for Type {
394    fn from(value: ScalarValueData) -> Self {
395        Self::ScalarValue(value)
396    }
397}
398
399impl From<u64> for Type {
400    fn from(value: u64) -> Self {
401        Self::ScalarValue(value.into())
402    }
403}
404
405impl Default for Type {
406    /// A new instance of `Type` where no bit has been written yet.
407    fn default() -> Self {
408        Self::UNINITIALIZED.clone()
409    }
410}
411
412impl Type {
413    /// An uninitialized value.
414    pub const UNINITIALIZED: Self = Self::ScalarValue(ScalarValueData::UNINITIALIZED);
415
416    /// A `Type` where the data is usable by userspace, but the value is not known by the verifier.
417    pub const UNKNOWN_SCALAR: Self = Self::ScalarValue(ScalarValueData::UNKNOWN_WRITTEN);
418
419    /// The mask associated with a data of size `width`.
420    fn mask(width: DataWidth) -> u64 {
421        if width == DataWidth::U64 { u64::MAX } else { (1 << width.bits()) - 1 }
422    }
423
424    /// Return true if `self` is a scalar with all bytes initialized.
425    fn is_written_scalar(&self) -> bool {
426        match self {
427            Self::ScalarValue(data) if data.is_fully_initialized() => true,
428            _ => false,
429        }
430    }
431
432    /// Return true if `self` has all bytes initialized.
433    fn is_initialized(&self) -> bool {
434        match self {
435            Self::ScalarValue(data) => data.is_fully_initialized(),
436            _ => true,
437        }
438    }
439
440    /// Return true if `self` is a subtype of `super_type`.
441    pub fn is_subtype(&self, super_type: &Type) -> bool {
442        match (self, super_type) {
443            // Anything can be used in place of an uninitialized value.
444            (_, Self::ScalarValue(data)) if data.is_uninitialized() => true,
445
446            (
447                Self::PtrToStruct { id: id1, offset: offset1, descriptor: descriptor1 },
448                Self::PtrToStruct { id: id2, offset: offset2, descriptor: descriptor2 },
449            ) => id1 == id2 && offset1 <= offset2 && descriptor1.is_subtype(descriptor2),
450
451            // Every type is a subtype of itself.
452            (self_type, super_type) if self_type == super_type => true,
453
454            _ => false,
455        }
456    }
457
458    /// Return true is `self` is guaranteed to be non-zero
459    pub fn is_non_zero(&self) -> bool {
460        match self {
461            Self::ScalarValue(d) => d.min() > 0,
462            Self::NullOr { .. } => false,
463            _ => true,
464        }
465    }
466
467    fn inner(&self, context: &ComputationContext) -> Result<&Type, String> {
468        match self {
469            Self::Releasable { id, inner } => {
470                if context.resources.contains(id) {
471                    Ok(&inner)
472                } else {
473                    Err("Access to released resource".to_string())
474                }
475            }
476            _ => Ok(self),
477        }
478    }
479
480    /// Constraints `type1` and `type2` for a conditional jump with the specified `jump_type` and
481    /// `jump_width`.
482    fn constraint(
483        context: &mut ComputationContext,
484        jump_type: JumpType,
485        jump_width: JumpWidth,
486        type1: Self,
487        type2: Self,
488    ) -> Result<(Self, Self), String> {
489        let result = match (jump_width, jump_type, type1.inner(context)?, type2.inner(context)?) {
490            (JumpWidth::W64, JumpType::Eq, Type::ScalarValue(data1), Type::ScalarValue(data2))
491                if data1.is_fully_initialized() && data2.is_fully_initialized() =>
492            {
493                let umin = std::cmp::max(data1.min(), data2.min());
494                let umax = std::cmp::min(data1.max(), data2.max());
495                let v = Type::ScalarValue(ScalarValueData::new(
496                    data1.value | data2.value,
497                    data1.unknown_mask & data2.unknown_mask,
498                    0,
499                    U64Range::new(umin, umax),
500                ));
501                (v.clone(), v)
502            }
503            (JumpWidth::W32, JumpType::Eq, Type::ScalarValue(data1), Type::ScalarValue(data2))
504                if data1.is_fully_initialized() && data2.is_fully_initialized() =>
505            {
506                let maybe_umin = if data1.min() <= U32_MAX && data2.min() <= U32_MAX {
507                    Some(std::cmp::max(data1.min(), data2.min()))
508                } else {
509                    None
510                };
511                let maybe_umax = if data1.max() <= U32_MAX && data2.max() <= U32_MAX {
512                    Some(std::cmp::min(data1.max(), data2.max()))
513                } else {
514                    None
515                };
516
517                let urange1 = U64Range::new(
518                    maybe_umin.unwrap_or_else(|| data1.min()),
519                    maybe_umax.unwrap_or_else(|| data1.max()),
520                );
521                let v1 = Type::ScalarValue(ScalarValueData::new(
522                    data1.value | (data2.value & U32_MAX),
523                    data1.unknown_mask & (data2.unknown_mask | (U32_MAX << 32)),
524                    0,
525                    urange1,
526                ));
527
528                let urange2 = U64Range::new(
529                    maybe_umin.unwrap_or_else(|| data2.min()),
530                    maybe_umax.unwrap_or_else(|| data2.max()),
531                );
532                let v2 = Type::ScalarValue(ScalarValueData::new(
533                    data2.value | (data1.value & U32_MAX),
534                    data2.unknown_mask & (data1.unknown_mask | (U32_MAX << 32)),
535                    0,
536                    urange2,
537                ));
538                (v1, v2)
539            }
540            (JumpWidth::W64, JumpType::Eq, Type::ScalarValue(data), Type::NullOr { id, .. })
541            | (JumpWidth::W64, JumpType::Eq, Type::NullOr { id, .. }, Type::ScalarValue(data))
542                if data.is_zero() =>
543            {
544                context.set_null(id, true);
545                let zero = Type::from(0);
546                (zero.clone(), zero)
547            }
548            (JumpWidth::W64, jump_type, Type::NullOr { id, inner }, Type::ScalarValue(data))
549                if jump_type.is_strict() && data.is_zero() =>
550            {
551                context.set_null(id, false);
552                let inner = *inner.clone();
553                inner.register_resource(context);
554                (inner, type2)
555            }
556            (JumpWidth::W64, jump_type, Type::ScalarValue(data), Type::NullOr { id, inner })
557                if jump_type.is_strict() && data.is_zero() =>
558            {
559                context.set_null(id, false);
560                let inner = *inner.clone();
561                inner.register_resource(context);
562                (type1, inner)
563            }
564
565            (JumpWidth::W64, JumpType::Lt, Type::ScalarValue(lhs), Type::ScalarValue(rhs)) => {
566                debug_assert!(lhs.min() < u64::MAX);
567                debug_assert!(rhs.max() > 0);
568                let new_max_lhs = std::cmp::min(lhs.max(), rhs.max() - 1);
569                debug_assert!(lhs.min() <= new_max_lhs);
570                let new_min_rhs = std::cmp::max(rhs.min(), lhs.min() + 1);
571                debug_assert!(new_min_rhs <= rhs.max());
572                let new_range_lhs = U64Range::new(lhs.min(), new_max_lhs);
573                let new_range_rhs = U64Range::new(new_min_rhs, rhs.max());
574                (lhs.update_range(new_range_lhs).into(), rhs.update_range(new_range_rhs).into())
575            }
576            (JumpWidth::W64, JumpType::Gt, Type::ScalarValue(lhs), Type::ScalarValue(rhs)) => {
577                debug_assert!(rhs.min() < u64::MAX);
578                debug_assert!(lhs.max() > 0);
579                let new_min_lhs = std::cmp::max(lhs.min(), rhs.min() + 1);
580                debug_assert!(new_min_lhs <= lhs.max());
581                let new_max_rhs = std::cmp::min(rhs.max(), lhs.max() - 1);
582                debug_assert!(rhs.min() <= new_max_rhs);
583                let new_range_lhs = U64Range::new(new_min_lhs, lhs.max());
584                let new_range_rhs = U64Range::new(rhs.min(), new_max_rhs);
585                (lhs.update_range(new_range_lhs).into(), rhs.update_range(new_range_rhs).into())
586            }
587
588            (JumpWidth::W64, JumpType::Le, Type::ScalarValue(lhs), Type::ScalarValue(rhs)) => {
589                let new_max_lhs = std::cmp::min(lhs.max(), rhs.max());
590                debug_assert!(lhs.min() <= new_max_lhs);
591                let new_min_rhs = std::cmp::max(rhs.min(), lhs.min());
592                debug_assert!(new_min_rhs <= rhs.max());
593                let new_range_lhs = U64Range::new(lhs.min(), new_max_lhs);
594                let new_range_rhs = U64Range::new(new_min_rhs, rhs.max());
595                (lhs.update_range(new_range_lhs).into(), rhs.update_range(new_range_rhs).into())
596            }
597            (JumpWidth::W64, JumpType::Ge, Type::ScalarValue(lhs), Type::ScalarValue(rhs)) => {
598                let new_min_lhs = std::cmp::max(lhs.min(), rhs.min());
599                debug_assert!(new_min_lhs <= lhs.max());
600                let new_max_rhs = std::cmp::min(rhs.max(), lhs.max());
601                debug_assert!(rhs.min() <= new_max_rhs);
602                let new_range_lhs = U64Range::new(new_min_lhs, lhs.max());
603                let new_range_rhs = U64Range::new(rhs.min(), new_max_rhs);
604                (lhs.update_range(new_range_lhs).into(), rhs.update_range(new_range_rhs).into())
605            }
606
607            (
608                JumpWidth::W64,
609                JumpType::Eq,
610                Type::PtrToArray { id: id1, offset },
611                Type::PtrToEndArray { id: id2 },
612            )
613            | (
614                JumpWidth::W64,
615                JumpType::Le,
616                Type::PtrToArray { id: id1, offset },
617                Type::PtrToEndArray { id: id2 },
618            )
619            | (
620                JumpWidth::W64,
621                JumpType::Ge,
622                Type::PtrToEndArray { id: id1 },
623                Type::PtrToArray { id: id2, offset },
624            ) if id1 == id2 => {
625                context.update_array_bounds(id1.clone(), *offset);
626                (type1, type2)
627            }
628            (
629                JumpWidth::W64,
630                JumpType::Lt,
631                Type::PtrToArray { id: id1, offset },
632                Type::PtrToEndArray { id: id2 },
633            )
634            | (
635                JumpWidth::W64,
636                JumpType::Gt,
637                Type::PtrToEndArray { id: id1 },
638                Type::PtrToArray { id: id2, offset },
639            ) if id1 == id2 => {
640                context.update_array_bounds(id1.clone(), *offset + 1);
641                (type1, type2)
642            }
643            (JumpWidth::W64, JumpType::Eq, _, _) => (type1.clone(), type1),
644            _ => (type1, type2),
645        };
646        Ok(result)
647    }
648
649    fn match_parameter_type(
650        &self,
651        verification_context: &VerificationContext<'_>,
652        context: &ComputationContext,
653        helper_name: &str,
654        parameter_type: &Type,
655        index: usize,
656        next: &mut ComputationContext,
657    ) -> Result<(), String> {
658        match (parameter_type, self) {
659            (Type::NullOrParameter(t), Type::ScalarValue(data))
660                if data.is_known() && data.value == 0 =>
661            {
662                Ok(())
663            }
664            (Type::NullOrParameter(t), _) => self.match_parameter_type(
665                verification_context,
666                context,
667                helper_name,
668                t,
669                index,
670                next,
671            ),
672            (Type::ScalarValueParameter, Type::ScalarValue(data))
673                if data.is_fully_initialized() =>
674            {
675                Ok(())
676            }
677            (Type::ConstPtrToMapParameter { filter }, Type::ConstPtrToMap { schema, .. }) => {
678                let map_type = schema.map_type;
679                if !filter.is_allowed(map_type) {
680                    return Err(format!("Map type {map_type} not allowed in {helper_name}"));
681                }
682                Ok(())
683            }
684            (
685                Type::MapKeyParameter { map_ptr_index },
686                Type::PtrToMemory { offset, buffer_size, .. },
687            ) => {
688                let schema = context.get_map_schema(*map_ptr_index)?;
689                context.check_memory_access(*offset, *buffer_size, 0, schema.key_size as usize)
690            }
691            (Type::MapKeyParameter { map_ptr_index }, Type::PtrToStack { offset }) => {
692                let schema = context.get_map_schema(*map_ptr_index)?;
693                context.stack.read_data_ptr(context.pc, *offset, schema.key_size as u64)
694            }
695            (
696                Type::MapValueParameter { map_ptr_index },
697                Type::PtrToMemory { offset, buffer_size, .. },
698            ) => {
699                let schema = context.get_map_schema(*map_ptr_index)?;
700                context.check_memory_access(*offset, *buffer_size, 0, schema.value_size as usize)
701            }
702            (Type::MapValueParameter { map_ptr_index }, Type::PtrToStack { offset }) => {
703                let schema = context.get_map_schema(*map_ptr_index)?;
704                context.stack.read_data_ptr(context.pc, *offset, schema.value_size as u64)
705            }
706            (Type::MemoryParameter { size, .. }, Type::PtrToMemory { offset, buffer_size, .. }) => {
707                let expected_size = size.size(context)?;
708                let offset_max = offset.max();
709                if offset_max > *buffer_size {
710                    return Err("out of bound read".to_string());
711                }
712                let size_left = *buffer_size - offset_max;
713                if expected_size > size_left {
714                    return Err("out of bound read".to_string());
715                }
716                Ok(())
717            }
718
719            (Type::MemoryParameter { size, input, output }, Type::PtrToStack { offset }) => {
720                let size = size.size(context)?;
721                let buffer_end = offset.add(size);
722                if !buffer_end.is_within_stack() {
723                    Err("out of bound access".to_string())
724                } else {
725                    if *output {
726                        next.stack.write_data_ptr(context.pc, *offset, size)?;
727                    }
728                    if *input {
729                        context.stack.read_data_ptr(context.pc, *offset, size)?;
730                    }
731                    Ok(())
732                }
733            }
734            (
735                Type::StructParameter { id: id1 },
736                Type::PtrToMemory { id: id2, offset, .. }
737                | Type::PtrToStruct { id: id2, offset, .. },
738            ) if offset.is_zero() && id1.matches(id2) => Ok(()),
739            (
740                Type::ReleasableParameter { id: id1, inner: inner1 },
741                Type::Releasable { id: id2, inner: inner2 },
742            ) if id2.has_parent(id1) => {
743                if next.resources.contains(id2) {
744                    inner2.match_parameter_type(
745                        verification_context,
746                        context,
747                        helper_name,
748                        inner1,
749                        index,
750                        next,
751                    )
752                } else {
753                    Err(format!("Resource already released for index {index}"))
754                }
755            }
756            (Type::ContextParameter { parameter_index }, arg_type) => {
757                if verification_context.calling_context.args.get(*parameter_index as usize)
758                    == Some(arg_type)
759                {
760                    Ok(())
761                } else {
762                    Err(format!("Helper expects program argument {parameter_index}"))
763                }
764            }
765            (Type::ReleaseParameter { id: id1 }, Type::Releasable { id: id2, .. })
766                if id2.has_parent(id1) =>
767            {
768                if next.resources.remove(id2) {
769                    Ok(())
770                } else {
771                    Err(format!("{id2:?} Resource already released for index {index}"))
772                }
773            }
774            (_, Type::Releasable { inner, .. }) => inner.match_parameter_type(
775                verification_context,
776                context,
777                helper_name,
778                parameter_type,
779                index,
780                next,
781            ),
782            (Type::AnyParameter, _) => Ok(()),
783
784            _ => Err(format!("incorrect parameter for index {index}")),
785        }
786    }
787
788    /// If this `Type` is an instance of NullOr with the given `null_id`, replace it wither either
789    /// 0 or the subtype depending on `is_null`
790    fn set_null(&mut self, null_id: &MemoryId, is_null: bool) {
791        match self {
792            Type::NullOr { id, inner } if id == null_id => {
793                if is_null {
794                    *self = Type::from(0);
795                } else {
796                    *self = *inner.clone();
797                }
798            }
799            _ => {}
800        }
801    }
802
803    fn register_resource(&self, context: &mut ComputationContext) {
804        match self {
805            Type::Releasable { id, .. } => {
806                context.resources.insert(id.clone());
807            }
808            _ => {}
809        }
810    }
811
812    /// Partially Compares two iterators of comparable items.
813    ///
814    /// This function iterates through both input iterators simultaneously and compares the corresponding elements.
815    /// The comparison continues until:
816    /// 1. Both iterators are exhausted and all elements were considered equal, in which case it returns `Some(Ordering::Equal)`.
817    /// 2. All pairs of corresponding elements that are not equal have the same ordering (`Ordering::Less` or `Ordering::Greater`), in which case it returns `Some(Ordering)` reflecting that consistent ordering.
818    /// 3. One iterator is exhausted before the other, or any comparison between elements yields `None`, or not all non-equal pairs have the same ordering, in which case it returns `None`.
819    fn compare_list<'a>(
820        mut l1: impl Iterator<Item = &'a Self>,
821        mut l2: impl Iterator<Item = &'a Self>,
822    ) -> Option<Ordering> {
823        let mut result = Ordering::Equal;
824        loop {
825            match (l1.next(), l2.next()) {
826                (None, None) => return Some(result),
827                (_, None) | (None, _) => return None,
828                (Some(v1), Some(v2)) => {
829                    result = associate_orderings(result, v1.partial_cmp(v2)?)?;
830                }
831            }
832        }
833    }
834}
835
836#[derive(Clone, Debug)]
837pub struct FunctionSignature {
838    pub args: Vec<Type>,
839    pub return_value: Type,
840    pub invalidate_array_bounds: bool,
841}
842
843#[derive(Clone, Debug)]
844pub struct HelperDefinition {
845    pub index: u32,
846    pub name: &'static str,
847    pub signature: FunctionSignature,
848}
849
850#[derive(Debug, Default)]
851pub struct CallingContext {
852    /// List of map schemas of the associated map. The maps can be accessed using LDDW instruction
853    /// with `src_reg=BPF_PSEUDO_MAP_IDX`.
854    pub maps: Vec<MapSchema>,
855    /// The registered external functions.
856    pub helpers: HashMap<u32, &'static HelperDefinition>,
857    /// The args of the program.
858    pub args: Vec<Type>,
859    /// Packet type. Normally it should be either `None` or `args[0]`.
860    pub packet_type: Option<Type>,
861}
862
863impl CallingContext {
864    pub fn register_map(&mut self, schema: MapSchema) -> usize {
865        let index = self.maps.len();
866        self.maps.push(schema);
867        index
868    }
869}
870
871#[derive(Debug, PartialEq, Clone)]
872pub struct StructAccess {
873    pub pc: ProgramCounter,
874
875    // Memory Id of the struct being accessed.
876    pub memory_id: MemoryId,
877
878    // Offset of the field being loaded.
879    pub field_offset: usize,
880
881    // Indicates that this is a 32-bit pointer load. These instructions must be remapped.
882    pub is_32_bit_ptr_load: bool,
883}
884
885#[derive(Debug, Clone)]
886pub struct VerifiedEbpfProgram {
887    pub(crate) code: Vec<EbpfInstruction>,
888    pub(crate) args: Vec<Type>,
889    pub(crate) struct_access_instructions: Vec<StructAccess>,
890    pub(crate) maps: Vec<MapSchema>,
891}
892
893impl VerifiedEbpfProgram {
894    // Convert the program to raw code. Can be used only when the program doesn't access any
895    // structs and maps.
896    pub fn to_code(self) -> Vec<EbpfInstruction> {
897        debug_assert!(self.struct_access_instructions.is_empty());
898        debug_assert!(self.maps.is_empty());
899        self.code
900    }
901
902    pub fn code(&self) -> &[EbpfInstruction] {
903        &self.code
904    }
905
906    pub fn struct_access_instructions(&self) -> &[StructAccess] {
907        &self.struct_access_instructions
908    }
909
910    pub fn from_verified_code(
911        code: Vec<EbpfInstruction>,
912        args: Vec<Type>,
913        struct_access_instructions: Vec<StructAccess>,
914        maps: Vec<MapSchema>,
915    ) -> Self {
916        Self { code, args, struct_access_instructions, maps }
917    }
918
919    pub fn maps(&self) -> &[MapSchema] {
920        &self.maps
921    }
922}
923
924/// Verify the given code depending on the type of the parameters and the registered external
925/// functions. Returned `VerifiedEbpfProgram` should be linked in order to execute it.
926pub fn verify_program(
927    code: Vec<EbpfInstruction>,
928    calling_context: CallingContext,
929    logger: &mut dyn VerifierLogger,
930) -> Result<VerifiedEbpfProgram, EbpfError> {
931    if code.len() > BPF_MAX_INSTS {
932        return error_and_log(logger, "ebpf program too long");
933    }
934    // Pre-scan the program to ensure all LDDW instructions are structurally well-formed,
935    // even if they are in unreachable code. This is necessary because:
936    // 1. The linker may rewrite LDDW instructions (e.g., to resolve map pointers) and
937    //    expects them to be well-formed.
938    // 2. It ensures that the second part of every LDDW has a `code` of 0 (invalid opcode).
939    //    This guarantees that any attempt to jump directly into the second part of an
940    //    LDDW will be rejected during verification as an invalid instruction.
941    let mut scan_pc = 0;
942    while scan_pc < code.len() {
943        let inst = &code[scan_pc];
944        if inst.code() == BPF_LDDW {
945            let Some(next_instruction) = code.get(scan_pc + 1) else {
946                return error_and_log(logger, "incomplete lddw");
947            };
948            if next_instruction.code() != 0
949                || next_instruction.offset() != 0
950                || next_instruction.src_reg() != 0
951                || next_instruction.dst_reg() != 0
952            {
953                return error_and_log(logger, "invalid lddw");
954            }
955            scan_pc += 2;
956        } else {
957            scan_pc += 1;
958        }
959    }
960
961    let mut context = ComputationContext::default();
962    for (i, t) in calling_context.args.iter().enumerate() {
963        // The parameter registers are r1 to r5.
964        context.set_reg((i + 1) as u8, t.clone()).map_err(EbpfError::ProgramVerifyError)?;
965    }
966    let states = vec![context];
967    let mut verification_context = VerificationContext {
968        calling_context,
969        logger,
970        states,
971        code: &code,
972        counter: 0,
973        iteration: 0,
974        terminating_contexts: Default::default(),
975        struct_access_instructions: Default::default(),
976    };
977    while let Some(mut context) = verification_context.states.pop() {
978        if let Some(terminating_contexts) =
979            verification_context.terminating_contexts.get(&context.pc)
980        {
981            // Check whether there exist a context that terminate and prove that this context does
982            // also terminate.
983            if let Some(ending_context) =
984                terminating_contexts.iter().find(|c| c.computation_context >= context)
985            {
986                // One such context has been found, this proves the current context terminates.
987                // If the context has a parent, register the data dependencies and try to terminate
988                // it.
989                if let Some(parent) = context.parent.take() {
990                    parent.dependencies.lock().push(ending_context.dependencies.clone());
991                    if let Some(parent) = Arc::into_inner(parent) {
992                        parent
993                            .terminate(&mut verification_context)
994                            .map_err(EbpfError::ProgramVerifyError)?;
995                    }
996                }
997                continue;
998            }
999        }
1000        if verification_context.iteration > 10 * BPF_MAX_INSTS {
1001            return error_and_log(verification_context.logger, "bpf byte code does not terminate");
1002        }
1003        if context.pc >= code.len() {
1004            return error_and_log(verification_context.logger, "pc out of bounds");
1005        }
1006        let visit_result = context.visit(&mut verification_context, code[context.pc]);
1007        match visit_result {
1008            Err(message) => {
1009                let message = format!("at PC {}: {}", context.pc, message);
1010                return error_and_log(verification_context.logger, message);
1011            }
1012            _ => {}
1013        }
1014        verification_context.iteration += 1;
1015    }
1016
1017    let struct_access_instructions =
1018        verification_context.struct_access_instructions.into_values().collect::<Vec<_>>();
1019    let CallingContext { maps, args, .. } = verification_context.calling_context;
1020    Ok(VerifiedEbpfProgram { code, struct_access_instructions, maps, args })
1021}
1022
1023struct VerificationContext<'a> {
1024    /// The type information for the program arguments and the registered functions.
1025    calling_context: CallingContext,
1026    /// The logger to use.
1027    logger: &'a mut dyn VerifierLogger,
1028    /// The `ComputationContext` yet to be validated.
1029    states: Vec<ComputationContext>,
1030    /// The program being analyzed.
1031    code: &'a [EbpfInstruction],
1032    /// A counter used to generated unique ids for memory buffers and maps.
1033    counter: u64,
1034    /// The current iteration of the verifier. Used to ensure termination by limiting the number of
1035    /// iteration before bailing out.
1036    iteration: usize,
1037    /// Keep track of the context that terminates at a given pc. The list of context will all be
1038    /// incomparables as each time a bigger context is computed, the smaller ones are removed from
1039    /// the list.
1040    terminating_contexts: BTreeMap<ProgramCounter, Vec<TerminatingContext>>,
1041    /// The current set of struct access instructions that will need to be updated when the
1042    /// program is linked. This is also used to ensure that a given instruction always loads the
1043    /// same field. If this is not the case, the verifier will reject the program.
1044    struct_access_instructions: HashMap<ProgramCounter, StructAccess>,
1045}
1046
1047impl<'a> VerificationContext<'a> {
1048    fn next_id(&mut self) -> MemoryId {
1049        let id = self.counter;
1050        self.counter += 1;
1051        MemoryId { namespace: Namespace::Verification, id, parent: None }
1052    }
1053
1054    /// Register an instruction that loads or stores a struct field. These instructions will need
1055    /// to updated later when the program is linked.
1056    fn register_struct_access(&mut self, struct_access: StructAccess) -> Result<(), String> {
1057        match self.struct_access_instructions.entry(struct_access.pc) {
1058            std::collections::hash_map::Entry::Vacant(entry) => {
1059                entry.insert(struct_access);
1060            }
1061            std::collections::hash_map::Entry::Occupied(entry) => {
1062                if *entry.get() != struct_access {
1063                    return Err("Inconsistent struct field access".to_string());
1064                }
1065            }
1066        }
1067        Ok(())
1068    }
1069}
1070
1071const STACK_ELEMENT_SIZE: usize = std::mem::size_of::<u64>();
1072
1073/// An offset inside the stack. The offset is from the end of the stack.
1074/// downward.
1075#[derive(Clone, Copy, Debug, PartialEq)]
1076pub struct StackOffset(ScalarValueData);
1077
1078impl Default for StackOffset {
1079    fn default() -> Self {
1080        Self(BPF_STACK_SIZE.into())
1081    }
1082}
1083
1084impl StackOffset {
1085    /// Whether the current offset is valid for element access.
1086    fn is_valid_offset(&self) -> bool {
1087        self.0.is_known() && self.0.value < (BPF_STACK_SIZE as u64)
1088    }
1089
1090    /// Whether the current offset is within the stack bounds, inclusive of `BPF_STACK_SIZE`.
1091    fn is_within_stack(&self) -> bool {
1092        self.0.is_known() && self.0.value <= (BPF_STACK_SIZE as u64)
1093    }
1094
1095    /// The value of the register.
1096    fn reg(&self) -> ScalarValueData {
1097        self.0
1098    }
1099
1100    /// The index into the stack array this offset points to. Can be called only if
1101    /// `is_within_stack()` is true.
1102    fn array_index(&self) -> usize {
1103        debug_assert!(self.is_within_stack());
1104        usize::try_from(self.0.value).unwrap() / STACK_ELEMENT_SIZE
1105    }
1106
1107    /// The offset inside the aligned u64 in the stack.
1108    fn sub_index(&self) -> usize {
1109        debug_assert!(self.is_within_stack());
1110        usize::try_from(self.0.value).unwrap() % STACK_ELEMENT_SIZE
1111    }
1112
1113    fn add<T: Into<ScalarValueData>>(self, rhs: T) -> Self {
1114        Self(self.0 + rhs)
1115    }
1116}
1117
1118/// The state of the stack
1119#[derive(Clone, Debug, Default, PartialEq)]
1120struct Stack {
1121    data: HashMap<usize, Type>,
1122}
1123
1124impl Stack {
1125    /// Replace all instances of the NullOr type with the given `null_id` to either 0 or the
1126    /// subtype depending on `is_null`
1127    fn set_null(&mut self, null_id: &MemoryId, is_null: bool) {
1128        for (_, t) in self.data.iter_mut() {
1129            t.set_null(null_id, is_null);
1130        }
1131    }
1132
1133    fn get(&self, index: usize) -> &Type {
1134        self.data.get(&index).unwrap_or(&Type::UNINITIALIZED)
1135    }
1136
1137    fn set(&mut self, index: usize, t: Type) {
1138        if t == Type::UNINITIALIZED {
1139            self.data.remove(&index);
1140        } else {
1141            self.data.insert(index, t);
1142        }
1143    }
1144
1145    fn extract_sub_value(value: u64, offset: usize, byte_count: usize) -> u64 {
1146        NativeEndian::read_uint(&value.as_bytes()[offset..], byte_count)
1147    }
1148
1149    fn insert_sub_value(mut original: u64, value: u64, width: DataWidth, offset: usize) -> u64 {
1150        let byte_count = width.bytes();
1151        let original_buf = original.as_mut_bytes();
1152        let value_buf = value.as_bytes();
1153        original_buf[offset..(byte_count + offset)].copy_from_slice(&value_buf[..byte_count]);
1154        original
1155    }
1156
1157    fn write_data_ptr(
1158        &mut self,
1159        pc: ProgramCounter,
1160        mut offset: StackOffset,
1161        bytes: u64,
1162    ) -> Result<(), String> {
1163        for i in 0..bytes {
1164            self.store(offset, Type::UNKNOWN_SCALAR, DataWidth::U8)?;
1165            offset = offset.add(1);
1166        }
1167        Ok(())
1168    }
1169
1170    fn read_data_ptr(
1171        &self,
1172        pc: ProgramCounter,
1173        offset: StackOffset,
1174        bytes: u64,
1175    ) -> Result<(), String> {
1176        let read_element =
1177            |index: usize, start_offset: usize, end_offset: usize| -> Result<(), String> {
1178                match self.get(index) {
1179                    Type::ScalarValue(data) => {
1180                        debug_assert!(end_offset > start_offset);
1181                        let unwritten_bits = Self::extract_sub_value(
1182                            data.unwritten_mask,
1183                            start_offset,
1184                            end_offset - start_offset,
1185                        );
1186                        if unwritten_bits == 0 {
1187                            Ok(())
1188                        } else {
1189                            Err("reading unwritten value from the stack".to_string())
1190                        }
1191                    }
1192                    _ => Err("invalid read from the stack".to_string()),
1193                }
1194            };
1195        if bytes == 0 {
1196            return Ok(());
1197        }
1198
1199        if bytes as usize > BPF_STACK_SIZE {
1200            return Err("stack overflow".to_string());
1201        }
1202
1203        if !offset.is_valid_offset() {
1204            return Err("invalid stack offset".to_string());
1205        }
1206
1207        let end_offset = offset.add(bytes);
1208        if !end_offset.is_within_stack() {
1209            return Err("stack overflow".to_string())?;
1210        }
1211
1212        // Handle the case where all the data is contained in a single element excluding the last
1213        // byte (the case when the read ends at an element edge, i.e. `end_offset.sub_index()==0`,
1214        // is covered by the default path below).
1215        if offset.array_index() == end_offset.array_index() {
1216            return read_element(offset.array_index(), offset.sub_index(), end_offset.sub_index());
1217        }
1218
1219        // Handle the first element, that might be partial.
1220        read_element(offset.array_index(), offset.sub_index(), STACK_ELEMENT_SIZE)?;
1221
1222        // Handle the last element, that might be partial.
1223        if end_offset.sub_index() != 0 {
1224            read_element(end_offset.array_index(), 0, end_offset.sub_index())?;
1225        }
1226
1227        // Handle the any full type between beginning and end.
1228        for i in (offset.array_index() + 1)..end_offset.array_index() {
1229            read_element(i, 0, STACK_ELEMENT_SIZE)?;
1230        }
1231
1232        Ok(())
1233    }
1234
1235    fn store(&mut self, offset: StackOffset, value: Type, width: DataWidth) -> Result<(), String> {
1236        if !offset.is_valid_offset() {
1237            return Err("out of bounds store".to_string());
1238        }
1239        if offset.sub_index() % width.bytes() != 0 {
1240            return Err("misaligned access".to_string());
1241        }
1242
1243        let index = offset.array_index();
1244        if width == DataWidth::U64 {
1245            self.set(index, value);
1246        } else {
1247            match value {
1248                Type::ScalarValue(data) => {
1249                    let old_data = match self.get(index) {
1250                        Type::ScalarValue(data) => *data,
1251                        _ => {
1252                            // The value in the stack is not a scalar. Let consider it an scalar
1253                            // value with no written bits.
1254                            ScalarValueData::UNINITIALIZED
1255                        }
1256                    };
1257                    let sub_index = offset.sub_index();
1258                    let value =
1259                        Self::insert_sub_value(old_data.value, data.value, width, sub_index);
1260                    let unknown_mask = Self::insert_sub_value(
1261                        old_data.unknown_mask,
1262                        data.unknown_mask,
1263                        width,
1264                        sub_index,
1265                    );
1266                    let unwritten_mask = Self::insert_sub_value(
1267                        old_data.unwritten_mask,
1268                        data.unwritten_mask,
1269                        width,
1270                        sub_index,
1271                    );
1272                    let urange = U64Range::compute_range_for_bytes_swap(
1273                        old_data.urange,
1274                        data.urange,
1275                        sub_index,
1276                        0,
1277                        width.bytes(),
1278                    );
1279                    self.set(
1280                        index,
1281                        Type::ScalarValue(ScalarValueData::new(
1282                            value,
1283                            unknown_mask,
1284                            unwritten_mask,
1285                            urange,
1286                        )),
1287                    );
1288                }
1289                _ => {
1290                    return Err("cannot store part of a non scalar value on the stack".to_string());
1291                }
1292            }
1293        }
1294        Ok(())
1295    }
1296
1297    fn load(&self, offset: StackOffset, width: DataWidth) -> Result<Type, String> {
1298        if !offset.is_valid_offset() {
1299            return Err("out of bounds load".to_string());
1300        }
1301        if offset.sub_index() % width.bytes() != 0 {
1302            return Err("misaligned access".to_string());
1303        }
1304
1305        let index = offset.array_index();
1306        let loaded_type = self.get(index).clone();
1307        let result = if width == DataWidth::U64 {
1308            loaded_type
1309        } else {
1310            match loaded_type {
1311                Type::ScalarValue(data) => {
1312                    let sub_index = offset.sub_index();
1313                    let value = Self::extract_sub_value(data.value, sub_index, width.bytes());
1314                    let unknown_mask =
1315                        Self::extract_sub_value(data.unknown_mask, sub_index, width.bytes());
1316                    let unwritten_mask =
1317                        Self::extract_sub_value(data.unwritten_mask, sub_index, width.bytes());
1318                    let urange = U64Range::compute_range_for_bytes_swap(
1319                        0.into(),
1320                        data.urange,
1321                        0,
1322                        sub_index,
1323                        width.bytes(),
1324                    );
1325                    Type::ScalarValue(ScalarValueData::new(
1326                        value,
1327                        unknown_mask,
1328                        unwritten_mask,
1329                        urange,
1330                    ))
1331                }
1332                _ => return Err(format!("incorrect load of {} bytes", width.bytes())),
1333            }
1334        };
1335        if !result.is_initialized() {
1336            return Err("reading unwritten value from the stack".to_string());
1337        }
1338        Ok(result)
1339    }
1340}
1341
1342/// Two types are ordered with `t1` > `t2` if a proof that a program in a state `t1` finish is also
1343/// a proof that a program in a state `t2` finish.
1344impl PartialOrd for Stack {
1345    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1346        let mut result = Ordering::Equal;
1347        let mut data_iter1 = self.data.iter().peekable();
1348        let mut data_iter2 = other.data.iter().peekable();
1349        loop {
1350            let k1 = data_iter1.peek().map(|(k, _)| *k);
1351            let k2 = data_iter2.peek().map(|(k, _)| *k);
1352            let k = match (k1, k2) {
1353                (None, None) => return Some(result),
1354                (Some(k), None) => {
1355                    data_iter1.next();
1356                    *k
1357                }
1358                (None, Some(k)) => {
1359                    data_iter2.next();
1360                    *k
1361                }
1362                (Some(k1), Some(k2)) => {
1363                    if k1 <= k2 {
1364                        data_iter1.next();
1365                    }
1366                    if k2 <= k1 {
1367                        data_iter2.next();
1368                    }
1369                    *std::cmp::min(k1, k2)
1370                }
1371            };
1372            result = associate_orderings(result, self.get(k).partial_cmp(other.get(k))?)?;
1373        }
1374    }
1375}
1376
1377macro_rules! bpf_log {
1378    ($context:ident, $verification_context:ident, $($msg:tt)*) => {
1379        let prefix = format!("{}: ({:02x})", $context.pc, $verification_context.code[$context.pc].code());
1380        let suffix = format!($($msg)*);
1381        $verification_context.logger.log(format!("{prefix} {suffix}").as_bytes());
1382    }
1383}
1384
1385/// The state of the computation as known by the verifier at a given point in time.
1386#[derive(Debug, Default)]
1387struct ComputationContext {
1388    /// The program counter.
1389    pc: ProgramCounter,
1390    /// Register 0 to 9.
1391    registers: [Type; GENERAL_REGISTER_COUNT as usize],
1392    /// The state of the stack.
1393    stack: Stack,
1394    /// The dynamically known bounds of buffers indexed by their ids.
1395    array_bounds: BTreeMap<MemoryId, u64>,
1396    /// The currently allocated resources.
1397    resources: HashSet<MemoryId>,
1398    /// The previous context in the computation.
1399    parent: Option<Arc<ComputationContext>>,
1400    /// The data dependencies of this context. This is used to broaden a known ending context to
1401    /// help cutting computation branches.
1402    dependencies: Mutex<Vec<DataDependencies>>,
1403}
1404
1405impl Clone for ComputationContext {
1406    fn clone(&self) -> Self {
1407        Self {
1408            pc: self.pc,
1409            registers: self.registers.clone(),
1410            stack: self.stack.clone(),
1411            array_bounds: self.array_bounds.clone(),
1412            resources: self.resources.clone(),
1413            parent: self.parent.clone(),
1414            // dependencies are erased as they must always be used on the same instance of the
1415            // context.
1416            dependencies: Default::default(),
1417        }
1418    }
1419}
1420
1421/// parent and dependencies are ignored for the comparison, as they do not matter for termination.
1422impl PartialEq for ComputationContext {
1423    fn eq(&self, other: &Self) -> bool {
1424        self.pc == other.pc
1425            && self.registers == other.registers
1426            && self.stack == other.stack
1427            && self.array_bounds == other.array_bounds
1428    }
1429}
1430
1431impl ComputationContext {
1432    /// Replace all instances of the NullOr type with the given `null_id` to either 0 or the
1433    /// subtype depending on `is_null`
1434    fn set_null(&mut self, null_id: &MemoryId, is_null: bool) {
1435        for i in 0..self.registers.len() {
1436            self.registers[i].set_null(null_id, is_null);
1437        }
1438        self.stack.set_null(null_id, is_null);
1439    }
1440
1441    fn reg(&self, index: Register) -> Result<Type, String> {
1442        if index >= REGISTER_COUNT {
1443            return Err(format!("R{index} is invalid"));
1444        }
1445        if index < GENERAL_REGISTER_COUNT {
1446            Ok(self.registers[index as usize].clone())
1447        } else {
1448            Ok(Type::PtrToStack { offset: StackOffset::default() })
1449        }
1450    }
1451
1452    fn set_reg(&mut self, index: Register, reg_type: Type) -> Result<(), String> {
1453        if index >= GENERAL_REGISTER_COUNT {
1454            return Err(format!("R{index} is invalid"));
1455        }
1456        self.registers[index as usize] = reg_type;
1457        Ok(())
1458    }
1459
1460    fn update_array_bounds(&mut self, id: MemoryId, new_bound: ScalarValueData) {
1461        let new_bound_min = new_bound.min();
1462        self.array_bounds
1463            .entry(id)
1464            .and_modify(|v| *v = std::cmp::max(*v, new_bound_min))
1465            .or_insert(new_bound_min);
1466    }
1467
1468    fn get_map_schema(&self, argument: u8) -> Result<MapSchema, String> {
1469        match self.reg(argument + 1)? {
1470            Type::ConstPtrToMap { schema, .. } => Ok(schema),
1471            _ => Err(format!("No map found at argument {argument}")),
1472        }
1473    }
1474
1475    fn next(&self) -> Result<Self, String> {
1476        let parent = Some(Arc::new(self.clone()));
1477        self.jump_with_offset(0, parent)
1478    }
1479
1480    /// Returns a new `ComputationContext` where the pc has jump by `offset + 1`. In particular,
1481    /// the next instruction is reached with `jump_with_offset(0)`.
1482    fn jump_with_offset(&self, offset: i16, parent: Option<Arc<Self>>) -> Result<Self, String> {
1483        let pc = self
1484            .pc
1485            .checked_add_signed(offset.into())
1486            .and_then(|v| v.checked_add_signed(1))
1487            .ok_or_else(|| "jump outside of program".to_string())?;
1488        let result = Self {
1489            pc,
1490            registers: self.registers.clone(),
1491            stack: self.stack.clone(),
1492            array_bounds: self.array_bounds.clone(),
1493            resources: self.resources.clone(),
1494            parent,
1495            dependencies: Default::default(),
1496        };
1497        Ok(result)
1498    }
1499
1500    fn check_memory_access(
1501        &self,
1502        dst_offset: ScalarValueData,
1503        dst_buffer_size: u64,
1504        instruction_offset: i16,
1505        width: usize,
1506    ) -> Result<(), String> {
1507        let memory_range = dst_offset.urange + instruction_offset + U64Range::new(0, width as u64);
1508        if memory_range.max > dst_buffer_size {
1509            return Err("out of bound access".to_string());
1510        }
1511        Ok(())
1512    }
1513
1514    fn store_memory(
1515        &mut self,
1516        context: &mut VerificationContext<'_>,
1517        addr: &Type,
1518        field: Field,
1519        value: Type,
1520    ) -> Result<(), String> {
1521        let addr = addr.inner(self)?;
1522        match *addr {
1523            Type::PtrToStack { offset } => {
1524                let offset_sum = offset.add(field.offset);
1525                return self.stack.store(offset_sum, value, field.width);
1526            }
1527            Type::PtrToMemory { offset, buffer_size, .. } => {
1528                self.check_memory_access(offset, buffer_size, field.offset, field.width.bytes())?;
1529            }
1530            Type::PtrToStruct { ref id, offset, ref descriptor, .. } => {
1531                let field_desc = descriptor
1532                    .find_field(offset, field)
1533                    .ok_or_else(|| "incorrect store".to_string())?;
1534
1535                if !matches!(field_desc.field_type, FieldType::MutableScalar { .. }) {
1536                    return Err("store to a read-only field".to_string());
1537                }
1538
1539                context.register_struct_access(StructAccess {
1540                    pc: self.pc,
1541                    memory_id: id.clone(),
1542                    field_offset: field_desc.offset,
1543                    is_32_bit_ptr_load: false,
1544                })?;
1545            }
1546            Type::PtrToArray { ref id, offset } => {
1547                self.check_memory_access(
1548                    offset,
1549                    *self.array_bounds.get(&id).unwrap_or(&0),
1550                    field.offset,
1551                    field.width.bytes(),
1552                )?;
1553            }
1554            _ => return Err("incorrect store".to_string()),
1555        }
1556
1557        match value {
1558            Type::ScalarValue(data) if data.is_fully_initialized() => {}
1559            // Private data should not be leaked.
1560            _ => return Err("incorrect store".to_string()),
1561        }
1562        Ok(())
1563    }
1564
1565    fn load_memory(
1566        &self,
1567        context: &mut VerificationContext<'_>,
1568        addr: &Type,
1569        field: Field,
1570    ) -> Result<Type, String> {
1571        let addr = addr.inner(self)?;
1572        match *addr {
1573            Type::PtrToStack { offset } => {
1574                let offset_sum = offset.add(field.offset);
1575                self.stack.load(offset_sum, field.width)
1576            }
1577            Type::PtrToMemory { ref id, offset, buffer_size, .. } => {
1578                self.check_memory_access(offset, buffer_size, field.offset, field.width.bytes())?;
1579                Ok(Type::UNKNOWN_SCALAR)
1580            }
1581            Type::PtrToStruct { ref id, offset, ref descriptor, .. } => {
1582                let field_desc = descriptor
1583                    .find_field(offset, field)
1584                    .ok_or_else(|| "incorrect load".to_string())?;
1585
1586                let (return_type, is_32_bit_ptr_load) = match &field_desc.field_type {
1587                    FieldType::Scalar { .. } | FieldType::MutableScalar { .. } => {
1588                        (Type::UNKNOWN_SCALAR, false)
1589                    }
1590                    FieldType::PtrToArray { id: array_id, is_32_bit } => (
1591                        Type::PtrToArray { id: array_id.prepended(id.clone()), offset: 0.into() },
1592                        *is_32_bit,
1593                    ),
1594                    FieldType::PtrToEndArray { id: array_id, is_32_bit } => {
1595                        (Type::PtrToEndArray { id: array_id.prepended(id.clone()) }, *is_32_bit)
1596                    }
1597                    FieldType::PtrToMemory { id: memory_id, buffer_size, is_32_bit } => (
1598                        Type::PtrToMemory {
1599                            id: memory_id.prepended(id.clone()),
1600                            offset: 0.into(),
1601                            buffer_size: *buffer_size as u64,
1602                        },
1603                        *is_32_bit,
1604                    ),
1605                    FieldType::NullablePtrToMemory { id: memory_id, buffer_size, is_32_bit } => {
1606                        let id = memory_id.prepended(id.clone());
1607                        (
1608                            Type::NullOr {
1609                                id: id.clone(),
1610                                inner: Box::new(Type::PtrToMemory {
1611                                    id,
1612                                    offset: 0.into(),
1613                                    buffer_size: *buffer_size as u64,
1614                                }),
1615                            },
1616                            *is_32_bit,
1617                        )
1618                    }
1619                };
1620
1621                context.register_struct_access(StructAccess {
1622                    pc: self.pc,
1623                    memory_id: id.clone(),
1624                    field_offset: field_desc.offset,
1625                    is_32_bit_ptr_load,
1626                })?;
1627
1628                Ok(return_type)
1629            }
1630            Type::PtrToArray { ref id, offset } => {
1631                self.check_memory_access(
1632                    offset,
1633                    *self.array_bounds.get(&id).unwrap_or(&0),
1634                    field.offset,
1635                    field.width.bytes(),
1636                )?;
1637                Ok(Type::UNKNOWN_SCALAR)
1638            }
1639            _ => Err("incorrect load".to_string()),
1640        }
1641    }
1642
1643    /**
1644     * Given the given `return_value` in a method signature, return the concrete type to use,
1645     * updating the `next` context is needed.
1646     *
1647     * `maybe_null` is true is the computed value will be a descendant of a `NullOr` type.
1648     */
1649    fn resolve_return_value(
1650        &self,
1651        verification_context: &mut VerificationContext<'_>,
1652        return_value: &Type,
1653        next: &mut ComputationContext,
1654        maybe_null: bool,
1655    ) -> Result<Type, String> {
1656        match return_value {
1657            Type::AliasParameter { parameter_index } => self.reg(parameter_index + 1),
1658            Type::ReleasableParameter { id, inner } => {
1659                let id = verification_context.next_id().prepended(id.clone());
1660                if !maybe_null {
1661                    next.resources.insert(id.clone());
1662                }
1663                Ok(Type::Releasable {
1664                    id,
1665                    inner: Box::new(self.resolve_return_value(
1666                        verification_context,
1667                        inner,
1668                        next,
1669                        maybe_null,
1670                    )?),
1671                })
1672            }
1673            Type::NullOrParameter(t) => {
1674                let id = verification_context.next_id();
1675                Ok(Type::NullOr {
1676                    id,
1677                    inner: Box::new(self.resolve_return_value(
1678                        verification_context,
1679                        t,
1680                        next,
1681                        true,
1682                    )?),
1683                })
1684            }
1685            Type::MapValueParameter { map_ptr_index } => {
1686                let schema = self.get_map_schema(*map_ptr_index)?;
1687                let id = verification_context.next_id();
1688                Ok(Type::PtrToMemory {
1689                    id,
1690                    offset: 0.into(),
1691                    buffer_size: schema.value_size as u64,
1692                })
1693            }
1694            Type::MemoryParameter { size, .. } => {
1695                let buffer_size = size.size(self)?;
1696                let id = verification_context.next_id();
1697                Ok(Type::PtrToMemory { id, offset: 0.into(), buffer_size })
1698            }
1699            t => Ok(t.clone()),
1700        }
1701    }
1702
1703    fn compute_source(&self, src: Source) -> Result<Type, String> {
1704        match src {
1705            Source::Reg(reg) => self.reg(reg),
1706            Source::Value(v) => Ok(v.into()),
1707        }
1708    }
1709
1710    fn apply_computation(
1711        context: &ComputationContext,
1712        op1: Type,
1713        op2: Type,
1714        alu_type: AluType,
1715        op: impl Fn(ScalarValueData, ScalarValueData) -> ScalarValueData,
1716    ) -> Result<Type, String> {
1717        let result: Type = match (alu_type, op1.inner(context)?, op2.inner(context)?) {
1718            (_, Type::ScalarValue(data1), Type::ScalarValue(data2)) => op(*data1, *data2).into(),
1719            (
1720                AluType::Add,
1721                Type::ScalarValue(_),
1722                Type::PtrToStack { .. } | Type::PtrToMemory { .. } | Type::PtrToStruct { .. },
1723            ) => {
1724                return Self::apply_computation(context, op2, op1, alu_type, op);
1725            }
1726            (alu_type, Type::PtrToStack { offset: x }, Type::ScalarValue(data))
1727                if alu_type.is_ptr_compatible() =>
1728            {
1729                Type::PtrToStack { offset: run_on_stack_offset(*x, |x| op(x, *data)) }
1730            }
1731            (
1732                alu_type,
1733                Type::PtrToMemory { id, offset: x, buffer_size },
1734                Type::ScalarValue(data),
1735            ) if alu_type.is_ptr_compatible() => {
1736                let offset = op(*x, *data);
1737                Type::PtrToMemory { id: id.clone(), offset, buffer_size: *buffer_size }
1738            }
1739            (
1740                alu_type,
1741                Type::PtrToStruct { id, offset: x, descriptor },
1742                Type::ScalarValue(data),
1743            ) if alu_type.is_ptr_compatible() => {
1744                let offset = op(*x, *data);
1745                Type::PtrToStruct { id: id.clone(), offset, descriptor: descriptor.clone() }
1746            }
1747            (AluType::Add, Type::PtrToArray { id, offset: x }, Type::ScalarValue(data)) => {
1748                let offset = x.checked_add(*data).ok_or_else(|| format!("XXX"))?;
1749                Type::PtrToArray { id: id.clone(), offset }
1750            }
1751            (AluType::Sub, Type::PtrToArray { id, offset: x }, Type::ScalarValue(data)) => {
1752                let offset = x.checked_sub(*data).ok_or_else(|| format!("XXX"))?;
1753                Type::PtrToArray { id: id.clone(), offset }
1754            }
1755            (
1756                AluType::Sub,
1757                Type::PtrToMemory { id: id1, offset: x1, .. },
1758                Type::PtrToMemory { id: id2, offset: x2, .. },
1759            )
1760            | (
1761                AluType::Sub,
1762                Type::PtrToStruct { id: id1, offset: x1, .. },
1763                Type::PtrToStruct { id: id2, offset: x2, .. },
1764            )
1765            | (
1766                AluType::Sub,
1767                Type::PtrToArray { id: id1, offset: x1 },
1768                Type::PtrToArray { id: id2, offset: x2 },
1769            ) if id1 == id2 => Type::from(op(*x1, *x2)),
1770            (AluType::Sub, Type::PtrToStack { offset: x1 }, Type::PtrToStack { offset: x2 }) => {
1771                Type::from(op(x1.reg(), x2.reg()))
1772            }
1773            (
1774                AluType::Sub,
1775                Type::PtrToArray { id: id1, .. },
1776                Type::PtrToEndArray { id: id2, .. },
1777            )
1778            | (
1779                AluType::Sub,
1780                Type::PtrToEndArray { id: id1, .. },
1781                Type::PtrToArray { id: id2, .. },
1782            ) if id1 == id2 => Type::UNKNOWN_SCALAR,
1783            _ => Type::default(),
1784        };
1785        Ok(result)
1786    }
1787
1788    fn alu(
1789        &mut self,
1790        op_name: Option<&str>,
1791        verification_context: &mut VerificationContext<'_>,
1792        dst: Register,
1793        src: Source,
1794        alu_type: AluType,
1795        op: impl Fn(ScalarValueData, ScalarValueData) -> ScalarValueData,
1796    ) -> Result<(), String> {
1797        if let Some(op_name) = op_name {
1798            bpf_log!(
1799                self,
1800                verification_context,
1801                "{op_name} {}, {}",
1802                display_register(dst),
1803                display_source(src)
1804            );
1805        }
1806        let op1 = self.reg(dst)?;
1807        let op2 = self.compute_source(src)?;
1808        let result = Self::apply_computation(self, op1, op2, alu_type, op)?;
1809        let mut next = self.next()?;
1810        next.set_reg(dst, result)?;
1811        verification_context.states.push(next);
1812        Ok(())
1813    }
1814
1815    fn log_atomic_operation(
1816        &mut self,
1817        op_name: &str,
1818        verification_context: &mut VerificationContext<'_>,
1819        fetch: bool,
1820        dst: Register,
1821        offset: i16,
1822        src: Register,
1823    ) {
1824        bpf_log!(
1825            self,
1826            verification_context,
1827            "lock {}{} [{}{}], {}",
1828            if fetch { "fetch " } else { "" },
1829            op_name,
1830            display_register(dst),
1831            print_offset(offset),
1832            display_register(src),
1833        );
1834    }
1835
1836    fn raw_atomic_operation(
1837        &mut self,
1838        op_name: &str,
1839        verification_context: &mut VerificationContext<'_>,
1840        width: DataWidth,
1841        fetch: bool,
1842        dst: Register,
1843        offset: i16,
1844        src: Register,
1845        op: impl FnOnce(&ComputationContext, Type, Type) -> Result<Type, String>,
1846    ) -> Result<(), String> {
1847        self.log_atomic_operation(op_name, verification_context, fetch, dst, offset, src);
1848        let addr = self.reg(dst)?;
1849        let value = self.reg(src)?;
1850        let field = Field::new(offset, width);
1851        let loaded_type = self.load_memory(verification_context, &addr, field)?;
1852        let result = op(self, loaded_type.clone(), value)?;
1853        let mut next = self.next()?;
1854        next.store_memory(verification_context, &addr, field, result)?;
1855        if fetch {
1856            next.set_reg(src, loaded_type)?;
1857        }
1858        verification_context.states.push(next);
1859        Ok(())
1860    }
1861
1862    fn atomic_operation(
1863        &mut self,
1864        op_name: &str,
1865        verification_context: &mut VerificationContext<'_>,
1866        width: DataWidth,
1867        fetch: bool,
1868        dst: Register,
1869        offset: i16,
1870        src: Register,
1871        alu_type: AluType,
1872        op: impl Fn(ScalarValueData, ScalarValueData) -> ScalarValueData,
1873    ) -> Result<(), String> {
1874        self.raw_atomic_operation(
1875            op_name,
1876            verification_context,
1877            width,
1878            fetch,
1879            dst,
1880            offset,
1881            src,
1882            |context: &ComputationContext, v1: Type, v2: Type| {
1883                Self::apply_computation(context, v1, v2, alu_type, op)
1884            },
1885        )
1886    }
1887
1888    fn raw_atomic_cmpxchg(
1889        &mut self,
1890        op_name: &str,
1891        verification_context: &mut VerificationContext<'_>,
1892        dst: Register,
1893        offset: i16,
1894        src: Register,
1895        jump_width: JumpWidth,
1896        op: impl Fn(ScalarValueData, ScalarValueData) -> Result<Option<bool>, ()>,
1897    ) -> Result<(), String> {
1898        self.log_atomic_operation(op_name, verification_context, true, dst, offset, src);
1899        let width = match jump_width {
1900            JumpWidth::W32 => DataWidth::U32,
1901            JumpWidth::W64 => DataWidth::U64,
1902        };
1903        let addr = self.reg(dst)?;
1904        let field = Field::new(offset, width);
1905        let dst = self.load_memory(verification_context, &addr, field)?;
1906        let value = self.reg(src)?;
1907        let r0 = self.reg(0)?;
1908        let branch = self.compute_branch(jump_width, &dst, &r0, op)?;
1909        // r0 = dst
1910        if branch.unwrap_or(true) {
1911            let mut next = self.next()?;
1912            let (dst, r0) =
1913                Type::constraint(&mut next, JumpType::Eq, jump_width, dst.clone(), r0.clone())?;
1914            next.set_reg(0, dst)?;
1915            next.store_memory(verification_context, &addr, field, value)?;
1916            verification_context.states.push(next);
1917        }
1918        // r0 != dst
1919        if !branch.unwrap_or(false) {
1920            let mut next = self.next()?;
1921            let (dst, r0) = Type::constraint(&mut next, JumpType::Ne, jump_width, dst, r0)?;
1922            next.set_reg(0, dst.clone())?;
1923            next.store_memory(verification_context, &addr, field, dst)?;
1924            verification_context.states.push(next);
1925        }
1926
1927        Ok(())
1928    }
1929    fn endianness<BO: ByteOrder>(
1930        &mut self,
1931        op_name: &str,
1932        verification_context: &mut VerificationContext<'_>,
1933        dst: Register,
1934        width: DataWidth,
1935    ) -> Result<(), String> {
1936        bpf_log!(self, verification_context, "{op_name}{} {}", width.bits(), display_register(dst),);
1937        let bit_op = |value: u64| match width {
1938            DataWidth::U16 => BO::read_u16((value as u16).as_bytes()) as u64,
1939            DataWidth::U32 => BO::read_u32((value as u32).as_bytes()) as u64,
1940            DataWidth::U64 => BO::read_u64(value.as_bytes()),
1941            _ => {
1942                panic!("Unexpected bit width for endianness operation");
1943            }
1944        };
1945        let value = self.reg(dst)?;
1946        let new_value = match value {
1947            Type::ScalarValue(data) => Type::ScalarValue(ScalarValueData::new(
1948                bit_op(data.value),
1949                bit_op(data.unknown_mask),
1950                bit_op(data.unwritten_mask),
1951                U64Range::max(),
1952            )),
1953            _ => Type::default(),
1954        };
1955        let mut next = self.next()?;
1956        next.set_reg(dst, new_value)?;
1957        verification_context.states.push(next);
1958        Ok(())
1959    }
1960
1961    fn compute_branch(
1962        &self,
1963        jump_width: JumpWidth,
1964        op1: &Type,
1965        op2: &Type,
1966        op: impl Fn(ScalarValueData, ScalarValueData) -> Result<Option<bool>, ()>,
1967    ) -> Result<Option<bool>, String> {
1968        match (jump_width, op1, op2) {
1969            (_, Type::ScalarValue(data1), Type::ScalarValue(data2)) => op(*data1, *data2),
1970            (JumpWidth::W64, Type::ScalarValue(data), Type::NullOr { .. })
1971            | (JumpWidth::W64, Type::NullOr { .. }, Type::ScalarValue(data))
1972                if data.is_zero() =>
1973            {
1974                Ok(None)
1975            }
1976
1977            (JumpWidth::W64, Type::ScalarValue(data), t) if data.is_zero() && t.is_non_zero() => {
1978                op(1.into(), 0.into())
1979            }
1980
1981            (JumpWidth::W64, t, Type::ScalarValue(data)) if data.is_zero() && t.is_non_zero() => {
1982                op(0.into(), 1.into())
1983            }
1984
1985            (JumpWidth::W64, Type::PtrToStack { offset: x }, Type::PtrToStack { offset: y }) => {
1986                op(x.reg(), y.reg())
1987            }
1988
1989            (
1990                JumpWidth::W64,
1991                Type::PtrToMemory { id: id1, offset: x, .. },
1992                Type::PtrToMemory { id: id2, offset: y, .. },
1993            )
1994            | (
1995                JumpWidth::W64,
1996                Type::PtrToStruct { id: id1, offset: x, .. },
1997                Type::PtrToStruct { id: id2, offset: y, .. },
1998            )
1999            | (
2000                JumpWidth::W64,
2001                Type::PtrToArray { id: id1, offset: x, .. },
2002                Type::PtrToArray { id: id2, offset: y, .. },
2003            ) if *id1 == *id2 => op(*x, *y),
2004
2005            (JumpWidth::W64, Type::PtrToArray { id: id1, .. }, Type::PtrToEndArray { id: id2 })
2006            | (JumpWidth::W64, Type::PtrToEndArray { id: id1 }, Type::PtrToArray { id: id2, .. })
2007                if *id1 == *id2 =>
2008            {
2009                Ok(None)
2010            }
2011
2012            _ => Err(()),
2013        }
2014        .map_err(|_| "non permitted comparison".to_string())
2015    }
2016
2017    fn conditional_jump(
2018        &mut self,
2019        op_name: &str,
2020        verification_context: &mut VerificationContext<'_>,
2021        dst: Register,
2022        src: Source,
2023        offset: i16,
2024        jump_type: JumpType,
2025        jump_width: JumpWidth,
2026        op: impl Fn(ScalarValueData, ScalarValueData) -> Result<Option<bool>, ()>,
2027    ) -> Result<(), String> {
2028        bpf_log!(
2029            self,
2030            verification_context,
2031            "{op_name} {}, {}, {}",
2032            display_register(dst),
2033            display_source(src),
2034            if offset == 0 { format!("0") } else { print_offset(offset) },
2035        );
2036        let op1 = self.reg(dst)?;
2037        let op2 = self.compute_source(src.clone())?;
2038        let apply_constraints_and_register = |mut next: Self,
2039                                              jump_type: JumpType|
2040         -> Result<Self, String> {
2041            if jump_type != JumpType::Unknown {
2042                let (new_op1, new_op2) =
2043                    Type::constraint(&mut next, jump_type, jump_width, op1.clone(), op2.clone())?;
2044                if dst < REGISTER_COUNT {
2045                    next.set_reg(dst, new_op1)?;
2046                }
2047                match src {
2048                    Source::Reg(r) => {
2049                        next.set_reg(r, new_op2)?;
2050                    }
2051                    _ => {
2052                        // Nothing to do
2053                    }
2054                }
2055            }
2056            Ok(next)
2057        };
2058        let branch = self.compute_branch(jump_width, &op1, &op2, op)?;
2059        let parent = Some(Arc::new(self.clone()));
2060        if branch.unwrap_or(true) {
2061            // Do the jump
2062            verification_context.states.push(apply_constraints_and_register(
2063                self.jump_with_offset(offset, parent.clone())?,
2064                jump_type,
2065            )?);
2066        }
2067        if !branch.unwrap_or(false) {
2068            // Skip the jump
2069            verification_context.states.push(apply_constraints_and_register(
2070                self.jump_with_offset(0, parent)?,
2071                jump_type.invert(),
2072            )?);
2073        }
2074        Ok(())
2075    }
2076
2077    /// Handles the termination of a `ComputationContext`, performing branch cutting optimization.
2078    ///
2079    /// This method is called when it has been proven that the current context terminates (e.g.,
2080    /// reaches an `exit` instruction).
2081    ///
2082    /// The following steps are performed:
2083    /// 1. **Dependency Calculation:** The data dependencies of the context are computed based on
2084    ///    the dependencies of its terminated children and the instruction at the current PC.
2085    /// 2. **Context Broadening:** The context's state is broadened by clearing registers and stack
2086    ///    slots that are *not* in the calculated data dependencies. This optimization assumes that
2087    ///    data not used by the terminated branch is irrelevant for future execution paths.
2088    /// 3. **Termination Registration:** The broadened context is added to the set of terminating
2089    ///    contexts if it is not less than any existing terminating context at the same PC.
2090    /// 4. **Parent Termination:** If all the children of the current context have terminated,
2091    ///    its parent context is recursively terminated.
2092    fn terminate(self, verification_context: &mut VerificationContext<'_>) -> Result<(), String> {
2093        let mut next = Some(self);
2094        // Because of the potential length of the parent chain, do not use recursion.
2095        while let Some(mut current) = next.take() {
2096            // Take the parent to process it at the end and not keep it in the terminating
2097            // contexts.
2098            let parent = current.parent.take();
2099
2100            // 1. Compute the dependencies of the context using the dependencies of its children
2101            //    and the actual operation.
2102            let mut dependencies = DataDependencies::default();
2103            for dependency in current.dependencies.get_mut().iter() {
2104                dependencies.merge(dependency);
2105            }
2106
2107            dependencies.visit(
2108                &mut DataDependenciesVisitorContext {
2109                    calling_context: &verification_context.calling_context,
2110                    computation_context: &current,
2111                },
2112                verification_context.code[current.pc],
2113            )?;
2114
2115            // 2. Clear the state depending on the dependencies states
2116            for register in 0..GENERAL_REGISTER_COUNT {
2117                if !dependencies.registers.contains(&register) {
2118                    current.set_reg(register, Default::default())?;
2119                }
2120            }
2121            current.stack.data.retain(|k, _| dependencies.stack.contains(k));
2122
2123            // 3. Add the cleared state to the set of `terminating_contexts`
2124            let terminating_contexts =
2125                verification_context.terminating_contexts.entry(current.pc).or_default();
2126            let mut is_dominated = false;
2127            terminating_contexts.retain(|c| match c.computation_context.partial_cmp(&current) {
2128                Some(Ordering::Less) => false,
2129                Some(Ordering::Equal) | Some(Ordering::Greater) => {
2130                    // If the list contains a context greater or equal to the current one, it
2131                    // should not be added.
2132                    is_dominated = true;
2133                    true
2134                }
2135                _ => true,
2136            });
2137            if !is_dominated {
2138                terminating_contexts.push(TerminatingContext {
2139                    computation_context: current,
2140                    dependencies: dependencies.clone(),
2141                });
2142            }
2143
2144            // 4. Register the computed dependencies in our parent, and terminate it if all
2145            //    dependencies has been computed.
2146            if let Some(parent) = parent {
2147                parent.dependencies.lock().push(dependencies);
2148                // To check whether all dependencies have been computed, rely on the fact that the Arc
2149                // count of the parent keep track of how many dependencies are left.
2150                next = Arc::into_inner(parent);
2151            }
2152        }
2153        Ok(())
2154    }
2155}
2156
2157impl Drop for ComputationContext {
2158    fn drop(&mut self) {
2159        let mut next = self.parent.take().and_then(Arc::into_inner);
2160        // Because of the potential length of the parent chain, do not use recursion.
2161        while let Some(mut current) = next {
2162            next = current.parent.take().and_then(Arc::into_inner);
2163        }
2164    }
2165}
2166
2167/// Two types are ordered with `t1` > `t2` if a proof that a program in a state `t1` finish is also
2168/// a proof that a program in a state `t2` finish.
2169impl PartialOrd for ComputationContext {
2170    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2171        if self.pc != other.pc || self.resources != other.resources {
2172            return None;
2173        }
2174        let mut result = self.stack.partial_cmp(&other.stack)?;
2175        result = associate_orderings(
2176            result,
2177            Type::compare_list(self.registers.iter(), other.registers.iter())?,
2178        )?;
2179        let mut array_bound_iter1 = self.array_bounds.iter().peekable();
2180        let mut array_bound_iter2 = other.array_bounds.iter().peekable();
2181        let result = loop {
2182            match (array_bound_iter1.peek().cloned(), array_bound_iter2.peek().cloned()) {
2183                (None, None) => break result,
2184                (None, _) => break associate_orderings(result, Ordering::Greater)?,
2185                (_, None) => break associate_orderings(result, Ordering::Less)?,
2186                (Some((k1, v1)), Some((k2, v2))) => match k1.cmp(k2) {
2187                    Ordering::Equal => {
2188                        array_bound_iter1.next();
2189                        array_bound_iter2.next();
2190                        // The comparison is intentionally reversed (v2.cmp(v1) instead of
2191                        // v1.cmp(v2)). `v1` is the bound of the explored state (`self`) and `v2` is
2192                        // the bound of the current state (`other`). A smaller bound value
2193                        // represents a stronger safety guarantee (fewer assumed valid bytes,
2194                        // meaning a more restricted program). Therefore, a state with a smaller
2195                        // bound dominates (is "Greater" than) a state with a larger bound.
2196                        result = associate_orderings(result, v2.cmp(v1))?;
2197                    }
2198                    v @ Ordering::Less => {
2199                        array_bound_iter1.next();
2200                        result = associate_orderings(result, v)?;
2201                    }
2202                    v @ Ordering::Greater => {
2203                        array_bound_iter2.next();
2204                        result = associate_orderings(result, v)?;
2205                    }
2206                },
2207            }
2208        };
2209        Some(result)
2210    }
2211}
2212
2213/// Represents the read data dependencies of an eBPF program branch.
2214///
2215/// This struct tracks which registers and stack positions are *read* by the
2216/// instructions within a branch of the eBPF program.  This information is used
2217/// during branch cutting optimization to broaden terminating contexts.
2218///
2219/// The verifier assumes that data not read by a terminated branch is irrelevant
2220/// for future execution paths and can be safely cleared.
2221#[derive(Clone, Debug, Default)]
2222struct DataDependencies {
2223    /// The set of registers read by the children of a context.
2224    registers: HashSet<Register>,
2225    /// The stack positions read by the children of a context.
2226    stack: HashSet<usize>,
2227}
2228
2229impl DataDependencies {
2230    fn merge(&mut self, other: &DataDependencies) {
2231        self.registers.extend(other.registers.iter());
2232        self.stack.extend(other.stack.iter());
2233    }
2234
2235    fn alu(&mut self, dst: Register, src: Source) -> Result<(), String> {
2236        // Only do something if the dst is read, otherwise the computation doesn't matter.
2237        if self.registers.contains(&dst) {
2238            if let Source::Reg(src) = src {
2239                self.registers.insert(src);
2240            }
2241        }
2242        Ok(())
2243    }
2244
2245    fn jmp(&mut self, dst: Register, src: Source) -> Result<(), String> {
2246        self.registers.insert(dst);
2247        if let Source::Reg(src) = src {
2248            self.registers.insert(src);
2249        }
2250        Ok(())
2251    }
2252
2253    fn atomic(
2254        &mut self,
2255        context: &ComputationContext,
2256        fetch: bool,
2257        dst: Register,
2258        offset: i16,
2259        src: Register,
2260        width: DataWidth,
2261        is_cmpxchg: bool,
2262    ) -> Result<(), String> {
2263        let mut is_read = false;
2264        if is_cmpxchg && self.registers.contains(&0) {
2265            is_read = true;
2266        }
2267        if fetch && self.registers.contains(&src) {
2268            is_read = true;
2269        }
2270        let addr = context.reg(dst)?;
2271        if let Type::PtrToStack { offset: stack_offset } = addr {
2272            let stack_offset = stack_offset.add(offset);
2273            if !stack_offset.is_valid_offset() {
2274                return Err(format!("Invalid stack offset at {}", context.pc));
2275            }
2276            if is_read || self.stack.contains(&stack_offset.array_index()) {
2277                is_read = true;
2278                self.stack.insert(stack_offset.array_index());
2279            }
2280        }
2281        if is_read {
2282            self.registers.insert(0);
2283            self.registers.insert(src);
2284        }
2285        self.registers.insert(dst);
2286        Ok(())
2287    }
2288}
2289
2290struct DataDependenciesVisitorContext<'a> {
2291    calling_context: &'a CallingContext,
2292    computation_context: &'a ComputationContext,
2293}
2294
2295impl BpfVisitor for DataDependencies {
2296    type Context<'a> = DataDependenciesVisitorContext<'a>;
2297
2298    fn add<'a>(
2299        &mut self,
2300        _context: &mut Self::Context<'a>,
2301        dst: Register,
2302        src: Source,
2303    ) -> Result<(), String> {
2304        self.alu(dst, src)
2305    }
2306    fn add64<'a>(
2307        &mut self,
2308        _context: &mut Self::Context<'a>,
2309        dst: Register,
2310        src: Source,
2311    ) -> Result<(), String> {
2312        self.alu(dst, src)
2313    }
2314    fn and<'a>(
2315        &mut self,
2316        _context: &mut Self::Context<'a>,
2317        dst: Register,
2318        src: Source,
2319    ) -> Result<(), String> {
2320        self.alu(dst, src)
2321    }
2322    fn and64<'a>(
2323        &mut self,
2324        _context: &mut Self::Context<'a>,
2325        dst: Register,
2326        src: Source,
2327    ) -> Result<(), String> {
2328        self.alu(dst, src)
2329    }
2330    fn arsh<'a>(
2331        &mut self,
2332        _context: &mut Self::Context<'a>,
2333        dst: Register,
2334        src: Source,
2335    ) -> Result<(), String> {
2336        self.alu(dst, src)
2337    }
2338    fn arsh64<'a>(
2339        &mut self,
2340        _context: &mut Self::Context<'a>,
2341        dst: Register,
2342        src: Source,
2343    ) -> Result<(), String> {
2344        self.alu(dst, src)
2345    }
2346    fn div<'a>(
2347        &mut self,
2348        _context: &mut Self::Context<'a>,
2349        dst: Register,
2350        src: Source,
2351    ) -> Result<(), String> {
2352        self.alu(dst, src)
2353    }
2354    fn div64<'a>(
2355        &mut self,
2356        _context: &mut Self::Context<'a>,
2357        dst: Register,
2358        src: Source,
2359    ) -> Result<(), String> {
2360        self.alu(dst, src)
2361    }
2362    fn lsh<'a>(
2363        &mut self,
2364        _context: &mut Self::Context<'a>,
2365        dst: Register,
2366        src: Source,
2367    ) -> Result<(), String> {
2368        self.alu(dst, src)
2369    }
2370    fn lsh64<'a>(
2371        &mut self,
2372        _context: &mut Self::Context<'a>,
2373        dst: Register,
2374        src: Source,
2375    ) -> Result<(), String> {
2376        self.alu(dst, src)
2377    }
2378    fn r#mod<'a>(
2379        &mut self,
2380        _context: &mut Self::Context<'a>,
2381        dst: Register,
2382        src: Source,
2383    ) -> Result<(), String> {
2384        self.alu(dst, src)
2385    }
2386    fn mod64<'a>(
2387        &mut self,
2388        _context: &mut Self::Context<'a>,
2389        dst: Register,
2390        src: Source,
2391    ) -> Result<(), String> {
2392        self.alu(dst, src)
2393    }
2394    fn mul<'a>(
2395        &mut self,
2396        _context: &mut Self::Context<'a>,
2397        dst: Register,
2398        src: Source,
2399    ) -> Result<(), String> {
2400        self.alu(dst, src)
2401    }
2402    fn mul64<'a>(
2403        &mut self,
2404        _context: &mut Self::Context<'a>,
2405        dst: Register,
2406        src: Source,
2407    ) -> Result<(), String> {
2408        self.alu(dst, src)
2409    }
2410    fn or<'a>(
2411        &mut self,
2412        _context: &mut Self::Context<'a>,
2413        dst: Register,
2414        src: Source,
2415    ) -> Result<(), String> {
2416        self.alu(dst, src)
2417    }
2418    fn or64<'a>(
2419        &mut self,
2420        _context: &mut Self::Context<'a>,
2421        dst: Register,
2422        src: Source,
2423    ) -> Result<(), String> {
2424        self.alu(dst, src)
2425    }
2426    fn rsh<'a>(
2427        &mut self,
2428        _context: &mut Self::Context<'a>,
2429        dst: Register,
2430        src: Source,
2431    ) -> Result<(), String> {
2432        self.alu(dst, src)
2433    }
2434    fn rsh64<'a>(
2435        &mut self,
2436        _context: &mut Self::Context<'a>,
2437        dst: Register,
2438        src: Source,
2439    ) -> Result<(), String> {
2440        self.alu(dst, src)
2441    }
2442    fn sub<'a>(
2443        &mut self,
2444        _context: &mut Self::Context<'a>,
2445        dst: Register,
2446        src: Source,
2447    ) -> Result<(), String> {
2448        self.alu(dst, src)
2449    }
2450    fn sub64<'a>(
2451        &mut self,
2452        _context: &mut Self::Context<'a>,
2453        dst: Register,
2454        src: Source,
2455    ) -> Result<(), String> {
2456        self.alu(dst, src)
2457    }
2458    fn xor<'a>(
2459        &mut self,
2460        _context: &mut Self::Context<'a>,
2461        dst: Register,
2462        src: Source,
2463    ) -> Result<(), String> {
2464        self.alu(dst, src)
2465    }
2466    fn xor64<'a>(
2467        &mut self,
2468        _context: &mut Self::Context<'a>,
2469        dst: Register,
2470        src: Source,
2471    ) -> Result<(), String> {
2472        self.alu(dst, src)
2473    }
2474
2475    fn mov<'a>(
2476        &mut self,
2477        _context: &mut Self::Context<'a>,
2478        dst: Register,
2479        src: Source,
2480    ) -> Result<(), String> {
2481        if src == Source::Reg(dst) || !self.registers.contains(&dst) {
2482            return Ok(());
2483        }
2484        if let Source::Reg(src) = src {
2485            self.registers.insert(src);
2486        }
2487        self.registers.remove(&dst);
2488        Ok(())
2489    }
2490    fn mov64<'a>(
2491        &mut self,
2492        context: &mut Self::Context<'a>,
2493        dst: Register,
2494        src: Source,
2495    ) -> Result<(), String> {
2496        self.mov(context, dst, src)
2497    }
2498
2499    fn neg<'a>(&mut self, _context: &mut Self::Context<'a>, _dst: Register) -> Result<(), String> {
2500        // This is reading and writing the same value. This induces no change in dependencies.
2501        Ok(())
2502    }
2503    fn neg64<'a>(
2504        &mut self,
2505        _context: &mut Self::Context<'a>,
2506        _dst: Register,
2507    ) -> Result<(), String> {
2508        // This is reading and writing the same value. This induces no change in dependencies.
2509        Ok(())
2510    }
2511
2512    fn be<'a>(
2513        &mut self,
2514        _context: &mut Self::Context<'a>,
2515        _dst: Register,
2516        _width: DataWidth,
2517    ) -> Result<(), String> {
2518        // This is reading and writing the same value. This induces no change in dependencies.
2519        Ok(())
2520    }
2521    fn le<'a>(
2522        &mut self,
2523        _context: &mut Self::Context<'a>,
2524        _dst: Register,
2525        _width: DataWidth,
2526    ) -> Result<(), String> {
2527        // This is reading and writing the same value. This induces no change in dependencies.
2528        Ok(())
2529    }
2530
2531    fn call_external<'a>(
2532        &mut self,
2533        context: &mut Self::Context<'a>,
2534        index: u32,
2535    ) -> Result<(), String> {
2536        let Some(helper) = context.calling_context.helpers.get(&index).cloned() else {
2537            return Err(format!("unknown external function {}", index));
2538        };
2539        // 0 is overwritten and 1 to 5 are scratch registers
2540        for register in 0..helper.signature.args.len() + 1 {
2541            self.registers.remove(&(register as Register));
2542        }
2543        // 1 to k are parameters.
2544        for register in 0..helper.signature.args.len() {
2545            self.registers.insert((register + 1) as Register);
2546        }
2547        Ok(())
2548    }
2549
2550    fn exit<'a>(&mut self, _context: &mut Self::Context<'a>) -> Result<(), String> {
2551        // This read r0 unconditionally.
2552        self.registers.insert(0);
2553        Ok(())
2554    }
2555
2556    fn jump<'a>(&mut self, _context: &mut Self::Context<'a>, _offset: i16) -> Result<(), String> {
2557        // This doesn't do anything with values.
2558        Ok(())
2559    }
2560
2561    fn jeq<'a>(
2562        &mut self,
2563        _context: &mut Self::Context<'a>,
2564        dst: Register,
2565        src: Source,
2566        offset: i16,
2567    ) -> Result<(), String> {
2568        self.jmp(dst, src)
2569    }
2570    fn jeq64<'a>(
2571        &mut self,
2572        _context: &mut Self::Context<'a>,
2573        dst: Register,
2574        src: Source,
2575        offset: i16,
2576    ) -> Result<(), String> {
2577        self.jmp(dst, src)
2578    }
2579    fn jne<'a>(
2580        &mut self,
2581        _context: &mut Self::Context<'a>,
2582        dst: Register,
2583        src: Source,
2584        offset: i16,
2585    ) -> Result<(), String> {
2586        self.jmp(dst, src)
2587    }
2588    fn jne64<'a>(
2589        &mut self,
2590        _context: &mut Self::Context<'a>,
2591        dst: Register,
2592        src: Source,
2593        offset: i16,
2594    ) -> Result<(), String> {
2595        self.jmp(dst, src)
2596    }
2597    fn jge<'a>(
2598        &mut self,
2599        _context: &mut Self::Context<'a>,
2600        dst: Register,
2601        src: Source,
2602        offset: i16,
2603    ) -> Result<(), String> {
2604        self.jmp(dst, src)
2605    }
2606    fn jge64<'a>(
2607        &mut self,
2608        _context: &mut Self::Context<'a>,
2609        dst: Register,
2610        src: Source,
2611        offset: i16,
2612    ) -> Result<(), String> {
2613        self.jmp(dst, src)
2614    }
2615    fn jgt<'a>(
2616        &mut self,
2617        _context: &mut Self::Context<'a>,
2618        dst: Register,
2619        src: Source,
2620        offset: i16,
2621    ) -> Result<(), String> {
2622        self.jmp(dst, src)
2623    }
2624    fn jgt64<'a>(
2625        &mut self,
2626        _context: &mut Self::Context<'a>,
2627        dst: Register,
2628        src: Source,
2629        offset: i16,
2630    ) -> Result<(), String> {
2631        self.jmp(dst, src)
2632    }
2633    fn jle<'a>(
2634        &mut self,
2635        _context: &mut Self::Context<'a>,
2636        dst: Register,
2637        src: Source,
2638        offset: i16,
2639    ) -> Result<(), String> {
2640        self.jmp(dst, src)
2641    }
2642    fn jle64<'a>(
2643        &mut self,
2644        _context: &mut Self::Context<'a>,
2645        dst: Register,
2646        src: Source,
2647        offset: i16,
2648    ) -> Result<(), String> {
2649        self.jmp(dst, src)
2650    }
2651    fn jlt<'a>(
2652        &mut self,
2653        _context: &mut Self::Context<'a>,
2654        dst: Register,
2655        src: Source,
2656        offset: i16,
2657    ) -> Result<(), String> {
2658        self.jmp(dst, src)
2659    }
2660    fn jlt64<'a>(
2661        &mut self,
2662        _context: &mut Self::Context<'a>,
2663        dst: Register,
2664        src: Source,
2665        offset: i16,
2666    ) -> Result<(), String> {
2667        self.jmp(dst, src)
2668    }
2669    fn jsge<'a>(
2670        &mut self,
2671        _context: &mut Self::Context<'a>,
2672        dst: Register,
2673        src: Source,
2674        offset: i16,
2675    ) -> Result<(), String> {
2676        self.jmp(dst, src)
2677    }
2678    fn jsge64<'a>(
2679        &mut self,
2680        _context: &mut Self::Context<'a>,
2681        dst: Register,
2682        src: Source,
2683        offset: i16,
2684    ) -> Result<(), String> {
2685        self.jmp(dst, src)
2686    }
2687    fn jsgt<'a>(
2688        &mut self,
2689        _context: &mut Self::Context<'a>,
2690        dst: Register,
2691        src: Source,
2692        offset: i16,
2693    ) -> Result<(), String> {
2694        self.jmp(dst, src)
2695    }
2696    fn jsgt64<'a>(
2697        &mut self,
2698        _context: &mut Self::Context<'a>,
2699        dst: Register,
2700        src: Source,
2701        offset: i16,
2702    ) -> Result<(), String> {
2703        self.jmp(dst, src)
2704    }
2705    fn jsle<'a>(
2706        &mut self,
2707        _context: &mut Self::Context<'a>,
2708        dst: Register,
2709        src: Source,
2710        offset: i16,
2711    ) -> Result<(), String> {
2712        self.jmp(dst, src)
2713    }
2714    fn jsle64<'a>(
2715        &mut self,
2716        _context: &mut Self::Context<'a>,
2717        dst: Register,
2718        src: Source,
2719        offset: i16,
2720    ) -> Result<(), String> {
2721        self.jmp(dst, src)
2722    }
2723    fn jslt<'a>(
2724        &mut self,
2725        _context: &mut Self::Context<'a>,
2726        dst: Register,
2727        src: Source,
2728        offset: i16,
2729    ) -> Result<(), String> {
2730        self.jmp(dst, src)
2731    }
2732    fn jslt64<'a>(
2733        &mut self,
2734        _context: &mut Self::Context<'a>,
2735        dst: Register,
2736        src: Source,
2737        offset: i16,
2738    ) -> Result<(), String> {
2739        self.jmp(dst, src)
2740    }
2741    fn jset<'a>(
2742        &mut self,
2743        _context: &mut Self::Context<'a>,
2744        dst: Register,
2745        src: Source,
2746        offset: i16,
2747    ) -> Result<(), String> {
2748        self.jmp(dst, src)
2749    }
2750    fn jset64<'a>(
2751        &mut self,
2752        _context: &mut Self::Context<'a>,
2753        dst: Register,
2754        src: Source,
2755        offset: i16,
2756    ) -> Result<(), String> {
2757        self.jmp(dst, src)
2758    }
2759
2760    fn atomic_add<'a>(
2761        &mut self,
2762        context: &mut Self::Context<'a>,
2763        fetch: bool,
2764        dst: Register,
2765        offset: i16,
2766        src: Register,
2767    ) -> Result<(), String> {
2768        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U32, false)
2769    }
2770
2771    fn atomic_add64<'a>(
2772        &mut self,
2773        context: &mut Self::Context<'a>,
2774        fetch: bool,
2775        dst: Register,
2776        offset: i16,
2777        src: Register,
2778    ) -> Result<(), String> {
2779        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U64, false)
2780    }
2781
2782    fn atomic_and<'a>(
2783        &mut self,
2784        context: &mut Self::Context<'a>,
2785        fetch: bool,
2786        dst: Register,
2787        offset: i16,
2788        src: Register,
2789    ) -> Result<(), String> {
2790        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U32, false)
2791    }
2792
2793    fn atomic_and64<'a>(
2794        &mut self,
2795        context: &mut Self::Context<'a>,
2796        fetch: bool,
2797        dst: Register,
2798        offset: i16,
2799        src: Register,
2800    ) -> Result<(), String> {
2801        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U64, false)
2802    }
2803
2804    fn atomic_or<'a>(
2805        &mut self,
2806        context: &mut Self::Context<'a>,
2807        fetch: bool,
2808        dst: Register,
2809        offset: i16,
2810        src: Register,
2811    ) -> Result<(), String> {
2812        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U32, false)
2813    }
2814
2815    fn atomic_or64<'a>(
2816        &mut self,
2817        context: &mut Self::Context<'a>,
2818        fetch: bool,
2819        dst: Register,
2820        offset: i16,
2821        src: Register,
2822    ) -> Result<(), String> {
2823        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U64, false)
2824    }
2825
2826    fn atomic_xor<'a>(
2827        &mut self,
2828        context: &mut Self::Context<'a>,
2829        fetch: bool,
2830        dst: Register,
2831        offset: i16,
2832        src: Register,
2833    ) -> Result<(), String> {
2834        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U32, false)
2835    }
2836
2837    fn atomic_xor64<'a>(
2838        &mut self,
2839        context: &mut Self::Context<'a>,
2840        fetch: bool,
2841        dst: Register,
2842        offset: i16,
2843        src: Register,
2844    ) -> Result<(), String> {
2845        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U64, false)
2846    }
2847
2848    fn atomic_xchg<'a>(
2849        &mut self,
2850        context: &mut Self::Context<'a>,
2851        fetch: bool,
2852        dst: Register,
2853        offset: i16,
2854        src: Register,
2855    ) -> Result<(), String> {
2856        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U32, false)
2857    }
2858
2859    fn atomic_xchg64<'a>(
2860        &mut self,
2861        context: &mut Self::Context<'a>,
2862        fetch: bool,
2863        dst: Register,
2864        offset: i16,
2865        src: Register,
2866    ) -> Result<(), String> {
2867        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U64, false)
2868    }
2869
2870    fn atomic_cmpxchg<'a>(
2871        &mut self,
2872        context: &mut Self::Context<'a>,
2873        dst: Register,
2874        offset: i16,
2875        src: Register,
2876    ) -> Result<(), String> {
2877        self.atomic(&context.computation_context, true, dst, offset, src, DataWidth::U32, true)
2878    }
2879
2880    fn atomic_cmpxchg64<'a>(
2881        &mut self,
2882        context: &mut Self::Context<'a>,
2883        dst: Register,
2884        offset: i16,
2885        src: Register,
2886    ) -> Result<(), String> {
2887        self.atomic(&context.computation_context, true, dst, offset, src, DataWidth::U64, true)
2888    }
2889
2890    fn load<'a>(
2891        &mut self,
2892        context: &mut Self::Context<'a>,
2893        dst: Register,
2894        offset: i16,
2895        src: Register,
2896        width: DataWidth,
2897    ) -> Result<(), String> {
2898        let context = &context.computation_context;
2899        if self.registers.contains(&dst) {
2900            let addr = context.reg(src)?;
2901            if let Type::PtrToStack { offset: stack_offset } = addr {
2902                let stack_offset = stack_offset.add(offset);
2903                if !stack_offset.is_valid_offset() {
2904                    return Err(format!("Invalid stack offset at {}", context.pc));
2905                }
2906                self.stack.insert(stack_offset.array_index());
2907            }
2908        }
2909        self.registers.insert(src);
2910        Ok(())
2911    }
2912
2913    fn load64<'a>(
2914        &mut self,
2915        _context: &mut Self::Context<'a>,
2916        dst: Register,
2917        _src: u8,
2918        _lower: u32,
2919    ) -> Result<(), String> {
2920        self.registers.remove(&dst);
2921        Ok(())
2922    }
2923
2924    fn load_from_packet<'a>(
2925        &mut self,
2926        _context: &mut Self::Context<'a>,
2927        dst: Register,
2928        src: Register,
2929        _offset: i32,
2930        register_offset: Option<Register>,
2931        _width: DataWidth,
2932    ) -> Result<(), String> {
2933        // 1 to 5 are scratch registers
2934        for register in 1..6 {
2935            self.registers.remove(&(register as Register));
2936        }
2937        // Only do something if the dst is read, otherwise the computation doesn't matter.
2938        if self.registers.remove(&dst) {
2939            self.registers.insert(src);
2940            if let Some(reg) = register_offset {
2941                self.registers.insert(reg);
2942            }
2943        }
2944        Ok(())
2945    }
2946
2947    fn store<'a>(
2948        &mut self,
2949        context: &mut Self::Context<'a>,
2950        dst: Register,
2951        offset: i16,
2952        src: Source,
2953        width: DataWidth,
2954    ) -> Result<(), String> {
2955        let context = &context.computation_context;
2956        let addr = context.reg(dst)?;
2957        if let Type::PtrToStack { offset: stack_offset } = addr {
2958            let stack_offset = stack_offset.add(offset);
2959            if !stack_offset.is_valid_offset() {
2960                return Err(format!("Invalid stack offset at {}", context.pc));
2961            }
2962            if self.stack.remove(&stack_offset.array_index()) {
2963                if let Source::Reg(src) = src {
2964                    self.registers.insert(src);
2965                }
2966            }
2967        } else {
2968            if let Source::Reg(src) = src {
2969                self.registers.insert(src);
2970            }
2971        }
2972
2973        Ok(())
2974    }
2975}
2976
2977#[derive(Debug)]
2978struct TerminatingContext {
2979    computation_context: ComputationContext,
2980    dependencies: DataDependencies,
2981}
2982
2983#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2984enum AluType {
2985    Plain,
2986    Sub,
2987    Add,
2988}
2989
2990impl AluType {
2991    /// Can this operation be done one a pointer and a scalar.
2992    fn is_ptr_compatible(&self) -> bool {
2993        match self {
2994            Self::Sub | Self::Add => true,
2995            _ => false,
2996        }
2997    }
2998}
2999
3000#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3001enum JumpWidth {
3002    W32,
3003    W64,
3004}
3005
3006#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3007enum JumpType {
3008    Eq,
3009    Ge,
3010    Gt,
3011    Le,
3012    LooseComparaison,
3013    Lt,
3014    Ne,
3015    StrictComparaison,
3016    Unknown,
3017}
3018
3019impl JumpType {
3020    fn invert(&self) -> Self {
3021        match self {
3022            Self::Eq => Self::Ne,
3023            Self::Ge => Self::Lt,
3024            Self::Gt => Self::Le,
3025            Self::Le => Self::Gt,
3026            Self::LooseComparaison => Self::StrictComparaison,
3027            Self::Lt => Self::Ge,
3028            Self::Ne => Self::Eq,
3029            Self::StrictComparaison => Self::LooseComparaison,
3030            Self::Unknown => Self::Unknown,
3031        }
3032    }
3033
3034    fn is_strict(&self) -> bool {
3035        match self {
3036            Self::Gt | Self::Lt | Self::Ne | Self::StrictComparaison => true,
3037            _ => false,
3038        }
3039    }
3040}
3041
3042fn display_register(register: Register) -> String {
3043    format!("%r{register}")
3044}
3045
3046fn display_source(src: Source) -> String {
3047    match src {
3048        Source::Reg(r) => display_register(r),
3049        Source::Value(v) => format!("0x{v:x}"),
3050    }
3051}
3052
3053impl BpfVisitor for ComputationContext {
3054    type Context<'a> = VerificationContext<'a>;
3055
3056    fn add<'a>(
3057        &mut self,
3058        context: &mut Self::Context<'a>,
3059        dst: Register,
3060        src: Source,
3061    ) -> Result<(), String> {
3062        self.alu(Some("add32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x + y))
3063    }
3064    fn add64<'a>(
3065        &mut self,
3066        context: &mut Self::Context<'a>,
3067        dst: Register,
3068        src: Source,
3069    ) -> Result<(), String> {
3070        self.alu(Some("add"), context, dst, src, AluType::Add, |x, y| x + y)
3071    }
3072    fn and<'a>(
3073        &mut self,
3074        context: &mut Self::Context<'a>,
3075        dst: Register,
3076        src: Source,
3077    ) -> Result<(), String> {
3078        self.alu(Some("and32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x & y))
3079    }
3080    fn and64<'a>(
3081        &mut self,
3082        context: &mut Self::Context<'a>,
3083        dst: Register,
3084        src: Source,
3085    ) -> Result<(), String> {
3086        self.alu(Some("and"), context, dst, src, AluType::Plain, |x, y| x & y)
3087    }
3088    fn arsh<'a>(
3089        &mut self,
3090        context: &mut Self::Context<'a>,
3091        dst: Register,
3092        src: Source,
3093    ) -> Result<(), String> {
3094        self.alu(Some("arsh32"), context, dst, src, AluType::Plain, |x, y| {
3095            alu32(x, y, |x, y| x.ashr(y))
3096        })
3097    }
3098    fn arsh64<'a>(
3099        &mut self,
3100        context: &mut Self::Context<'a>,
3101        dst: Register,
3102        src: Source,
3103    ) -> Result<(), String> {
3104        self.alu(Some("arsh"), context, dst, src, AluType::Plain, |x, y| x.ashr(y))
3105    }
3106    fn div<'a>(
3107        &mut self,
3108        context: &mut Self::Context<'a>,
3109        dst: Register,
3110        src: Source,
3111    ) -> Result<(), String> {
3112        self.alu(Some("div32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x / y))
3113    }
3114    fn div64<'a>(
3115        &mut self,
3116        context: &mut Self::Context<'a>,
3117        dst: Register,
3118        src: Source,
3119    ) -> Result<(), String> {
3120        self.alu(Some("div"), context, dst, src, AluType::Plain, |x, y| x / y)
3121    }
3122    fn lsh<'a>(
3123        &mut self,
3124        context: &mut Self::Context<'a>,
3125        dst: Register,
3126        src: Source,
3127    ) -> Result<(), String> {
3128        self.alu(Some("lsh32"), context, dst, src, AluType::Plain, |x, y| {
3129            alu32(x, y, |x, y| x << y)
3130        })
3131    }
3132    fn lsh64<'a>(
3133        &mut self,
3134        context: &mut Self::Context<'a>,
3135        dst: Register,
3136        src: Source,
3137    ) -> Result<(), String> {
3138        self.alu(Some("lsh"), context, dst, src, AluType::Plain, |x, y| x << y)
3139    }
3140    fn r#mod<'a>(
3141        &mut self,
3142        context: &mut Self::Context<'a>,
3143        dst: Register,
3144        src: Source,
3145    ) -> Result<(), String> {
3146        self.alu(Some("mod32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x % y))
3147    }
3148    fn mod64<'a>(
3149        &mut self,
3150        context: &mut Self::Context<'a>,
3151        dst: Register,
3152        src: Source,
3153    ) -> Result<(), String> {
3154        self.alu(Some("mod"), context, dst, src, AluType::Plain, |x, y| x % y)
3155    }
3156    fn mov<'a>(
3157        &mut self,
3158        context: &mut Self::Context<'a>,
3159        dst: Register,
3160        src: Source,
3161    ) -> Result<(), String> {
3162        bpf_log!(self, context, "mov32 {}, {}", display_register(dst), display_source(src));
3163        let src = self.compute_source(src)?;
3164        let value = match src {
3165            Type::ScalarValue(data) => {
3166                let value = (data.value as u32) as u64;
3167                let unknown_mask = (data.unknown_mask as u32) as u64;
3168                let unwritten_mask = (data.unwritten_mask as u32) as u64;
3169                let urange = U64Range::compute_range_for_bytes_swap(0.into(), data.urange, 0, 0, 4);
3170                Type::ScalarValue(ScalarValueData::new(value, unknown_mask, unwritten_mask, urange))
3171            }
3172            _ => Type::default(),
3173        };
3174        let mut next = self.next()?;
3175        next.set_reg(dst, value)?;
3176        context.states.push(next);
3177        Ok(())
3178    }
3179    fn mov64<'a>(
3180        &mut self,
3181        context: &mut Self::Context<'a>,
3182        dst: Register,
3183        src: Source,
3184    ) -> Result<(), String> {
3185        bpf_log!(self, context, "mov {}, {}", display_register(dst), display_source(src));
3186        let src = self.compute_source(src)?;
3187        let mut next = self.next()?;
3188        next.set_reg(dst, src)?;
3189        context.states.push(next);
3190        Ok(())
3191    }
3192    fn mul<'a>(
3193        &mut self,
3194        context: &mut Self::Context<'a>,
3195        dst: Register,
3196        src: Source,
3197    ) -> Result<(), String> {
3198        self.alu(Some("mul32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x * y))
3199    }
3200    fn mul64<'a>(
3201        &mut self,
3202        context: &mut Self::Context<'a>,
3203        dst: Register,
3204        src: Source,
3205    ) -> Result<(), String> {
3206        self.alu(Some("mul"), context, dst, src, AluType::Plain, |x, y| x * y)
3207    }
3208    fn or<'a>(
3209        &mut self,
3210        context: &mut Self::Context<'a>,
3211        dst: Register,
3212        src: Source,
3213    ) -> Result<(), String> {
3214        self.alu(Some("or32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x | y))
3215    }
3216    fn or64<'a>(
3217        &mut self,
3218        context: &mut Self::Context<'a>,
3219        dst: Register,
3220        src: Source,
3221    ) -> Result<(), String> {
3222        self.alu(Some("or"), context, dst, src, AluType::Plain, |x, y| x | y)
3223    }
3224    fn rsh<'a>(
3225        &mut self,
3226        context: &mut Self::Context<'a>,
3227        dst: Register,
3228        src: Source,
3229    ) -> Result<(), String> {
3230        self.alu(Some("rsh32"), context, dst, src, AluType::Plain, |x, y| {
3231            alu32(x, y, |x, y| x >> y)
3232        })
3233    }
3234    fn rsh64<'a>(
3235        &mut self,
3236        context: &mut Self::Context<'a>,
3237        dst: Register,
3238        src: Source,
3239    ) -> Result<(), String> {
3240        self.alu(Some("rsh"), context, dst, src, AluType::Plain, |x, y| x >> y)
3241    }
3242    fn sub<'a>(
3243        &mut self,
3244        context: &mut Self::Context<'a>,
3245        dst: Register,
3246        src: Source,
3247    ) -> Result<(), String> {
3248        self.alu(Some("sub32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x - y))
3249    }
3250    fn sub64<'a>(
3251        &mut self,
3252        context: &mut Self::Context<'a>,
3253        dst: Register,
3254        src: Source,
3255    ) -> Result<(), String> {
3256        self.alu(Some("sub"), context, dst, src, AluType::Sub, |x, y| x - y)
3257    }
3258    fn xor<'a>(
3259        &mut self,
3260        context: &mut Self::Context<'a>,
3261        dst: Register,
3262        src: Source,
3263    ) -> Result<(), String> {
3264        self.alu(Some("xor32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x ^ y))
3265    }
3266    fn xor64<'a>(
3267        &mut self,
3268        context: &mut Self::Context<'a>,
3269        dst: Register,
3270        src: Source,
3271    ) -> Result<(), String> {
3272        self.alu(Some("xor"), context, dst, src, AluType::Plain, |x, y| x ^ y)
3273    }
3274
3275    fn neg<'a>(&mut self, context: &mut Self::Context<'a>, dst: Register) -> Result<(), String> {
3276        bpf_log!(self, context, "neg32 {}", display_register(dst));
3277        self.alu(None, context, dst, Source::Value(0), AluType::Plain, |x, y| {
3278            alu32(x, y, |x, _y| -x)
3279        })
3280    }
3281    fn neg64<'a>(&mut self, context: &mut Self::Context<'a>, dst: Register) -> Result<(), String> {
3282        bpf_log!(self, context, "neg {}", display_register(dst));
3283        self.alu(None, context, dst, Source::Value(0), AluType::Plain, |x, _y| -x)
3284    }
3285
3286    fn be<'a>(
3287        &mut self,
3288        context: &mut Self::Context<'a>,
3289        dst: Register,
3290        width: DataWidth,
3291    ) -> Result<(), String> {
3292        self.endianness::<BigEndian>("be", context, dst, width)
3293    }
3294
3295    fn le<'a>(
3296        &mut self,
3297        context: &mut Self::Context<'a>,
3298        dst: Register,
3299        width: DataWidth,
3300    ) -> Result<(), String> {
3301        self.endianness::<LittleEndian>("le", context, dst, width)
3302    }
3303
3304    fn call_external<'a>(
3305        &mut self,
3306        context: &mut Self::Context<'a>,
3307        index: u32,
3308    ) -> Result<(), String> {
3309        bpf_log!(self, context, "call 0x{:x}", index);
3310        let Some(helper) = context.calling_context.helpers.get(&index).cloned() else {
3311            return Err(format!("unknown external function {}", index));
3312        };
3313        let HelperDefinition { signature, name, .. } = helper;
3314        debug_assert!(signature.args.len() <= 5);
3315        let mut next = self.next()?;
3316        for (arg_index, arg) in signature.args.iter().enumerate() {
3317            let reg = (arg_index + 1) as u8;
3318            self.reg(reg)?.match_parameter_type(context, self, name, arg, arg_index, &mut next)?
3319        }
3320        // Parameters have been validated, specify the return value on return.
3321        if signature.invalidate_array_bounds {
3322            next.array_bounds.clear();
3323        }
3324        let value =
3325            self.resolve_return_value(context, &signature.return_value, &mut next, false)?;
3326        next.set_reg(0, value)?;
3327        for i in 1..=5 {
3328            next.set_reg(i, Type::default())?;
3329        }
3330        context.states.push(next);
3331        Ok(())
3332    }
3333
3334    fn exit<'a>(&mut self, context: &mut Self::Context<'a>) -> Result<(), String> {
3335        bpf_log!(self, context, "exit");
3336        if !self.reg(0)?.is_written_scalar() {
3337            return Err("register 0 is incorrect at exit time".to_string());
3338        }
3339        if !self.resources.is_empty() {
3340            return Err("some resources have not been released at exit time".to_string());
3341        }
3342        let this = self.clone();
3343        this.terminate(context)?;
3344        // Nothing to do, the program terminated with a valid scalar value.
3345        Ok(())
3346    }
3347
3348    fn jump<'a>(&mut self, context: &mut Self::Context<'a>, offset: i16) -> Result<(), String> {
3349        bpf_log!(self, context, "ja {}", offset);
3350        let parent = Some(Arc::new(self.clone()));
3351        context.states.push(self.jump_with_offset(offset, parent)?);
3352        Ok(())
3353    }
3354
3355    fn jeq<'a>(
3356        &mut self,
3357        context: &mut Self::Context<'a>,
3358        dst: Register,
3359        src: Source,
3360        offset: i16,
3361    ) -> Result<(), String> {
3362        self.conditional_jump(
3363            "jeq32",
3364            context,
3365            dst,
3366            src,
3367            offset,
3368            JumpType::Eq,
3369            JumpWidth::W32,
3370            |x, y| {
3371                comp32(x, y, |x, y| {
3372                    // x == y
3373                    if x.min == x.max && x.min == y.min && x.min == y.max {
3374                        return Some(true);
3375                    }
3376                    if x.max < y.min || y.max < x.min {
3377                        return Some(false);
3378                    }
3379                    None
3380                })
3381            },
3382        )
3383    }
3384    fn jeq64<'a>(
3385        &mut self,
3386        context: &mut Self::Context<'a>,
3387        dst: Register,
3388        src: Source,
3389        offset: i16,
3390    ) -> Result<(), String> {
3391        self.conditional_jump(
3392            "jeq",
3393            context,
3394            dst,
3395            src,
3396            offset,
3397            JumpType::Eq,
3398            JumpWidth::W64,
3399            |x, y| {
3400                comp64(x, y, |x, y| {
3401                    // x == y
3402                    if x.min == x.max && x.min == y.min && x.min == y.max {
3403                        return Some(true);
3404                    }
3405                    if x.max < y.min || y.max < x.min {
3406                        return Some(false);
3407                    }
3408                    None
3409                })
3410            },
3411        )
3412    }
3413    fn jne<'a>(
3414        &mut self,
3415        context: &mut Self::Context<'a>,
3416        dst: Register,
3417        src: Source,
3418        offset: i16,
3419    ) -> Result<(), String> {
3420        self.conditional_jump(
3421            "jne32",
3422            context,
3423            dst,
3424            src,
3425            offset,
3426            JumpType::Ne,
3427            JumpWidth::W32,
3428            |x, y| {
3429                comp32(x, y, |x, y| {
3430                    // x != y
3431                    if x.min == x.max && x.min == y.min && x.min == y.max {
3432                        return Some(false);
3433                    }
3434                    if x.max < y.min || y.max < x.min {
3435                        return Some(true);
3436                    }
3437                    None
3438                })
3439            },
3440        )
3441    }
3442    fn jne64<'a>(
3443        &mut self,
3444        context: &mut Self::Context<'a>,
3445        dst: Register,
3446        src: Source,
3447        offset: i16,
3448    ) -> Result<(), String> {
3449        self.conditional_jump(
3450            "jne",
3451            context,
3452            dst,
3453            src,
3454            offset,
3455            JumpType::Ne,
3456            JumpWidth::W64,
3457            |x, y| {
3458                comp64(x, y, |x, y| {
3459                    // x != y
3460                    if x.min == x.max && x.min == y.min && x.min == y.max {
3461                        return Some(false);
3462                    }
3463                    if x.max < y.min || y.max < x.min {
3464                        return Some(true);
3465                    }
3466                    None
3467                })
3468            },
3469        )
3470    }
3471    fn jge<'a>(
3472        &mut self,
3473        context: &mut Self::Context<'a>,
3474        dst: Register,
3475        src: Source,
3476        offset: i16,
3477    ) -> Result<(), String> {
3478        self.conditional_jump(
3479            "jge32",
3480            context,
3481            dst,
3482            src,
3483            offset,
3484            JumpType::Ge,
3485            JumpWidth::W32,
3486            |x, y| {
3487                comp32(x, y, |x, y| {
3488                    // x >= y
3489                    if x.min >= y.max {
3490                        return Some(true);
3491                    }
3492                    if y.min > x.max {
3493                        return Some(false);
3494                    }
3495                    None
3496                })
3497            },
3498        )
3499    }
3500    fn jge64<'a>(
3501        &mut self,
3502        context: &mut Self::Context<'a>,
3503        dst: Register,
3504        src: Source,
3505        offset: i16,
3506    ) -> Result<(), String> {
3507        self.conditional_jump(
3508            "jge",
3509            context,
3510            dst,
3511            src,
3512            offset,
3513            JumpType::Ge,
3514            JumpWidth::W64,
3515            |x, y| {
3516                comp64(x, y, |x, y| {
3517                    // x >= y
3518                    if x.min >= y.max {
3519                        return Some(true);
3520                    }
3521                    if y.min > x.max {
3522                        return Some(false);
3523                    }
3524                    None
3525                })
3526            },
3527        )
3528    }
3529    fn jgt<'a>(
3530        &mut self,
3531        context: &mut Self::Context<'a>,
3532        dst: Register,
3533        src: Source,
3534        offset: i16,
3535    ) -> Result<(), String> {
3536        self.conditional_jump(
3537            "jgt32",
3538            context,
3539            dst,
3540            src,
3541            offset,
3542            JumpType::Gt,
3543            JumpWidth::W32,
3544            |x, y| {
3545                comp32(x, y, |x, y| {
3546                    // x > y
3547                    if x.min > y.max {
3548                        return Some(true);
3549                    }
3550                    if y.min >= x.max {
3551                        return Some(false);
3552                    }
3553                    None
3554                })
3555            },
3556        )
3557    }
3558    fn jgt64<'a>(
3559        &mut self,
3560        context: &mut Self::Context<'a>,
3561        dst: Register,
3562        src: Source,
3563        offset: i16,
3564    ) -> Result<(), String> {
3565        self.conditional_jump(
3566            "jgt",
3567            context,
3568            dst,
3569            src,
3570            offset,
3571            JumpType::Gt,
3572            JumpWidth::W64,
3573            |x, y| {
3574                comp64(x, y, |x, y| {
3575                    // x > y
3576                    if x.min > y.max {
3577                        return Some(true);
3578                    }
3579                    if y.min >= x.max {
3580                        return Some(false);
3581                    }
3582                    None
3583                })
3584            },
3585        )
3586    }
3587    fn jle<'a>(
3588        &mut self,
3589        context: &mut Self::Context<'a>,
3590        dst: Register,
3591        src: Source,
3592        offset: i16,
3593    ) -> Result<(), String> {
3594        self.conditional_jump(
3595            "jle32",
3596            context,
3597            dst,
3598            src,
3599            offset,
3600            JumpType::Le,
3601            JumpWidth::W32,
3602            |x, y| {
3603                comp32(x, y, |x, y| {
3604                    // x <= y
3605                    if x.max <= y.min {
3606                        return Some(true);
3607                    }
3608                    if y.max < x.min {
3609                        return Some(false);
3610                    }
3611                    None
3612                })
3613            },
3614        )
3615    }
3616    fn jle64<'a>(
3617        &mut self,
3618        context: &mut Self::Context<'a>,
3619        dst: Register,
3620        src: Source,
3621        offset: i16,
3622    ) -> Result<(), String> {
3623        self.conditional_jump(
3624            "jle",
3625            context,
3626            dst,
3627            src,
3628            offset,
3629            JumpType::Le,
3630            JumpWidth::W64,
3631            |x, y| {
3632                comp64(x, y, |x, y| {
3633                    // x <= y
3634                    if x.max <= y.min {
3635                        return Some(true);
3636                    }
3637                    if y.max < x.min {
3638                        return Some(false);
3639                    }
3640                    None
3641                })
3642            },
3643        )
3644    }
3645    fn jlt<'a>(
3646        &mut self,
3647        context: &mut Self::Context<'a>,
3648        dst: Register,
3649        src: Source,
3650        offset: i16,
3651    ) -> Result<(), String> {
3652        self.conditional_jump(
3653            "jlt32",
3654            context,
3655            dst,
3656            src,
3657            offset,
3658            JumpType::Lt,
3659            JumpWidth::W32,
3660            |x, y| {
3661                comp32(x, y, |x, y| {
3662                    // x < y
3663                    if x.max < y.min {
3664                        return Some(true);
3665                    }
3666                    if y.max <= x.min {
3667                        return Some(false);
3668                    }
3669                    None
3670                })
3671            },
3672        )
3673    }
3674    fn jlt64<'a>(
3675        &mut self,
3676        context: &mut Self::Context<'a>,
3677        dst: Register,
3678        src: Source,
3679        offset: i16,
3680    ) -> Result<(), String> {
3681        self.conditional_jump(
3682            "jlt",
3683            context,
3684            dst,
3685            src,
3686            offset,
3687            JumpType::Lt,
3688            JumpWidth::W64,
3689            |x, y| {
3690                comp64(x, y, |x, y| {
3691                    // x < y
3692                    if x.max < y.min {
3693                        return Some(true);
3694                    }
3695                    if y.max <= x.min {
3696                        return Some(false);
3697                    }
3698                    None
3699                })
3700            },
3701        )
3702    }
3703    fn jsge<'a>(
3704        &mut self,
3705        context: &mut Self::Context<'a>,
3706        dst: Register,
3707        src: Source,
3708        offset: i16,
3709    ) -> Result<(), String> {
3710        self.conditional_jump(
3711            "jsge32",
3712            context,
3713            dst,
3714            src,
3715            offset,
3716            JumpType::LooseComparaison,
3717            JumpWidth::W32,
3718            |x, y| scomp32(x, y, |x, y| x >= y),
3719        )
3720    }
3721    fn jsge64<'a>(
3722        &mut self,
3723        context: &mut Self::Context<'a>,
3724        dst: Register,
3725        src: Source,
3726        offset: i16,
3727    ) -> Result<(), String> {
3728        self.conditional_jump(
3729            "jsge",
3730            context,
3731            dst,
3732            src,
3733            offset,
3734            JumpType::LooseComparaison,
3735            JumpWidth::W64,
3736            |x, y| scomp64(x, y, |x, y| x >= y),
3737        )
3738    }
3739    fn jsgt<'a>(
3740        &mut self,
3741        context: &mut Self::Context<'a>,
3742        dst: Register,
3743        src: Source,
3744        offset: i16,
3745    ) -> Result<(), String> {
3746        self.conditional_jump(
3747            "jsgt32",
3748            context,
3749            dst,
3750            src,
3751            offset,
3752            JumpType::StrictComparaison,
3753            JumpWidth::W32,
3754            |x, y| scomp32(x, y, |x, y| x > y),
3755        )
3756    }
3757    fn jsgt64<'a>(
3758        &mut self,
3759        context: &mut Self::Context<'a>,
3760        dst: Register,
3761        src: Source,
3762        offset: i16,
3763    ) -> Result<(), String> {
3764        self.conditional_jump(
3765            "jsgt",
3766            context,
3767            dst,
3768            src,
3769            offset,
3770            JumpType::StrictComparaison,
3771            JumpWidth::W64,
3772            |x, y| scomp64(x, y, |x, y| x > y),
3773        )
3774    }
3775    fn jsle<'a>(
3776        &mut self,
3777        context: &mut Self::Context<'a>,
3778        dst: Register,
3779        src: Source,
3780        offset: i16,
3781    ) -> Result<(), String> {
3782        self.conditional_jump(
3783            "jsle32",
3784            context,
3785            dst,
3786            src,
3787            offset,
3788            JumpType::LooseComparaison,
3789            JumpWidth::W32,
3790            |x, y| scomp32(x, y, |x, y| x <= y),
3791        )
3792    }
3793    fn jsle64<'a>(
3794        &mut self,
3795        context: &mut Self::Context<'a>,
3796        dst: Register,
3797        src: Source,
3798        offset: i16,
3799    ) -> Result<(), String> {
3800        self.conditional_jump(
3801            "jsle",
3802            context,
3803            dst,
3804            src,
3805            offset,
3806            JumpType::LooseComparaison,
3807            JumpWidth::W64,
3808            |x, y| scomp64(x, y, |x, y| x <= y),
3809        )
3810    }
3811    fn jslt<'a>(
3812        &mut self,
3813        context: &mut Self::Context<'a>,
3814        dst: Register,
3815        src: Source,
3816        offset: i16,
3817    ) -> Result<(), String> {
3818        self.conditional_jump(
3819            "jslt32",
3820            context,
3821            dst,
3822            src,
3823            offset,
3824            JumpType::StrictComparaison,
3825            JumpWidth::W32,
3826            |x, y| scomp32(x, y, |x, y| x < y),
3827        )
3828    }
3829    fn jslt64<'a>(
3830        &mut self,
3831        context: &mut Self::Context<'a>,
3832        dst: Register,
3833        src: Source,
3834        offset: i16,
3835    ) -> Result<(), String> {
3836        self.conditional_jump(
3837            "jslt",
3838            context,
3839            dst,
3840            src,
3841            offset,
3842            JumpType::StrictComparaison,
3843            JumpWidth::W64,
3844            |x, y| scomp64(x, y, |x, y| x < y),
3845        )
3846    }
3847    fn jset<'a>(
3848        &mut self,
3849        context: &mut Self::Context<'a>,
3850        dst: Register,
3851        src: Source,
3852        offset: i16,
3853    ) -> Result<(), String> {
3854        self.conditional_jump(
3855            "jset32",
3856            context,
3857            dst,
3858            src,
3859            offset,
3860            JumpType::Unknown,
3861            JumpWidth::W32,
3862            |x, y| {
3863                comp32(x, y, |x, y| {
3864                    // x & y != 0
3865                    if x.min != x.max || y.min != y.max {
3866                        return None;
3867                    }
3868                    Some(x.min & y.min != 0)
3869                })
3870            },
3871        )
3872    }
3873    fn jset64<'a>(
3874        &mut self,
3875        context: &mut Self::Context<'a>,
3876        dst: Register,
3877        src: Source,
3878        offset: i16,
3879    ) -> Result<(), String> {
3880        self.conditional_jump(
3881            "jset",
3882            context,
3883            dst,
3884            src,
3885            offset,
3886            JumpType::Unknown,
3887            JumpWidth::W64,
3888            |x, y| {
3889                comp64(x, y, |x, y| {
3890                    // x & y != 0
3891                    if x.min != x.max || y.min != y.max {
3892                        return None;
3893                    }
3894                    Some(x.min & y.min != 0)
3895                })
3896            },
3897        )
3898    }
3899
3900    fn atomic_add<'a>(
3901        &mut self,
3902        context: &mut Self::Context<'a>,
3903        fetch: bool,
3904        dst: Register,
3905        offset: i16,
3906        src: Register,
3907    ) -> Result<(), String> {
3908        self.atomic_operation(
3909            "add32",
3910            context,
3911            DataWidth::U32,
3912            fetch,
3913            dst,
3914            offset,
3915            src,
3916            AluType::Add,
3917            |x, y| alu32(x, y, |x, y| x + y),
3918        )
3919    }
3920
3921    fn atomic_add64<'a>(
3922        &mut self,
3923        context: &mut Self::Context<'a>,
3924        fetch: bool,
3925        dst: Register,
3926        offset: i16,
3927        src: Register,
3928    ) -> Result<(), String> {
3929        self.atomic_operation(
3930            "add",
3931            context,
3932            DataWidth::U64,
3933            fetch,
3934            dst,
3935            offset,
3936            src,
3937            AluType::Add,
3938            |x, y| x + y,
3939        )
3940    }
3941
3942    fn atomic_and<'a>(
3943        &mut self,
3944        context: &mut Self::Context<'a>,
3945        fetch: bool,
3946        dst: Register,
3947        offset: i16,
3948        src: Register,
3949    ) -> Result<(), String> {
3950        self.atomic_operation(
3951            "and32",
3952            context,
3953            DataWidth::U32,
3954            fetch,
3955            dst,
3956            offset,
3957            src,
3958            AluType::Plain,
3959            |x, y| alu32(x, y, |x, y| x & y),
3960        )
3961    }
3962
3963    fn atomic_and64<'a>(
3964        &mut self,
3965        context: &mut Self::Context<'a>,
3966        fetch: bool,
3967        dst: Register,
3968        offset: i16,
3969        src: Register,
3970    ) -> Result<(), String> {
3971        self.atomic_operation(
3972            "and",
3973            context,
3974            DataWidth::U64,
3975            fetch,
3976            dst,
3977            offset,
3978            src,
3979            AluType::Plain,
3980            |x, y| x & y,
3981        )
3982    }
3983
3984    fn atomic_or<'a>(
3985        &mut self,
3986        context: &mut Self::Context<'a>,
3987        fetch: bool,
3988        dst: Register,
3989        offset: i16,
3990        src: Register,
3991    ) -> Result<(), String> {
3992        self.atomic_operation(
3993            "or32",
3994            context,
3995            DataWidth::U32,
3996            fetch,
3997            dst,
3998            offset,
3999            src,
4000            AluType::Plain,
4001            |x, y| alu32(x, y, |x, y| x | y),
4002        )
4003    }
4004
4005    fn atomic_or64<'a>(
4006        &mut self,
4007        context: &mut Self::Context<'a>,
4008        fetch: bool,
4009        dst: Register,
4010        offset: i16,
4011        src: Register,
4012    ) -> Result<(), String> {
4013        self.atomic_operation(
4014            "or",
4015            context,
4016            DataWidth::U64,
4017            fetch,
4018            dst,
4019            offset,
4020            src,
4021            AluType::Plain,
4022            |x, y| x | y,
4023        )
4024    }
4025
4026    fn atomic_xor<'a>(
4027        &mut self,
4028        context: &mut Self::Context<'a>,
4029        fetch: bool,
4030        dst: Register,
4031        offset: i16,
4032        src: Register,
4033    ) -> Result<(), String> {
4034        self.atomic_operation(
4035            "xor32",
4036            context,
4037            DataWidth::U32,
4038            fetch,
4039            dst,
4040            offset,
4041            src,
4042            AluType::Plain,
4043            |x, y| alu32(x, y, |x, y| x ^ y),
4044        )
4045    }
4046
4047    fn atomic_xor64<'a>(
4048        &mut self,
4049        context: &mut Self::Context<'a>,
4050        fetch: bool,
4051        dst: Register,
4052        offset: i16,
4053        src: Register,
4054    ) -> Result<(), String> {
4055        self.atomic_operation(
4056            "xor",
4057            context,
4058            DataWidth::U64,
4059            fetch,
4060            dst,
4061            offset,
4062            src,
4063            AluType::Plain,
4064            |x, y| x ^ y,
4065        )
4066    }
4067
4068    fn atomic_xchg<'a>(
4069        &mut self,
4070        context: &mut Self::Context<'a>,
4071        fetch: bool,
4072        dst: Register,
4073        offset: i16,
4074        src: Register,
4075    ) -> Result<(), String> {
4076        self.atomic_operation(
4077            "xchg32",
4078            context,
4079            DataWidth::U32,
4080            fetch,
4081            dst,
4082            offset,
4083            src,
4084            AluType::Plain,
4085            |_, x| x,
4086        )
4087    }
4088
4089    fn atomic_xchg64<'a>(
4090        &mut self,
4091        context: &mut Self::Context<'a>,
4092        fetch: bool,
4093        dst: Register,
4094        offset: i16,
4095        src: Register,
4096    ) -> Result<(), String> {
4097        self.raw_atomic_operation(
4098            "xchg",
4099            context,
4100            DataWidth::U64,
4101            fetch,
4102            dst,
4103            offset,
4104            src,
4105            |_, _, x| Ok(x),
4106        )
4107    }
4108
4109    fn atomic_cmpxchg<'a>(
4110        &mut self,
4111        context: &mut Self::Context<'a>,
4112        dst: Register,
4113        offset: i16,
4114        src: Register,
4115    ) -> Result<(), String> {
4116        self.raw_atomic_cmpxchg("cmpxchg32", context, dst, offset, src, JumpWidth::W32, |x, y| {
4117            comp32(x, y, |x, y| {
4118                // x == y
4119                if x.min == x.max && x.min == y.min && x.min == y.max {
4120                    return Some(true);
4121                }
4122                if x.max < y.min || y.max < x.min {
4123                    return Some(false);
4124                }
4125                None
4126            })
4127        })
4128    }
4129
4130    fn atomic_cmpxchg64<'a>(
4131        &mut self,
4132        context: &mut Self::Context<'a>,
4133        dst: Register,
4134        offset: i16,
4135        src: Register,
4136    ) -> Result<(), String> {
4137        self.raw_atomic_cmpxchg("cmpxchg", context, dst, offset, src, JumpWidth::W64, |x, y| {
4138            comp64(x, y, |x, y| {
4139                // x == y
4140                if x.min == x.max && x.min == y.min && x.min == y.max {
4141                    return Some(true);
4142                }
4143                if x.max < y.min || y.max < x.min {
4144                    return Some(false);
4145                }
4146                None
4147            })
4148        })
4149    }
4150
4151    fn load<'a>(
4152        &mut self,
4153        context: &mut Self::Context<'a>,
4154        dst: Register,
4155        offset: i16,
4156        src: Register,
4157        width: DataWidth,
4158    ) -> Result<(), String> {
4159        bpf_log!(
4160            self,
4161            context,
4162            "ldx{} {}, [{}{}]",
4163            width.str(),
4164            display_register(dst),
4165            display_register(src),
4166            print_offset(offset)
4167        );
4168        let addr = self.reg(src)?;
4169        let loaded_type = self.load_memory(context, &addr, Field::new(offset, width))?;
4170        let mut next = self.next()?;
4171        next.set_reg(dst, loaded_type)?;
4172        context.states.push(next);
4173        Ok(())
4174    }
4175
4176    fn load64<'a>(
4177        &mut self,
4178        context: &mut Self::Context<'a>,
4179        dst: Register,
4180        src: u8,
4181        lower: u32,
4182    ) -> Result<(), String> {
4183        // The pre-scan has already guaranteed that `pc + 1` is valid and structurally well-formed.
4184        let next_instruction = &context.code[self.pc + 1];
4185
4186        let value = match src {
4187            0 => {
4188                let value = (lower as u64) | (((next_instruction.imm() as u32) as u64) << 32);
4189                bpf_log!(self, context, "lddw {}, 0x{:x}", display_register(dst), value);
4190                Type::from(value)
4191            }
4192            BPF_PSEUDO_MAP_IDX => {
4193                let map_index = lower;
4194                bpf_log!(
4195                    self,
4196                    context,
4197                    "lddw {}, map_by_index({:x})",
4198                    display_register(dst),
4199                    map_index
4200                );
4201                context
4202                    .calling_context
4203                    .maps
4204                    .get(usize::try_from(map_index).unwrap())
4205                    .map(|schema| Type::ConstPtrToMap { id: map_index.into(), schema: *schema })
4206                    .ok_or_else(|| format!("lddw with invalid map index: {}", map_index))?
4207            }
4208            BPF_PSEUDO_MAP_IDX_VALUE => {
4209                let map_index = lower;
4210                let offset = next_instruction.imm();
4211                bpf_log!(
4212                    self,
4213                    context,
4214                    "lddw {}, map_value_by_index({:x})+{offset}",
4215                    display_register(dst),
4216                    map_index
4217                );
4218                let id = context.next_id();
4219                let map_schema = context
4220                    .calling_context
4221                    .maps
4222                    .get(usize::try_from(map_index).unwrap())
4223                    .ok_or_else(|| format!("lddw with invalid map index: {}", map_index))?;
4224
4225                if map_schema.map_type != bpf_map_type_BPF_MAP_TYPE_ARRAY {
4226                    return Err(format!(
4227                        "Invalid map type at index {map_index} for lddw. Expecting array."
4228                    ));
4229                }
4230                if map_schema.max_entries == 0 {
4231                    return Err(format!("Array has no entry."));
4232                }
4233
4234                Type::PtrToMemory {
4235                    id: MemoryId::from(id),
4236                    offset: offset.into(),
4237                    buffer_size: map_schema.value_size.into(),
4238                }
4239            }
4240            _ => {
4241                return Err(format!("invalid lddw"));
4242            }
4243        };
4244
4245        let parent = Some(Arc::new(self.clone()));
4246        let mut next = self.jump_with_offset(1, parent)?;
4247        next.set_reg(dst, value.into())?;
4248
4249        context.states.push(next);
4250        Ok(())
4251    }
4252
4253    fn load_from_packet<'a>(
4254        &mut self,
4255        context: &mut Self::Context<'a>,
4256        dst: Register,
4257        src: Register,
4258        offset: i32,
4259        register_offset: Option<Register>,
4260        width: DataWidth,
4261    ) -> Result<(), String> {
4262        bpf_log!(
4263            self,
4264            context,
4265            "ldp{} {}{}",
4266            width.str(),
4267            register_offset.map(display_register).unwrap_or_else(Default::default),
4268            print_offset(offset)
4269        );
4270
4271        // Verify that `src` refers to a packet.
4272        let src_type = self.reg(src)?;
4273        let src_is_packet = match &context.calling_context.packet_type {
4274            Some(packet_type) => src_type == *packet_type,
4275            None => false,
4276        };
4277        if !src_is_packet {
4278            return Err(format!("R{} is not a packet", src));
4279        }
4280
4281        if let Some(reg) = register_offset {
4282            let reg = self.reg(reg)?;
4283            if !reg.is_written_scalar() {
4284                return Err("access to unwritten offset".to_string());
4285            }
4286        }
4287        // Handle the case where the load succeed.
4288        let mut next = self.next()?;
4289        next.set_reg(dst, Type::UNKNOWN_SCALAR)?;
4290        for i in 1..=5 {
4291            next.set_reg(i, Type::default())?;
4292        }
4293        context.states.push(next);
4294        // Handle the case where the load fails.
4295        if !self.reg(0)?.is_written_scalar() {
4296            return Err("register 0 is incorrect at exit time".to_string());
4297        }
4298        if !self.resources.is_empty() {
4299            return Err("some resources have not been released at exit time".to_string());
4300        }
4301        let this = self.clone();
4302        this.terminate(context)?;
4303        // Nothing to do, the program terminated with a valid scalar value.
4304        Ok(())
4305    }
4306
4307    fn store<'a>(
4308        &mut self,
4309        context: &mut Self::Context<'a>,
4310        dst: Register,
4311        offset: i16,
4312        src: Source,
4313        width: DataWidth,
4314    ) -> Result<(), String> {
4315        let value = match src {
4316            Source::Reg(r) => {
4317                bpf_log!(
4318                    self,
4319                    context,
4320                    "stx{} [{}{}], {}",
4321                    width.str(),
4322                    display_register(dst),
4323                    print_offset(offset),
4324                    display_register(r),
4325                );
4326                self.reg(r)?
4327            }
4328            Source::Value(v) => {
4329                bpf_log!(
4330                    self,
4331                    context,
4332                    "st{} [{}{}], 0x{:x}",
4333                    width.str(),
4334                    display_register(dst),
4335                    print_offset(offset),
4336                    v,
4337                );
4338                Type::from(v & Type::mask(width))
4339            }
4340        };
4341        let mut next = self.next()?;
4342        let addr = self.reg(dst)?;
4343        next.store_memory(context, &addr, Field::new(offset, width), value)?;
4344        context.states.push(next);
4345        Ok(())
4346    }
4347}
4348
4349fn alu32(
4350    x: ScalarValueData,
4351    y: ScalarValueData,
4352    op: impl FnOnce(U32ScalarValueData, U32ScalarValueData) -> U32ScalarValueData,
4353) -> ScalarValueData {
4354    op(U32ScalarValueData::from(x), U32ScalarValueData::from(y)).into()
4355}
4356
4357fn comp64(
4358    x: ScalarValueData,
4359    y: ScalarValueData,
4360    op: impl FnOnce(U64Range, U64Range) -> Option<bool>,
4361) -> Result<Option<bool>, ()> {
4362    if !x.is_fully_initialized() || !y.is_fully_initialized() {
4363        return Err(());
4364    }
4365    Ok(op(x.urange, y.urange))
4366}
4367
4368fn comp32(
4369    x: ScalarValueData,
4370    y: ScalarValueData,
4371    op: impl FnOnce(U32Range, U32Range) -> Option<bool>,
4372) -> Result<Option<bool>, ()> {
4373    let x = U32ScalarValueData::from(x);
4374    let y = U32ScalarValueData::from(y);
4375    if !x.is_fully_initialized() || !y.is_fully_initialized() {
4376        return Err(());
4377    }
4378    Ok(op(x.urange, y.urange))
4379}
4380
4381fn scomp64(
4382    x: ScalarValueData,
4383    y: ScalarValueData,
4384    op: impl FnOnce(i64, i64) -> bool,
4385) -> Result<Option<bool>, ()> {
4386    if !x.is_fully_initialized() || !y.is_fully_initialized() {
4387        return Err(());
4388    }
4389    if !x.is_known() || !y.is_known() {
4390        return Ok(None);
4391    }
4392    Ok(Some(op(x.value as i64, y.value as i64)))
4393}
4394
4395fn scomp32(
4396    x: ScalarValueData,
4397    y: ScalarValueData,
4398    op: impl FnOnce(i32, i32) -> bool,
4399) -> Result<Option<bool>, ()> {
4400    let x = U32ScalarValueData::from(x);
4401    let y = U32ScalarValueData::from(y);
4402    if !x.is_fully_initialized() || !y.is_fully_initialized() {
4403        return Err(());
4404    }
4405    if !x.is_known() || !y.is_known() {
4406        return Ok(None);
4407    }
4408    Ok(Some(op(x.value as i32, y.value as i32)))
4409}
4410
4411fn print_offset<T: Into<i32>>(offset: T) -> String {
4412    let offset: i32 = offset.into();
4413    if offset == 0 {
4414        String::new()
4415    } else if offset > 0 {
4416        format!("+{offset}")
4417    } else {
4418        format!("{offset}")
4419    }
4420}
4421
4422fn run_on_stack_offset<F>(v: StackOffset, f: F) -> StackOffset
4423where
4424    F: FnOnce(ScalarValueData) -> ScalarValueData,
4425{
4426    StackOffset(f(v.reg()))
4427}
4428
4429fn error_and_log<T>(
4430    logger: &mut dyn VerifierLogger,
4431    msg: impl std::string::ToString,
4432) -> Result<T, EbpfError> {
4433    let msg = msg.to_string();
4434    logger.log(msg.as_bytes());
4435    return Err(EbpfError::ProgramVerifyError(msg));
4436}
4437
4438fn associate_orderings(o1: Ordering, o2: Ordering) -> Option<Ordering> {
4439    match (o1, o2) {
4440        (o1, o2) if o1 == o2 => Some(o1),
4441        (o, Ordering::Equal) | (Ordering::Equal, o) => Some(o),
4442        _ => None,
4443    }
4444}
4445
4446#[cfg(test)]
4447mod tests {
4448    use super::*;
4449    use std::collections::BTreeSet;
4450    use test_util::{assert_geq, assert_leq};
4451
4452    #[test]
4453    fn test_type_ordering() {
4454        let t0 = Type::from(0);
4455        let t1 = Type::from(1);
4456        let random = Type::AliasParameter { parameter_index: 8 };
4457        let unknown_written = Type::UNKNOWN_SCALAR;
4458        let unwritten = Type::default();
4459
4460        assert_eq!(t0.partial_cmp(&t0), Some(Ordering::Equal));
4461        assert_eq!(t0.partial_cmp(&t1), None);
4462        assert_eq!(t0.partial_cmp(&random), None);
4463        assert_eq!(t0.partial_cmp(&unknown_written), Some(Ordering::Less));
4464        assert_eq!(t0.partial_cmp(&unwritten), Some(Ordering::Less));
4465
4466        assert_eq!(t1.partial_cmp(&t0), None);
4467        assert_eq!(t1.partial_cmp(&t1), Some(Ordering::Equal));
4468        assert_eq!(t1.partial_cmp(&random), None);
4469        assert_eq!(t1.partial_cmp(&unknown_written), Some(Ordering::Less));
4470        assert_eq!(t1.partial_cmp(&unwritten), Some(Ordering::Less));
4471
4472        assert_eq!(random.partial_cmp(&t0), None);
4473        assert_eq!(random.partial_cmp(&t1), None);
4474        assert_eq!(random.partial_cmp(&random), Some(Ordering::Equal));
4475        assert_eq!(random.partial_cmp(&unknown_written), None);
4476        assert_eq!(random.partial_cmp(&unwritten), Some(Ordering::Less));
4477
4478        assert_eq!(unknown_written.partial_cmp(&t0), Some(Ordering::Greater));
4479        assert_eq!(unknown_written.partial_cmp(&t1), Some(Ordering::Greater));
4480        assert_eq!(unknown_written.partial_cmp(&random), None);
4481        assert_eq!(unknown_written.partial_cmp(&unknown_written), Some(Ordering::Equal));
4482        assert_eq!(unknown_written.partial_cmp(&unwritten), Some(Ordering::Less));
4483
4484        assert_eq!(unwritten.partial_cmp(&t0), Some(Ordering::Greater));
4485        assert_eq!(unwritten.partial_cmp(&t1), Some(Ordering::Greater));
4486        assert_eq!(unwritten.partial_cmp(&random), Some(Ordering::Greater));
4487        assert_eq!(unwritten.partial_cmp(&unknown_written), Some(Ordering::Greater));
4488        assert_eq!(unwritten.partial_cmp(&unwritten), Some(Ordering::Equal));
4489    }
4490
4491    #[test]
4492    fn test_stack_ordering() {
4493        let mut s1 = Stack::default();
4494        let mut s2 = Stack::default();
4495
4496        assert_eq!(s1.partial_cmp(&s2), Some(Ordering::Equal));
4497        s1.set(0, 0.into());
4498        assert_eq!(s1.partial_cmp(&s2), Some(Ordering::Less));
4499        assert_eq!(s2.partial_cmp(&s1), Some(Ordering::Greater));
4500        s2.set(1, 1.into());
4501        assert_eq!(s1.partial_cmp(&s2), None);
4502        assert_eq!(s2.partial_cmp(&s1), None);
4503    }
4504
4505    #[test]
4506    fn test_context_ordering() {
4507        let mut c1 = ComputationContext::default();
4508        let mut c2 = ComputationContext::default();
4509
4510        assert_eq!(c1.partial_cmp(&c2), Some(Ordering::Equal));
4511
4512        c1.array_bounds.insert(1.into(), 5);
4513        assert_eq!(c1.partial_cmp(&c2), Some(Ordering::Less));
4514        assert_eq!(c2.partial_cmp(&c1), Some(Ordering::Greater));
4515
4516        c2.array_bounds.insert(1.into(), 7);
4517        assert_eq!(c1.partial_cmp(&c2), Some(Ordering::Greater));
4518        assert_eq!(c2.partial_cmp(&c1), Some(Ordering::Less));
4519
4520        c1.array_bounds.insert(2.into(), 9);
4521        assert_eq!(c1.partial_cmp(&c2), None);
4522        assert_eq!(c2.partial_cmp(&c1), None);
4523
4524        c2.array_bounds.insert(2.into(), 9);
4525        assert_eq!(c1.partial_cmp(&c2), Some(Ordering::Greater));
4526        assert_eq!(c2.partial_cmp(&c1), Some(Ordering::Less));
4527
4528        c2.array_bounds.insert(3.into(), 12);
4529        assert_eq!(c1.partial_cmp(&c2), Some(Ordering::Greater));
4530        assert_eq!(c2.partial_cmp(&c1), Some(Ordering::Less));
4531
4532        c1.pc = 8;
4533        assert_eq!(c1.partial_cmp(&c2), None);
4534        assert_eq!(c2.partial_cmp(&c1), None);
4535    }
4536
4537    #[test]
4538    fn test_stack_access() {
4539        let mut s = Stack::default();
4540
4541        // Store data in the range [8, 26) and verify that `read_data_ptr()` fails for any
4542        // reads outside of that range.
4543        assert!(s.store(StackOffset(8.into()), Type::UNKNOWN_SCALAR, DataWidth::U64).is_ok());
4544        assert!(s.store(StackOffset(16.into()), Type::UNKNOWN_SCALAR, DataWidth::U64).is_ok());
4545        assert!(s.store(StackOffset(24.into()), Type::UNKNOWN_SCALAR, DataWidth::U16).is_ok());
4546
4547        for offset in 0..32 {
4548            for end in (offset + 1)..32 {
4549                assert_eq!(
4550                    s.read_data_ptr(2, StackOffset(offset.into()), (end - offset) as u64).is_ok(),
4551                    offset >= 8 && end <= 26
4552                );
4553            }
4554        }
4555
4556        // Verify that overflows are handled properly.
4557        assert!(s.read_data_ptr(2, StackOffset(12.into()), u64::MAX - 2).is_err());
4558    }
4559
4560    #[test]
4561    fn test_compute_range_for_bytes_swap() {
4562        // Build a list of interesting values. Interesting values are all possible combination of
4563        // bytes being either 0, max value, or an intermediary value.
4564        let mut values = BTreeSet::<u64>::default();
4565        for v1 in &[0x00, 0x1, u64::MAX] {
4566            for v2 in &[0x00, 0x1, u64::MAX] {
4567                for v3 in &[0x00, 0x1, u64::MAX] {
4568                    values.insert(U64Range::assemble_slices((*v1, *v2, *v3), 1, 1));
4569                }
4570            }
4571        }
4572        // Replace the second byte of old by the first byte of new and return the result.
4573        let store = |old: u64, new: u64| (old & !0xff00) | ((new & 0xff) << 8);
4574
4575        for old in &values {
4576            for new in &values {
4577                let s = store(*old, *new);
4578                for min_old in values.iter().filter(|v| *v <= old) {
4579                    for min_new in values.iter().filter(|v| *v <= new) {
4580                        for max_old in values.iter().filter(|v| *v >= old) {
4581                            for max_new in values.iter().filter(|v| *v >= new) {
4582                                let range = U64Range::compute_range_for_bytes_swap(
4583                                    U64Range::new(*min_old, *max_old),
4584                                    U64Range::new(*min_new, *max_new),
4585                                    1,
4586                                    0,
4587                                    1,
4588                                );
4589                                assert_leq!(range.min, s);
4590                                assert_geq!(range.max, s);
4591                            }
4592                        }
4593                    }
4594                }
4595            }
4596        }
4597    }
4598
4599    #[test]
4600    fn test_type_constraint_ge_gt() {
4601        let mut context = ComputationContext::default();
4602        let (new_lhs, new_rhs) = Type::constraint(
4603            &mut context,
4604            JumpType::Ge,
4605            JumpWidth::W64,
4606            Type::UNKNOWN_SCALAR,
4607            Type::from(10),
4608        )
4609        .unwrap();
4610
4611        if let Type::ScalarValue(data1) = new_lhs {
4612            assert_eq!(data1.min(), 10);
4613            assert_eq!(data1.max(), u64::MAX);
4614        } else {
4615            panic!("Expected ScalarValue");
4616        }
4617
4618        if let Type::ScalarValue(data2) = new_rhs {
4619            assert_eq!(data2.min(), 10);
4620            assert_eq!(data2.max(), 10);
4621        } else {
4622            panic!("Expected ScalarValue");
4623        }
4624
4625        let (new_lhs, _) = Type::constraint(
4626            &mut context,
4627            JumpType::Gt,
4628            JumpWidth::W64,
4629            Type::UNKNOWN_SCALAR,
4630            Type::from(10),
4631        )
4632        .unwrap();
4633
4634        if let Type::ScalarValue(data1) = new_lhs {
4635            assert_eq!(data1.min(), 11);
4636            assert_eq!(data1.max(), u64::MAX);
4637        } else {
4638            panic!("Expected ScalarValue");
4639        }
4640    }
4641
4642    #[test]
4643    fn test_type_constraint_lt_le() {
4644        let mut context = ComputationContext::default();
4645        let (new_lhs, _) = Type::constraint(
4646            &mut context,
4647            JumpType::Lt,
4648            JumpWidth::W64,
4649            Type::UNKNOWN_SCALAR,
4650            Type::from(10),
4651        )
4652        .unwrap();
4653
4654        if let Type::ScalarValue(data1) = new_lhs {
4655            assert_eq!(data1.min(), 0);
4656            assert_eq!(data1.max(), 9);
4657        } else {
4658            panic!("Expected ScalarValue");
4659        }
4660
4661        let (new_lhs, _) = Type::constraint(
4662            &mut context,
4663            JumpType::Le,
4664            JumpWidth::W64,
4665            Type::UNKNOWN_SCALAR,
4666            Type::from(10),
4667        )
4668        .unwrap();
4669
4670        if let Type::ScalarValue(data1) = new_lhs {
4671            assert_eq!(data1.min(), 0);
4672            assert_eq!(data1.max(), 10);
4673        } else {
4674            panic!("Expected ScalarValue");
4675        }
4676    }
4677
4678    #[test]
4679    fn test_type_constraint_w32() {
4680        let mut context = ComputationContext::default();
4681        let (new_lhs, _) = Type::constraint(
4682            &mut context,
4683            JumpType::Eq,
4684            JumpWidth::W32,
4685            Type::ScalarValue(ScalarValueData::UNKNOWN_WRITTEN),
4686            Type::from(10),
4687        )
4688        .unwrap();
4689        if let Type::ScalarValue(data1) = new_lhs {
4690            assert_eq!(data1.min(), 10);
4691            assert_eq!(data1.max(), 0xffff_ffff_0000_000a);
4692        } else {
4693            panic!("Expected ScalarValue");
4694        }
4695    }
4696
4697    #[test]
4698    fn test_type_constraint_null_or() {
4699        let mut context = ComputationContext::default();
4700        let id = MemoryId::new();
4701        let null_or = Type::NullOr { id: id.clone(), inner: Box::new(Type::UNKNOWN_SCALAR) };
4702        let (new_lhs, _) =
4703            Type::constraint(&mut context, JumpType::Eq, JumpWidth::W64, null_or, Type::from(0))
4704                .unwrap();
4705        if let Type::ScalarValue(data) = new_lhs {
4706            assert_eq!(data.value, 0);
4707        } else {
4708            panic!("Expected zero ScalarValue");
4709        }
4710    }
4711
4712    #[test]
4713    fn test_type_constraint_array_bounds() {
4714        let mut context = ComputationContext::default();
4715        let id = MemoryId::new();
4716        let _ = Type::constraint(
4717            &mut context,
4718            JumpType::Le,
4719            JumpWidth::W64,
4720            Type::PtrToArray { id: id.clone(), offset: 22.into() },
4721            Type::PtrToEndArray { id: id.clone() },
4722        )
4723        .unwrap();
4724        assert_eq!(context.array_bounds.get(&id), Some(&22));
4725    }
4726}