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_ATOMIC, BPF_CALL, BPF_CLS_MASK, BPF_CMPXCHG, BPF_DW, BPF_EXIT, BPF_JA, BPF_JMP, BPF_JMP32,
11    BPF_LDDW, BPF_MAX_INSTS, BPF_PSEUDO_MAP_IDX, BPF_PSEUDO_MAP_IDX_VALUE, BPF_STACK_SIZE, BPF_STX,
12    BPF_SUB_OP_MASK, BPF_W, DataWidth, EbpfInstruction, GENERAL_REGISTER_COUNT, MapFlags,
13    MapSchema, REGISTER_COUNT, VerifierError, VerifierErrorKind,
14};
15use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian};
16use linux_uapi::{bpf_map_type, bpf_map_type_BPF_MAP_TYPE_ARRAY};
17use std::cell::Cell;
18use std::cmp::Ordering;
19use std::collections::{BTreeMap, BTreeSet, HashMap};
20use std::rc::Rc;
21use std::sync::Arc;
22use zerocopy::IntoBytes;
23
24const U32_MAX: u64 = u32::MAX as u64;
25
26/// A trait to receive the log from the verifier.
27pub trait VerifierLogger {
28    /// Returns whether logging is currently enabled and can accept output.
29    ///
30    /// Verifiers check this before evaluating log messages or formatting strings.
31    fn is_enabled(&self) -> bool {
32        true
33    }
34
35    /// Log a line. The line is always a correct encoded ASCII string.
36    fn log(&mut self, line: &[u8]);
37}
38
39/// A `VerifierLogger` that drops all its content.
40pub struct NullVerifierLogger;
41
42impl VerifierLogger for NullVerifierLogger {
43    fn is_enabled(&self) -> bool {
44        false
45    }
46
47    fn log(&mut self, line: &[u8]) {
48        debug_assert!(line.is_ascii());
49    }
50}
51
52/// An identifier for a memory buffer accessible by an ebpf program. The identifiers are built as a
53/// chain of unique identifier so that a buffer can contain multiple pointers to the same type and
54/// the verifier can distinguish between the different instances.
55/// The namespace of a `MemoryId`, used to prevent collisions between different
56/// sources of identifiers.
57#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Ord, PartialOrd)]
58pub enum Namespace {
59    /// For static structure identifiers (e.g., `SkBuff`, `BpfSock`) defined in `program_type.rs`.
60    Hardcoded,
61    /// For IDs generated by `MemoryId::new()` using the global counter.
62    Generated,
63    /// For IDs generated by `VerificationContext::next_id()` using the local counter.
64    Verification,
65}
66
67#[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
68pub struct MemoryId {
69    namespace: Namespace,
70    id: u64,
71    parent: Option<Arc<MemoryId>>,
72}
73
74impl MemoryId {
75    pub fn id(&self) -> u64 {
76        self.id
77    }
78}
79
80impl From<u64> for MemoryId {
81    fn from(id: u64) -> Self {
82        Self { namespace: Namespace::Hardcoded, id, parent: None }
83    }
84}
85
86/// A counter that allows to generate new ids for parameters. The namespace is the same as for id
87/// generated for types while verifying an ebpf program. Because different sources of identifiers
88/// use different namespaces, this counter can start at 0.
89static BPF_TYPE_IDENTIFIER_COUNTER: std::sync::atomic::AtomicU64 =
90    std::sync::atomic::AtomicU64::new(0);
91
92impl MemoryId {
93    /// Build a `MemoryId` from a raw u64.
94    ///
95    /// This is supposed to be used by the `ebpf_api` crate to build static identifiers (e.g.
96    /// for hardcoded program contexts like `SkBuff`), but not elsewhere.
97    pub const fn from_raw(id: u64) -> MemoryId {
98        Self { namespace: Namespace::Hardcoded, id, parent: None }
99    }
100
101    pub fn new() -> MemoryId {
102        Self {
103            namespace: Namespace::Generated,
104            id: BPF_TYPE_IDENTIFIER_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
105            parent: None,
106        }
107    }
108
109    /// Build a new id such that `other` is prepended to the chain of parent of `self`.
110    fn prepended(&self, other: MemoryId) -> Self {
111        match &self.parent {
112            None => {
113                MemoryId { namespace: self.namespace, id: self.id, parent: Some(Arc::new(other)) }
114            }
115            Some(parent) => MemoryId {
116                namespace: self.namespace,
117                id: self.id,
118                parent: Some(Arc::new(parent.prepended(other))),
119            },
120        }
121    }
122
123    /// Returns whether the parent of `self` is `parent`
124    fn has_parent(&self, parent: &MemoryId) -> bool {
125        match &self.parent {
126            None => false,
127            Some(p) => p.as_ref() == parent,
128        }
129    }
130
131    /// Returns whether `self` and `other` represent the same type. This is true if both have the
132    /// same id and if they both have parent, parents must also match.
133    fn matches(&self, other: &MemoryId) -> bool {
134        if self.namespace != other.namespace || self.id != other.id {
135            return false;
136        };
137        match (&self.parent, &other.parent) {
138            (Some(p1), Some(p2)) => p1.matches(p2.as_ref()),
139            _ => true,
140        }
141    }
142}
143
144/// The type of a field in a struct pointed by `Type::PtrToStruct`.
145#[derive(Clone, Debug, PartialEq, Eq)]
146pub enum FieldType {
147    /// Read-only scalar value.
148    Scalar { size: usize },
149
150    /// Mutable scalar value.
151    MutableScalar { size: usize },
152
153    /// A pointer to the kernel memory. The full buffer is `buffer_size` bytes long.
154    PtrToMemory { is_32_bit: bool, id: MemoryId, buffer_size: usize },
155
156    /// A nullable pointer to the kernel memory. The full buffer is `buffer_size` bytes long.
157    NullablePtrToMemory { is_32_bit: bool, id: MemoryId, buffer_size: usize },
158
159    /// A pointer to the kernel memory. The full buffer size is determined by an instance of
160    /// `PtrToEndArray` with the same `id`.
161    PtrToArray { is_32_bit: bool, id: MemoryId },
162
163    /// A pointer to the kernel memory that represents the first non accessible byte of a
164    /// `PtrToArray` with the same `id`.
165    PtrToEndArray { is_32_bit: bool, id: MemoryId },
166}
167
168/// Definition of a field in a `Type::PtrToStruct`.
169#[derive(Clone, Debug, PartialEq, Eq)]
170pub struct FieldDescriptor {
171    /// The offset at which the field is located.
172    pub offset: usize,
173
174    /// The type of the pointed memory. Currently the verifier supports only `PtrToArray`,
175    /// `PtrToStruct`, `PtrToEndArray` and `ScalarValue`.
176    pub field_type: FieldType,
177}
178
179impl FieldDescriptor {
180    fn size(&self) -> usize {
181        match self.field_type {
182            FieldType::Scalar { size } | FieldType::MutableScalar { size } => size,
183            FieldType::PtrToMemory { is_32_bit, .. }
184            | FieldType::NullablePtrToMemory { is_32_bit, .. }
185            | FieldType::PtrToArray { is_32_bit, .. }
186            | FieldType::PtrToEndArray { is_32_bit, .. } => {
187                if is_32_bit {
188                    4
189                } else {
190                    8
191                }
192            }
193        }
194    }
195}
196
197/// The offset and width of a field in a struct.
198#[derive(Clone, Copy, Debug, Eq, PartialEq)]
199struct Field {
200    offset: i16,
201    width: DataWidth,
202}
203
204impl Field {
205    fn new(offset: i16, width: DataWidth) -> Self {
206        Self { offset, width }
207    }
208}
209
210/// Defines field layout in a struct pointed by `Type::PtrToStruct`.
211#[derive(Debug, PartialEq, Eq, Default)]
212pub struct StructDescriptor {
213    /// The list of fields.
214    pub fields: Vec<FieldDescriptor>,
215}
216
217impl StructDescriptor {
218    /// Return true if `self` is a subtype of `super_struct`.
219    fn is_subtype(&self, super_struct: &StructDescriptor) -> bool {
220        // Check that `super_struct.fields` is a subset of `fields`.
221        for super_field in super_struct.fields.iter() {
222            if self.fields.iter().find(|field| *field == super_field).is_none() {
223                return false;
224            }
225        }
226        true
227    }
228
229    /// Finds the field type for load/store at the specified location. None is returned if the
230    /// access is invalid and the program must be rejected.
231    fn find_field(&self, base_offset: ScalarValueData, field: Field) -> Option<&FieldDescriptor> {
232        let offset = base_offset + field.offset;
233        let field_desc = self.fields.iter().find(|f| {
234            f.offset <= offset.min() as usize && (offset.max() as usize) < f.offset + f.size()
235        })?;
236        let is_valid_load = match field_desc.field_type {
237            FieldType::Scalar { size } | FieldType::MutableScalar { size } => {
238                // For scalars check that the access is within the bounds of the field.
239                ((offset + field.width.bytes()).max() as usize) <= field_desc.offset + size
240            }
241            FieldType::PtrToMemory { is_32_bit, .. }
242            | FieldType::NullablePtrToMemory { is_32_bit, .. }
243            | FieldType::PtrToArray { is_32_bit, .. }
244            | FieldType::PtrToEndArray { is_32_bit, .. } => {
245                let expected_width = if is_32_bit { DataWidth::U32 } else { DataWidth::U64 };
246                // Pointer loads are expected to load the whole field.
247                offset.is_known()
248                    && offset.value as usize == field_desc.offset
249                    && field.width == expected_width
250            }
251        };
252
253        is_valid_load.then_some(field_desc)
254    }
255}
256
257#[derive(Clone, Debug, PartialEq, Eq)]
258pub enum MemoryParameterSize {
259    /// The memory buffer have the given size.
260    Value(u64),
261    /// The memory buffer size is given by the parameter in the given index.
262    Reference { index: u8 },
263}
264
265impl MemoryParameterSize {
266    fn size(&self, context: &ComputationContext) -> Result<u64, VerifierErrorKind> {
267        match self {
268            Self::Value(size) => Ok(*size),
269            Self::Reference { index } => {
270                let size_type = context.reg(index + 1)?;
271                match size_type {
272                    Type::ScalarValue(data) if data.is_known() => Ok(data.value),
273                    _ => Err(VerifierErrorKind::NonConstantBufferSize),
274                }
275            }
276        }
277    }
278}
279
280#[derive(Clone, Debug, PartialEq, Eq)]
281pub enum MapTypeFilter {
282    AllowList(&'static [bpf_map_type]),
283    DenyList(&'static [bpf_map_type]),
284}
285
286impl MapTypeFilter {
287    pub fn is_allowed(&self, map_type: bpf_map_type) -> bool {
288        match self {
289            MapTypeFilter::AllowList(types) => types.contains(&map_type),
290            MapTypeFilter::DenyList(types) => !types.contains(&map_type),
291        }
292    }
293}
294
295/// Indicates the origin and mutability of a memory buffer referenced by
296/// `Type::PtrToMemory`.
297#[derive(Clone, Copy, Debug, Eq, PartialEq)]
298pub enum PointerOrigin {
299    /// A pointer to a value in a read-only eBPF map (`BPF_F_RDONLY_PROG`).
300    MapReadOnly,
301    /// A pointer to a value in a writable eBPF map.
302    MapWritable,
303    /// A read-only pointer to kernel memory (e.g., packet payload in
304    /// `sk_buff`).
305    KernelReadOnly,
306    /// A writable pointer to kernel memory.
307    KernelWritable,
308}
309
310impl PointerOrigin {
311    /// Returns true if the memory referenced by this pointer can be written to.
312    pub fn is_writable(self) -> bool {
313        match self {
314            Self::MapWritable | Self::KernelWritable => true,
315            Self::MapReadOnly | Self::KernelReadOnly => false,
316        }
317    }
318
319    /// Returns true if the pointer points to an eBPF map value.
320    pub fn is_map_value(self) -> bool {
321        match self {
322            Self::MapReadOnly | Self::MapWritable => true,
323            Self::KernelReadOnly | Self::KernelWritable => false,
324        }
325    }
326}
327
328#[derive(Clone, Debug, PartialEq, Eq)]
329pub enum Type {
330    /// A number.
331    ScalarValue(ScalarValueData),
332    /// A pointer to a map object.
333    ConstPtrToMap { id: u64, schema: MapSchema },
334    /// A pointer into the stack.
335    PtrToStack { offset: StackOffset },
336    /// A pointer to the kernel memory. The full buffer is `buffer_size` bytes long. The pointer is
337    /// situated at `offset` from the start of the buffer.
338    PtrToMemory { id: MemoryId, offset: ScalarValueData, buffer_size: u64, origin: PointerOrigin },
339    /// A pointer to a struct with the specified `StructDescriptor`.
340    PtrToStruct { id: MemoryId, offset: ScalarValueData, descriptor: Arc<StructDescriptor> },
341    /// A pointer to the kernel memory. The full buffer size is determined by an instance of
342    /// `PtrToEndArray` with the same `id`. The pointer is situadted at `offset` from the start of
343    /// the buffer.
344    PtrToArray { id: MemoryId, offset: ScalarValueData },
345    /// A pointer to the kernel memory that represents the first non accessible byte of a
346    /// `PtrToArray` with the same `id`.
347    PtrToEndArray { id: MemoryId },
348    /// A pointer that might be null and must be validated before being referenced.
349    NullOr { id: MemoryId, inner: Arc<Type> },
350    /// An object that must be passed to a method with an associated `ReleaseParameter` before the
351    /// end of the program.
352    Releasable { id: MemoryId, inner: Arc<Type> },
353    /// A function parameter that must be a `ScalarValue` when called.
354    ScalarValueParameter,
355    /// A function parameter that must be a `ConstPtrToMap` when called.
356    ConstPtrToMapParameter { filter: MapTypeFilter, must_be_writable: bool },
357    /// A function parameter that must be a key of a map.
358    MapKeyParameter {
359        /// The index in the arguments list that contains a `ConstPtrToMap` for the map this key is
360        /// associated with.
361        map_ptr_index: u8,
362    },
363    /// A function parameter that must be a value of a map.
364    MapValueParameter {
365        /// The index in the arguments list that contains a `ConstPtrToMap` for the map this key is
366        /// associated with.
367        map_ptr_index: u8,
368    },
369    /// A function parameter that must be a pointer to memory.
370    MemoryParameter {
371        /// The index in the arguments list that contains a scalar value containing the size of the
372        /// memory.
373        size: MemoryParameterSize,
374        /// Whether this memory is read by the function.
375        input: bool,
376        /// Whether this memory is written by the function.
377        output: bool,
378    },
379    /// A function return value that is the same type as a parameter.
380    AliasParameter {
381        /// The index in the argument list of the parameter that has the type of this return value.
382        parameter_index: u8,
383    },
384    /// A function return value that is either null, or the given type.
385    NullOrParameter(Box<Type>),
386    /// A function parameter that must be a pointer to memory with the given id.
387    StructParameter { id: MemoryId },
388    /// The parameter should be the same as the specified argument passed to the program.
389    ContextParameter { parameter_index: u8 },
390    /// A function return value that must be passed to a method with an associated
391    /// `ReleaseParameter` before the end of the program.
392    ReleasableParameter { id: MemoryId, inner: Box<Type> },
393    /// A function parameter that will release the value.
394    ReleaseParameter { id: MemoryId },
395    /// A function parameter that is not checked.
396    AnyParameter,
397}
398
399/// Defines a partial ordering on `Type` instances, capturing the notion of how "broad"
400/// a type is in terms of the set of potential values it represents.
401///
402/// The ordering is defined such that `t1 > t2` if a proof that an eBPF program terminates
403/// in a state where a register or memory location has type `t1` is also a proof that
404/// the program terminates in a state where that location has type `t2`.
405///
406/// In other words, a "broader" type represents a larger set of possible values, and
407/// proving termination with a broader type implies termination with any narrower type.
408///
409/// Examples:
410/// * `Type::ScalarValue(ScalarValueData({ unknown_mask: 0, .. }))` (a known scalar value) is less than
411///   `Type::ScalarValue(ScalarValueData({ unknown_mask: u64::MAX, .. }))` (an unknown scalar value).
412impl PartialOrd for Type {
413    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
414        // UNINITIALIZED is treated as the Top element (Greater than all other types).
415        // If a path is proven safe when a register is UNINITIALIZED, it means the path
416        // does not read that register. Consequently, it is safe to prune any state
417        // that has a more specific type (like a pointer) in that register, because
418        // the program will still not read it.
419        match (self, other) {
420            (Self::ScalarValue(data1), Self::ScalarValue(data2)) => data1.partial_cmp(data2),
421            (Self::ScalarValue(data), _) => {
422                (*data == ScalarValueData::UNINITIALIZED).then_some(Ordering::Greater)
423            }
424            (_, Self::ScalarValue(data)) => {
425                (*data == ScalarValueData::UNINITIALIZED).then_some(Ordering::Less)
426            }
427            // If the values are equal, return the known result.
428            _ if self == other => Some(Ordering::Equal),
429            _ => None,
430        }
431    }
432}
433
434impl From<ScalarValueData> for Type {
435    fn from(value: ScalarValueData) -> Self {
436        Self::ScalarValue(value)
437    }
438}
439
440impl From<u64> for Type {
441    fn from(value: u64) -> Self {
442        Self::ScalarValue(value.into())
443    }
444}
445
446impl Default for Type {
447    /// A new instance of `Type` where no bit has been written yet.
448    fn default() -> Self {
449        Self::UNINITIALIZED.clone()
450    }
451}
452
453impl Type {
454    /// An uninitialized value.
455    pub const UNINITIALIZED: Self = Self::ScalarValue(ScalarValueData::UNINITIALIZED);
456
457    /// A `Type` where the data is usable by userspace, but the value is not known by the verifier.
458    pub const UNKNOWN_SCALAR: Self = Self::ScalarValue(ScalarValueData::UNKNOWN_WRITTEN);
459
460    /// The mask associated with a data of size `width`.
461    fn mask(width: DataWidth) -> u64 {
462        if width == DataWidth::U64 { u64::MAX } else { (1 << width.bits()) - 1 }
463    }
464
465    /// Return true if `self` is a scalar with all bytes initialized.
466    fn is_written_scalar(&self) -> bool {
467        match self {
468            Self::ScalarValue(data) if data.is_fully_initialized() => true,
469            _ => false,
470        }
471    }
472
473    /// Return true if `self` has all bytes initialized.
474    fn is_initialized(&self) -> bool {
475        match self {
476            Self::ScalarValue(data) => data.is_fully_initialized(),
477            _ => true,
478        }
479    }
480
481    /// Return true if `self` is a subtype of `super_type`.
482    pub fn is_subtype(&self, super_type: &Type) -> bool {
483        match (self, super_type) {
484            // Anything can be used in place of an uninitialized value.
485            (_, Self::ScalarValue(data)) if data.is_uninitialized() => true,
486
487            (
488                Self::PtrToStruct { id: id1, offset: offset1, descriptor: descriptor1 },
489                Self::PtrToStruct { id: id2, offset: offset2, descriptor: descriptor2 },
490            ) => id1 == id2 && offset1 <= offset2 && descriptor1.is_subtype(descriptor2),
491
492            // Every type is a subtype of itself.
493            (self_type, super_type) if self_type == super_type => true,
494
495            _ => false,
496        }
497    }
498
499    /// Return true is `self` is guaranteed to be non-zero
500    pub fn is_non_zero(&self) -> bool {
501        match self {
502            Self::ScalarValue(d) => d.min() > 0,
503            Self::NullOr { .. } => false,
504            _ => true,
505        }
506    }
507
508    fn inner(&self, context: &ComputationContext) -> Result<&Type, VerifierErrorKind> {
509        match self {
510            Self::Releasable { id, inner } => {
511                if context.resources.contains(id) {
512                    Ok(&inner)
513                } else {
514                    Err(VerifierErrorKind::UseAfterRelease)
515                }
516            }
517            _ => Ok(self),
518        }
519    }
520
521    /// Returns a copy of this pointer with its offset replaced by `offset`, looking through
522    /// `Releasable` so that the resource stays tracked. Other types are returned unchanged.
523    fn with_offset(&self, offset: ScalarValueData) -> Self {
524        match self {
525            Self::Releasable { id, inner } => {
526                Self::Releasable { id: id.clone(), inner: Arc::new(inner.with_offset(offset)) }
527            }
528            Self::PtrToMemory { id, buffer_size, origin, .. } => Self::PtrToMemory {
529                id: id.clone(),
530                offset,
531                buffer_size: *buffer_size,
532                origin: *origin,
533            },
534            Self::PtrToStruct { id, descriptor, .. } => {
535                Self::PtrToStruct { id: id.clone(), offset, descriptor: descriptor.clone() }
536            }
537            Self::PtrToArray { id, .. } => Self::PtrToArray { id: id.clone(), offset },
538            _ => self.clone(),
539        }
540    }
541
542    /// Constraints `type1` and `type2` for a conditional jump with the specified `jump_type` and
543    /// `jump_width`.
544    fn constraint(
545        context: &mut ComputationContext,
546        jump_type: JumpType,
547        jump_width: JumpWidth,
548        type1: Self,
549        type2: Self,
550    ) -> Result<(Self, Self), VerifierErrorKind> {
551        let result = match (jump_width, jump_type, type1.inner(context)?, type2.inner(context)?) {
552            (JumpWidth::W64, JumpType::Eq, Type::ScalarValue(data1), Type::ScalarValue(data2))
553                if data1.is_fully_initialized() && data2.is_fully_initialized() =>
554            {
555                let umin = std::cmp::max(data1.min(), data2.min());
556                let umax = std::cmp::min(data1.max(), data2.max());
557                let v = Type::ScalarValue(ScalarValueData::new(
558                    data1.value | data2.value,
559                    data1.unknown_mask & data2.unknown_mask,
560                    0,
561                    U64Range::new(umin, umax),
562                ));
563                (v.clone(), v)
564            }
565            (JumpWidth::W32, JumpType::Eq, Type::ScalarValue(data1), Type::ScalarValue(data2))
566                if data1.is_fully_initialized() && data2.is_fully_initialized() =>
567            {
568                // Refine ranges only when both operands fit in 32 bits; a range
569                // straddling 2^32 can have low bits below its min, so refining would
570                // drop reachable values.
571                let both_fit_u32 = data1.max() <= U32_MAX && data2.max() <= U32_MAX;
572                let (urange1, urange2) = if both_fit_u32 {
573                    let umin = std::cmp::max(data1.min(), data2.min());
574                    let umax = std::cmp::min(data1.max(), data2.max());
575                    let urange = U64Range::new(umin, umax);
576                    (urange, urange)
577                } else {
578                    (data1.urange, data2.urange)
579                };
580                let v1 = Type::ScalarValue(ScalarValueData::new(
581                    data1.value | (data2.value & U32_MAX),
582                    data1.unknown_mask & (data2.unknown_mask | (U32_MAX << 32)),
583                    0,
584                    urange1,
585                ));
586                let v2 = Type::ScalarValue(ScalarValueData::new(
587                    data2.value | (data1.value & U32_MAX),
588                    data2.unknown_mask & (data1.unknown_mask | (U32_MAX << 32)),
589                    0,
590                    urange2,
591                ));
592                (v1, v2)
593            }
594            (JumpWidth::W64, JumpType::Eq, Type::ScalarValue(data), Type::NullOr { id, .. })
595            | (JumpWidth::W64, JumpType::Eq, Type::NullOr { id, .. }, Type::ScalarValue(data))
596                if data.is_zero() =>
597            {
598                context.set_null(id, true);
599                let zero = Type::from(0);
600                (zero.clone(), zero)
601            }
602            (JumpWidth::W64, jump_type, Type::NullOr { id, inner }, Type::ScalarValue(data))
603                if jump_type.is_strict() && data.is_zero() =>
604            {
605                context.set_null(id, false);
606                let inner = (**inner).clone();
607                inner.register_resource(context);
608                (inner, type2)
609            }
610            (JumpWidth::W64, jump_type, Type::ScalarValue(data), Type::NullOr { id, inner })
611                if jump_type.is_strict() && data.is_zero() =>
612            {
613                context.set_null(id, false);
614                let inner = (**inner).clone();
615                inner.register_resource(context);
616                (type1, inner)
617            }
618
619            (JumpWidth::W64, JumpType::Lt, Type::ScalarValue(lhs), Type::ScalarValue(rhs)) => {
620                debug_assert!(lhs.min() < u64::MAX);
621                debug_assert!(rhs.max() > 0);
622                let new_max_lhs = std::cmp::min(lhs.max(), rhs.max() - 1);
623                debug_assert!(lhs.min() <= new_max_lhs);
624                let new_min_rhs = std::cmp::max(rhs.min(), lhs.min() + 1);
625                debug_assert!(new_min_rhs <= rhs.max());
626                let new_range_lhs = U64Range::new(lhs.min(), new_max_lhs);
627                let new_range_rhs = U64Range::new(new_min_rhs, rhs.max());
628                (lhs.update_range(new_range_lhs).into(), rhs.update_range(new_range_rhs).into())
629            }
630            (JumpWidth::W64, JumpType::Gt, Type::ScalarValue(lhs), Type::ScalarValue(rhs)) => {
631                debug_assert!(rhs.min() < u64::MAX);
632                debug_assert!(lhs.max() > 0);
633                let new_min_lhs = std::cmp::max(lhs.min(), rhs.min() + 1);
634                debug_assert!(new_min_lhs <= lhs.max());
635                let new_max_rhs = std::cmp::min(rhs.max(), lhs.max() - 1);
636                debug_assert!(rhs.min() <= new_max_rhs);
637                let new_range_lhs = U64Range::new(new_min_lhs, lhs.max());
638                let new_range_rhs = U64Range::new(rhs.min(), new_max_rhs);
639                (lhs.update_range(new_range_lhs).into(), rhs.update_range(new_range_rhs).into())
640            }
641
642            (JumpWidth::W64, JumpType::Le, Type::ScalarValue(lhs), Type::ScalarValue(rhs)) => {
643                let new_max_lhs = std::cmp::min(lhs.max(), rhs.max());
644                debug_assert!(lhs.min() <= new_max_lhs);
645                let new_min_rhs = std::cmp::max(rhs.min(), lhs.min());
646                debug_assert!(new_min_rhs <= rhs.max());
647                let new_range_lhs = U64Range::new(lhs.min(), new_max_lhs);
648                let new_range_rhs = U64Range::new(new_min_rhs, rhs.max());
649                (lhs.update_range(new_range_lhs).into(), rhs.update_range(new_range_rhs).into())
650            }
651            (JumpWidth::W64, JumpType::Ge, Type::ScalarValue(lhs), Type::ScalarValue(rhs)) => {
652                let new_min_lhs = std::cmp::max(lhs.min(), rhs.min());
653                debug_assert!(new_min_lhs <= lhs.max());
654                let new_max_rhs = std::cmp::min(rhs.max(), lhs.max());
655                debug_assert!(rhs.min() <= new_max_rhs);
656                let new_range_lhs = U64Range::new(new_min_lhs, lhs.max());
657                let new_range_rhs = U64Range::new(rhs.min(), new_max_rhs);
658                (lhs.update_range(new_range_lhs).into(), rhs.update_range(new_range_rhs).into())
659            }
660
661            (
662                JumpWidth::W64,
663                JumpType::Eq,
664                Type::PtrToArray { id: id1, offset },
665                Type::PtrToEndArray { id: id2 },
666            )
667            | (
668                JumpWidth::W64,
669                JumpType::Le,
670                Type::PtrToArray { id: id1, offset },
671                Type::PtrToEndArray { id: id2 },
672            )
673            | (
674                JumpWidth::W64,
675                JumpType::Ge,
676                Type::PtrToEndArray { id: id1 },
677                Type::PtrToArray { id: id2, offset },
678            ) if id1 == id2 => {
679                context.update_array_bounds(id1.clone(), *offset);
680                (type1, type2)
681            }
682            (
683                JumpWidth::W64,
684                JumpType::Lt,
685                Type::PtrToArray { id: id1, offset },
686                Type::PtrToEndArray { id: id2 },
687            )
688            | (
689                JumpWidth::W64,
690                JumpType::Gt,
691                Type::PtrToEndArray { id: id1 },
692                Type::PtrToArray { id: id2, offset },
693            ) if id1 == id2 => {
694                context.update_array_bounds(id1.clone(), *offset + 1);
695                (type1, type2)
696            }
697            (
698                JumpWidth::W64,
699                JumpType::Eq,
700                Type::PtrToMemory { id: id1, offset: offset1, .. },
701                Type::PtrToMemory { id: id2, offset: offset2, .. },
702            )
703            | (
704                JumpWidth::W64,
705                JumpType::Eq,
706                Type::PtrToStruct { id: id1, offset: offset1, .. },
707                Type::PtrToStruct { id: id2, offset: offset2, .. },
708            )
709            | (
710                JumpWidth::W64,
711                JumpType::Eq,
712                Type::PtrToArray { id: id1, offset: offset1 },
713                Type::PtrToArray { id: id2, offset: offset2 },
714            ) if id1 == id2 => {
715                // Both pointers share the same base, so they are equal iff their offsets are
716                // equal. Constrain the offsets, and keep the rest of each type (including any
717                // `Releasable` wrapper) so that neither pointer loses information.
718                let (offset, _) = Self::constraint(
719                    context,
720                    JumpType::Eq,
721                    JumpWidth::W64,
722                    Type::ScalarValue(*offset1),
723                    Type::ScalarValue(*offset2),
724                )?;
725                let offset = match offset {
726                    Type::ScalarValue(offset) => offset,
727                    _ => *offset1,
728                };
729                (type1.with_offset(offset), type2.with_offset(offset))
730            }
731            (JumpWidth::W64, JumpType::Eq, _, _) => (type1.clone(), type1),
732            _ => (type1, type2),
733        };
734        Ok(result)
735    }
736
737    fn match_parameter_type(
738        &self,
739        verification_context: &VerificationContext<'_>,
740        context: &ComputationContext,
741        helper_name: &str,
742        parameter_type: &Type,
743        index: usize,
744        next: &mut ComputationContext,
745    ) -> Result<(), VerifierErrorKind> {
746        match (parameter_type, self) {
747            (Type::NullOrParameter(t), Type::ScalarValue(data))
748                if data.is_known() && data.value == 0 =>
749            {
750                Ok(())
751            }
752            (Type::NullOrParameter(t), _) => self.match_parameter_type(
753                verification_context,
754                context,
755                helper_name,
756                t,
757                index,
758                next,
759            ),
760            (Type::ScalarValueParameter, Type::ScalarValue(data))
761                if data.is_fully_initialized() =>
762            {
763                Ok(())
764            }
765            (
766                Type::ConstPtrToMapParameter { filter, must_be_writable },
767                Type::ConstPtrToMap { schema, .. },
768            ) => {
769                if *must_be_writable && schema.flags.contains(MapFlags::ProgReadOnly) {
770                    return Err(VerifierErrorKind::WriteToReadOnlyMap);
771                }
772                let map_type = schema.map_type;
773                if !filter.is_allowed(map_type) {
774                    return Err(VerifierErrorKind::InvalidMapTypeForHelper(map_type));
775                }
776                Ok(())
777            }
778            (
779                Type::MapKeyParameter { map_ptr_index },
780                Type::PtrToMemory { offset, buffer_size, .. },
781            ) => {
782                let schema = context.get_map_schema(*map_ptr_index)?;
783                context.check_memory_access(*offset, *buffer_size, 0, schema.key_size as usize)
784            }
785            (Type::MapKeyParameter { map_ptr_index }, Type::PtrToStack { offset }) => {
786                let schema = context.get_map_schema(*map_ptr_index)?;
787                context.stack.read_data_ptr(*offset, schema.key_size as u64)
788            }
789            (
790                Type::MapValueParameter { map_ptr_index },
791                Type::PtrToMemory { offset, buffer_size, .. },
792            ) => {
793                let schema = context.get_map_schema(*map_ptr_index)?;
794                context.check_memory_access(*offset, *buffer_size, 0, schema.value_size as usize)
795            }
796            (Type::MapValueParameter { map_ptr_index }, Type::PtrToStack { offset }) => {
797                let schema = context.get_map_schema(*map_ptr_index)?;
798                context.stack.read_data_ptr(*offset, schema.value_size as u64)
799            }
800            (
801                Type::MemoryParameter { size, output, .. },
802                Type::PtrToMemory { offset, buffer_size, origin, .. },
803            ) => {
804                if *output && !origin.is_writable() {
805                    return Err(VerifierErrorKind::WriteToReadOnlyMemory);
806                }
807                let expected_size = size.size(context)?;
808                let offset_max = offset.max();
809                if offset_max > *buffer_size {
810                    return Err(VerifierErrorKind::OutOfBoundsMemoryAccess);
811                }
812                let size_left = *buffer_size - offset_max;
813                if expected_size > size_left {
814                    return Err(VerifierErrorKind::OutOfBoundsMemoryAccess);
815                }
816                Ok(())
817            }
818
819            (Type::MemoryParameter { size, input, output }, Type::PtrToStack { offset }) => {
820                let size = size.size(context)?;
821                let buffer_end = offset.add(size);
822                if !buffer_end.is_within_stack() {
823                    Err(VerifierErrorKind::OutOfBoundsMemoryAccess)
824                } else {
825                    if *output {
826                        next.stack.write_data_ptr(*offset, size)?;
827                    }
828                    if *input {
829                        context.stack.read_data_ptr(*offset, size)?;
830                    }
831                    Ok(())
832                }
833            }
834            (
835                Type::StructParameter { id: id1 },
836                Type::PtrToMemory { id: id2, offset, .. }
837                | Type::PtrToStruct { id: id2, offset, .. },
838            ) if offset.is_zero() && id1.matches(id2) => Ok(()),
839            (
840                Type::ReleasableParameter { id: id1, inner: inner1 },
841                Type::Releasable { id: id2, inner: inner2 },
842            ) if id2.has_parent(id1) => {
843                if next.resources.contains(id2) {
844                    inner2.match_parameter_type(
845                        verification_context,
846                        context,
847                        helper_name,
848                        inner1,
849                        index,
850                        next,
851                    )
852                } else {
853                    Err(VerifierErrorKind::UseAfterRelease)
854                }
855            }
856            (Type::ContextParameter { parameter_index }, arg_type) => {
857                if verification_context.calling_context.args.get(*parameter_index as usize)
858                    == Some(arg_type)
859                {
860                    Ok(())
861                } else {
862                    Err(VerifierErrorKind::ExpectedProgramArgument(*parameter_index))
863                }
864            }
865            (Type::ReleaseParameter { id: id1 }, Type::Releasable { id: id2, inner })
866                if id2.has_parent(id1) =>
867            {
868                // Only the unmodified base pointer of a releasable resource may be released.
869                let base_offset = match inner.as_ref() {
870                    Type::PtrToMemory { offset, .. } | Type::PtrToStruct { offset, .. } => offset,
871                    _ => return Err(VerifierErrorKind::HelperArgumentTypeMismatch(index)),
872                };
873                if !base_offset.is_zero() {
874                    return Err(VerifierErrorKind::HelperArgumentTypeMismatch(index));
875                }
876                if next.resources.remove(id2) {
877                    Ok(())
878                } else {
879                    Err(VerifierErrorKind::DoubleRelease)
880                }
881            }
882            (_, Type::Releasable { id, inner }) => {
883                if !next.resources.contains(id) {
884                    return Err(VerifierErrorKind::UseAfterRelease);
885                }
886                inner.match_parameter_type(
887                    verification_context,
888                    context,
889                    helper_name,
890                    parameter_type,
891                    index,
892                    next,
893                )
894            }
895            (Type::AnyParameter, _) => Ok(()),
896
897            _ => Err(VerifierErrorKind::HelperArgumentTypeMismatch(index)),
898        }
899    }
900
901    /// If this `Type` is an instance of NullOr with the given `null_id`, replace it wither either
902    /// 0 or the subtype depending on `is_null`
903    fn set_null(&mut self, null_id: &MemoryId, is_null: bool) {
904        match self {
905            Type::NullOr { id, inner } if id == null_id => {
906                if is_null {
907                    *self = Type::from(0);
908                } else {
909                    *self = (**inner).clone();
910                }
911            }
912            _ => {}
913        }
914    }
915
916    fn register_resource(&self, context: &mut ComputationContext) {
917        match self {
918            Type::Releasable { id, .. } => {
919                context.resources.insert(id.clone());
920            }
921            _ => {}
922        }
923    }
924
925    /// Partially Compares two iterators of comparable items.
926    ///
927    /// This function iterates through both input iterators simultaneously and compares the corresponding elements.
928    /// The comparison continues until:
929    /// 1. Both iterators are exhausted and all elements were considered equal, in which case it returns `Some(Ordering::Equal)`.
930    /// 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.
931    /// 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`.
932    fn compare_list<'a>(
933        mut l1: impl Iterator<Item = &'a Self>,
934        mut l2: impl Iterator<Item = &'a Self>,
935    ) -> Option<Ordering> {
936        let mut result = Ordering::Equal;
937        loop {
938            match (l1.next(), l2.next()) {
939                (None, None) => return Some(result),
940                (_, None) | (None, _) => return None,
941                (Some(v1), Some(v2)) => {
942                    result = associate_orderings(result, v1.partial_cmp(v2)?)?;
943                }
944            }
945        }
946    }
947}
948
949#[derive(Clone, Debug)]
950pub struct FunctionSignature {
951    pub args: Vec<Type>,
952    pub return_value: Type,
953    pub invalidate_array_bounds: bool,
954}
955
956#[derive(Clone, Debug)]
957pub struct HelperDefinition {
958    pub index: u32,
959    pub name: &'static str,
960    pub signature: FunctionSignature,
961}
962
963#[derive(Debug, Default)]
964pub struct CallingContext {
965    /// List of map schemas of the associated map. The maps can be accessed using LDDW instruction
966    /// with `src_reg=BPF_PSEUDO_MAP_IDX`.
967    pub maps: Vec<MapSchema>,
968    /// The registered external functions.
969    pub helpers: HashMap<u32, &'static HelperDefinition>,
970    /// The args of the program.
971    pub args: Vec<Type>,
972    /// Packet type. Normally it should be either `None` or `args[0]`.
973    pub packet_type: Option<Type>,
974}
975
976impl CallingContext {
977    pub fn register_map(&mut self, schema: MapSchema) -> usize {
978        let index = self.maps.len();
979        self.maps.push(schema);
980        index
981    }
982}
983
984#[derive(Debug, PartialEq, Eq, Clone)]
985pub struct StructAccess {
986    pub pc: ProgramCounter,
987
988    // Memory Id of the struct being accessed.
989    pub memory_id: MemoryId,
990
991    // Offset of the field being loaded.
992    pub field_offset: usize,
993
994    // Indicates that this is a 32-bit pointer load. These instructions must be remapped.
995    pub is_32_bit_ptr_load: bool,
996}
997
998#[derive(Debug, Clone)]
999pub struct VerifiedEbpfProgram {
1000    pub(crate) code: Vec<EbpfInstruction>,
1001    pub(crate) args: Vec<Type>,
1002    pub(crate) struct_access_instructions: Vec<StructAccess>,
1003    pub(crate) maps: Vec<MapSchema>,
1004}
1005
1006impl VerifiedEbpfProgram {
1007    pub fn code(&self) -> &[EbpfInstruction] {
1008        &self.code
1009    }
1010
1011    pub fn struct_access_instructions(&self) -> &[StructAccess] {
1012        &self.struct_access_instructions
1013    }
1014
1015    pub fn from_verified_code(
1016        code: Vec<EbpfInstruction>,
1017        args: Vec<Type>,
1018        struct_access_instructions: Vec<StructAccess>,
1019        maps: Vec<MapSchema>,
1020    ) -> Self {
1021        Self { code, args, struct_access_instructions, maps }
1022    }
1023
1024    pub fn maps(&self) -> &[MapSchema] {
1025        &self.maps
1026    }
1027}
1028
1029/// Verify the given code depending on the type of the parameters and the registered external
1030/// functions. Returned `VerifiedEbpfProgram` should be linked in order to execute it.
1031pub fn verify_program(
1032    code: Vec<EbpfInstruction>,
1033    calling_context: CallingContext,
1034    logger: &mut dyn VerifierLogger,
1035) -> Result<VerifiedEbpfProgram, VerifierError> {
1036    if code.len() > BPF_MAX_INSTS {
1037        return error_and_log(
1038            logger,
1039            VerifierError { kind: VerifierErrorKind::ProgramTooLarge, pc: None },
1040        );
1041    }
1042    // Pre-scan the program to ensure all LDDW instructions are structurally well-formed,
1043    // even if they are in unreachable code. This is necessary because:
1044    // 1. The linker may rewrite LDDW instructions (e.g., to resolve map pointers) and
1045    //    expects them to be well-formed.
1046    // 2. It ensures that the second part of every LDDW has a `code` of 0 (invalid opcode).
1047    //    This guarantees that any attempt to jump directly into the second part of an
1048    //    LDDW will be rejected during verification as an invalid instruction.
1049    //
1050    // It also computes the prune points: the targets of the jumps and the instructions following
1051    // a conditional jump or a `cmpxchg` (which also branches into two states). Every loop and
1052    // every join of two branches goes through one of them, and the states are only compared with
1053    // and recorded as terminating contexts at these points.
1054    let mut terminating_contexts: Vec<Option<TerminatingContexts>> =
1055        (0..code.len()).map(|_| None).collect();
1056    let mut scan_pc = 0;
1057    while scan_pc < code.len() {
1058        let inst = &code[scan_pc];
1059        if inst.code() == BPF_LDDW {
1060            let Some(next_instruction) = code.get(scan_pc + 1) else {
1061                return error_and_log(
1062                    logger,
1063                    VerifierError { kind: VerifierErrorKind::IncompleteLddw, pc: Some(scan_pc) },
1064                );
1065            };
1066            if next_instruction.code() != 0
1067                || next_instruction.offset() != 0
1068                || next_instruction.src_reg() != 0
1069                || next_instruction.dst_reg() != 0
1070            {
1071                return error_and_log(
1072                    logger,
1073                    VerifierError { kind: VerifierErrorKind::MalformedLddw, pc: Some(scan_pc) },
1074                );
1075            }
1076            scan_pc += 2;
1077        } else {
1078            let class = inst.code() & BPF_CLS_MASK;
1079            let op = inst.code() & BPF_SUB_OP_MASK;
1080            let is_cmpxchg = (inst.code() == (BPF_STX | BPF_ATOMIC | BPF_W)
1081                || inst.code() == (BPF_STX | BPF_ATOMIC | BPF_DW))
1082                && inst.imm() as u8 == BPF_CMPXCHG;
1083            if (class == BPF_JMP || class == BPF_JMP32) && op != BPF_CALL && op != BPF_EXIT {
1084                // Invalid targets are ignored here, and rejected if they are reached.
1085                let target = scan_pc.checked_add_signed(isize::from(inst.offset()) + 1);
1086                if let Some(entry) = target.and_then(|pc| terminating_contexts.get_mut(pc)) {
1087                    *entry = Some(Default::default());
1088                }
1089                if op != BPF_JA {
1090                    if let Some(entry) = terminating_contexts.get_mut(scan_pc + 1) {
1091                        *entry = Some(Default::default());
1092                    }
1093                }
1094            } else if is_cmpxchg {
1095                if let Some(entry) = terminating_contexts.get_mut(scan_pc + 1) {
1096                    *entry = Some(Default::default());
1097                }
1098            }
1099            scan_pc += 1;
1100        }
1101    }
1102
1103    let mut context = ComputationContext::default();
1104    for (i, t) in calling_context.args.iter().enumerate() {
1105        // The parameter registers are r1 to r5.
1106        context
1107            .set_reg((i + 1) as u8, t.clone())
1108            .map_err(|kind| VerifierError { kind, pc: None })?;
1109    }
1110    let states = vec![context];
1111    let mut verification_context =
1112        VerificationContext::new(calling_context, logger, &code, states, terminating_contexts);
1113    while let Some(mut context) = verification_context.states.pop() {
1114        if let Some(Some(terminating_contexts)) =
1115            verification_context.terminating_contexts.get(context.pc)
1116        {
1117            // Check whether there exist a context that terminate and prove that this context does
1118            // also terminate.
1119            if let Some(ending_context) = terminating_contexts.find_greater_or_equal(&context) {
1120                // One such context has been found, this proves the current context terminates.
1121                // If the context has a parent, register the data dependencies and try to terminate
1122                // it.
1123                if let Some(parent) = context.parent.take() {
1124                    parent.dependencies.update(|d| d.merge(ending_context.dependencies));
1125                    if let Some(parent) = Rc::into_inner(parent) {
1126                        parent.terminate(&mut verification_context)?;
1127                    }
1128                }
1129                continue;
1130            }
1131        }
1132        if verification_context.iteration > 10 * BPF_MAX_INSTS {
1133            return error_and_log(
1134                verification_context.logger,
1135                VerifierError { kind: VerifierErrorKind::ComplexityLimitExceeded, pc: None },
1136            );
1137        }
1138        if context.pc >= code.len() {
1139            return error_and_log(
1140                verification_context.logger,
1141                VerifierError { kind: VerifierErrorKind::FellOffProgramEnd, pc: Some(context.pc) },
1142            );
1143        }
1144        let first_successor = verification_context.states.len();
1145        if let Err(kind) = (&context).visit(&mut verification_context, code[context.pc]) {
1146            return error_and_log(
1147                verification_context.logger,
1148                VerifierError { kind, pc: Some(context.pc) },
1149            );
1150        }
1151        // All the contexts pushed while visiting `&context` are its successors, and are created
1152        // without a parent. Register `context` as their shared parent, or terminate it now if it
1153        // has no successors (i.e. `exit`).
1154        if let Some((last, rest)) = verification_context.states[first_successor..].split_last_mut()
1155        {
1156            let parent = Rc::new(context);
1157            for successor in rest {
1158                successor.parent = Some(Rc::clone(&parent));
1159            }
1160            last.parent = Some(parent);
1161        } else {
1162            debug_assert_eq!(code[context.pc].code(), BPF_JMP | BPF_EXIT);
1163            context.terminate(&mut verification_context)?;
1164        }
1165        verification_context.iteration += 1;
1166    }
1167
1168    let struct_access_instructions =
1169        verification_context.struct_access_instructions.into_values().collect::<Vec<_>>();
1170    let CallingContext { maps, args, .. } = verification_context.calling_context;
1171    Ok(VerifiedEbpfProgram { code, struct_access_instructions, maps, args })
1172}
1173
1174struct VerificationContext<'a> {
1175    /// The type information for the program arguments and the registered functions.
1176    calling_context: CallingContext,
1177    /// The logger to use.
1178    logger: &'a mut dyn VerifierLogger,
1179    /// The `ComputationContext` yet to be validated.
1180    states: Vec<ComputationContext>,
1181    /// The program being analyzed.
1182    code: &'a [EbpfInstruction],
1183    /// A counter used to generated unique ids for memory buffers and maps.
1184    counter: u64,
1185    /// The current iteration of the verifier. Used to ensure termination by limiting the number of
1186    /// iteration before bailing out.
1187    iteration: usize,
1188    /// Keep track of the context that terminates at a given pc. The list of context will all be
1189    /// incomparables as each time a bigger context is computed, the smaller ones are removed from
1190    /// the list. Indexed directly by `ProgramCounter` (`0..code.len()`) for O(1) lookup, and
1191    /// `None` if the pc is not a prune point.
1192    terminating_contexts: Vec<Option<TerminatingContexts>>,
1193    /// The current set of struct access instructions that will need to be updated when the
1194    /// program is linked. This is also used to ensure that a given instruction always loads the
1195    /// same field. If this is not the case, the verifier will reject the program.
1196    struct_access_instructions: HashMap<ProgramCounter, StructAccess>,
1197}
1198
1199impl<'a> VerificationContext<'a> {
1200    fn new(
1201        calling_context: CallingContext,
1202        logger: &'a mut dyn VerifierLogger,
1203        code: &'a [EbpfInstruction],
1204        states: Vec<ComputationContext>,
1205        terminating_contexts: Vec<Option<TerminatingContexts>>,
1206    ) -> Self {
1207        debug_assert_eq!(code.len(), terminating_contexts.len());
1208        Self {
1209            calling_context,
1210            logger,
1211            states,
1212            code,
1213            counter: 0,
1214            iteration: 0,
1215            terminating_contexts,
1216            struct_access_instructions: Default::default(),
1217        }
1218    }
1219
1220    fn next_id(&mut self) -> MemoryId {
1221        let id = self.counter;
1222        self.counter += 1;
1223        MemoryId { namespace: Namespace::Verification, id, parent: None }
1224    }
1225
1226    /// Register an instruction that loads or stores a struct field. These instructions will need
1227    /// to updated later when the program is linked.
1228    fn register_struct_access(
1229        &mut self,
1230        struct_access: StructAccess,
1231    ) -> Result<(), VerifierErrorKind> {
1232        match self.struct_access_instructions.entry(struct_access.pc) {
1233            std::collections::hash_map::Entry::Vacant(entry) => {
1234                entry.insert(struct_access);
1235            }
1236            std::collections::hash_map::Entry::Occupied(entry) => {
1237                if *entry.get() != struct_access {
1238                    return Err(VerifierErrorKind::InconsistentStructFieldAccess);
1239                }
1240            }
1241        }
1242        Ok(())
1243    }
1244}
1245
1246const STACK_ELEMENT_SIZE: usize = std::mem::size_of::<u64>();
1247
1248/// An offset inside the stack. The offset is from the end of the stack.
1249/// downward.
1250#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1251pub struct StackOffset(ScalarValueData);
1252
1253impl Default for StackOffset {
1254    fn default() -> Self {
1255        Self(BPF_STACK_SIZE.into())
1256    }
1257}
1258
1259impl StackOffset {
1260    /// Returns an error if the current offset is not valid for element access, distinguishing
1261    /// variable offsets from out of bounds ones.
1262    fn check_valid_offset(&self) -> Result<(), VerifierErrorKind> {
1263        if !self.0.is_known() {
1264            return Err(VerifierErrorKind::VariableStackOffset);
1265        }
1266        if self.0.value >= (BPF_STACK_SIZE as u64) {
1267            return Err(VerifierErrorKind::StackAccessOutOfBounds);
1268        }
1269        Ok(())
1270    }
1271
1272    /// Whether the current offset is within the stack bounds, inclusive of `BPF_STACK_SIZE`.
1273    fn is_within_stack(&self) -> bool {
1274        self.0.is_known() && self.0.value <= (BPF_STACK_SIZE as u64)
1275    }
1276
1277    /// The value of the register.
1278    fn reg(&self) -> ScalarValueData {
1279        self.0
1280    }
1281
1282    /// The index into the stack array this offset points to. Can be called only if
1283    /// `is_within_stack()` is true.
1284    fn array_index(&self) -> usize {
1285        debug_assert!(self.is_within_stack());
1286        usize::try_from(self.0.value).unwrap() / STACK_ELEMENT_SIZE
1287    }
1288
1289    /// The offset inside the aligned u64 in the stack.
1290    fn sub_index(&self) -> usize {
1291        debug_assert!(self.is_within_stack());
1292        usize::try_from(self.0.value).unwrap() % STACK_ELEMENT_SIZE
1293    }
1294
1295    fn add<T: Into<ScalarValueData>>(self, rhs: T) -> Self {
1296        Self(self.0 + rhs)
1297    }
1298}
1299
1300// The initialized elements of the stack are tracked in a `u64` bitmask.
1301const _: () = assert!(BPF_STACK_SIZE / STACK_ELEMENT_SIZE <= u64::BITS as usize);
1302
1303/// Returns an iterator over the indices of the bits set in `mask`, in increasing order.
1304fn bit_indices(mut mask: u64) -> impl Iterator<Item = usize> {
1305    std::iter::from_fn(move || {
1306        if mask == 0 {
1307            return None;
1308        }
1309        let index = mask.trailing_zeros() as usize;
1310        mask &= mask - 1;
1311        Some(index)
1312    })
1313}
1314
1315/// The state of the stack
1316///
1317/// Only the initialized elements are stored, densely and in increasing index order. The storage is
1318/// shared between the clones made for each verified instruction and copied on write. This makes
1319/// cloning cheap and comparing two stacks, the hot path of state pruning, a linear scan.
1320#[derive(Clone, Debug, Default, PartialEq, Eq)]
1321struct Stack {
1322    /// Bit `i` is set iff element `i` is initialized.
1323    mask: u64,
1324    /// The initialized elements, in increasing index order.
1325    data: Arc<Vec<Type>>,
1326}
1327
1328impl Stack {
1329    fn bit(index: usize) -> u64 {
1330        assert!(index < u64::BITS as usize);
1331        1 << index
1332    }
1333
1334    /// Returns the position of the element at `index` in `data`, or the position where it must be
1335    /// inserted if it is not initialized.
1336    fn position(&self, index: usize) -> usize {
1337        (self.mask & (Self::bit(index) - 1)).count_ones() as usize
1338    }
1339
1340    /// Replace all instances of the NullOr type with the given `null_id` to either 0 or the
1341    /// subtype depending on `is_null`
1342    fn set_null(&mut self, null_id: &MemoryId, is_null: bool) {
1343        if !self.data.iter().any(|t| matches!(t, Type::NullOr { id, .. } if id == null_id)) {
1344            return;
1345        }
1346        for t in Arc::make_mut(&mut self.data).iter_mut() {
1347            t.set_null(null_id, is_null);
1348        }
1349    }
1350
1351    fn get(&self, index: usize) -> &Type {
1352        if self.mask & Self::bit(index) == 0 {
1353            &Type::UNINITIALIZED
1354        } else {
1355            &self.data[self.position(index)]
1356        }
1357    }
1358
1359    fn set(&mut self, index: usize, t: Type) {
1360        let bit = Self::bit(index);
1361        let position = self.position(index);
1362        let is_set = self.mask & bit != 0;
1363        if t == Type::UNINITIALIZED {
1364            if is_set {
1365                Arc::make_mut(&mut self.data).remove(position);
1366                self.mask &= !bit;
1367            }
1368        } else if is_set {
1369            if self.data[position] != t {
1370                Arc::make_mut(&mut self.data)[position] = t;
1371            }
1372        } else {
1373            Arc::make_mut(&mut self.data).insert(position, t);
1374            self.mask |= bit;
1375        }
1376    }
1377
1378    /// Uninitializes all the elements whose bit is not set in `mask`.
1379    fn retain(&mut self, mask: u64) {
1380        if self.mask & !mask == 0 {
1381            return;
1382        }
1383        if mask == 0 {
1384            match Arc::get_mut(&mut self.data) {
1385                Some(data) => data.clear(),
1386                None => self.data = Default::default(),
1387            }
1388            self.mask = 0;
1389            return;
1390        }
1391
1392        let mut indices = bit_indices(self.mask);
1393        Arc::make_mut(&mut self.data).retain(|_| mask & Self::bit(indices.next().unwrap()) != 0);
1394        self.mask &= mask;
1395    }
1396
1397    fn extract_sub_value(value: u64, offset: usize, byte_count: usize) -> u64 {
1398        NativeEndian::read_uint(&value.as_bytes()[offset..], byte_count)
1399    }
1400
1401    fn insert_sub_value(mut original: u64, value: u64, width: DataWidth, offset: usize) -> u64 {
1402        let byte_count = width.bytes();
1403        let original_buf = original.as_mut_bytes();
1404        let value_buf = value.as_bytes();
1405        original_buf[offset..(byte_count + offset)].copy_from_slice(&value_buf[..byte_count]);
1406        original
1407    }
1408
1409    fn write_data_ptr(
1410        &mut self,
1411        mut offset: StackOffset,
1412        bytes: u64,
1413    ) -> Result<(), VerifierErrorKind> {
1414        for i in 0..bytes {
1415            self.store(offset, Type::UNKNOWN_SCALAR, DataWidth::U8)?;
1416            offset = offset.add(1);
1417        }
1418        Ok(())
1419    }
1420
1421    fn read_data_ptr(&self, offset: StackOffset, bytes: u64) -> Result<(), VerifierErrorKind> {
1422        let read_element = |index: usize,
1423                            start_offset: usize,
1424                            end_offset: usize|
1425         -> Result<(), VerifierErrorKind> {
1426            match self.get(index) {
1427                Type::ScalarValue(data) => {
1428                    debug_assert!(end_offset > start_offset);
1429                    let unwritten_bits = Self::extract_sub_value(
1430                        data.unwritten_mask,
1431                        start_offset,
1432                        end_offset - start_offset,
1433                    );
1434                    if unwritten_bits == 0 {
1435                        Ok(())
1436                    } else {
1437                        Err(VerifierErrorKind::UninitializedStackRead)
1438                    }
1439                }
1440                _ => Err(VerifierErrorKind::NonScalarStackRead),
1441            }
1442        };
1443        if bytes == 0 {
1444            return Ok(());
1445        }
1446
1447        if bytes as usize > BPF_STACK_SIZE {
1448            return Err(VerifierErrorKind::StackAccessOutOfBounds);
1449        }
1450
1451        offset.check_valid_offset()?;
1452
1453        let end_offset = offset.add(bytes);
1454        if !end_offset.is_within_stack() {
1455            return Err(VerifierErrorKind::StackAccessOutOfBounds);
1456        }
1457
1458        // Handle the case where all the data is contained in a single element excluding the last
1459        // byte (the case when the read ends at an element edge, i.e. `end_offset.sub_index()==0`,
1460        // is covered by the default path below).
1461        if offset.array_index() == end_offset.array_index() {
1462            return read_element(offset.array_index(), offset.sub_index(), end_offset.sub_index());
1463        }
1464
1465        // Handle the first element, that might be partial.
1466        read_element(offset.array_index(), offset.sub_index(), STACK_ELEMENT_SIZE)?;
1467
1468        // Handle the last element, that might be partial.
1469        if end_offset.sub_index() != 0 {
1470            read_element(end_offset.array_index(), 0, end_offset.sub_index())?;
1471        }
1472
1473        // Handle the any full type between beginning and end.
1474        for i in (offset.array_index() + 1)..end_offset.array_index() {
1475            read_element(i, 0, STACK_ELEMENT_SIZE)?;
1476        }
1477
1478        Ok(())
1479    }
1480
1481    fn store(
1482        &mut self,
1483        offset: StackOffset,
1484        value: Type,
1485        width: DataWidth,
1486    ) -> Result<(), VerifierErrorKind> {
1487        offset.check_valid_offset()?;
1488        if offset.sub_index() % width.bytes() != 0 {
1489            return Err(VerifierErrorKind::MisalignedStackAccess);
1490        }
1491
1492        let index = offset.array_index();
1493        if width == DataWidth::U64 {
1494            self.set(index, value);
1495        } else {
1496            match value {
1497                Type::ScalarValue(data) => {
1498                    let old_data = match self.get(index) {
1499                        Type::ScalarValue(data) => *data,
1500                        _ => {
1501                            // The value in the stack is not a scalar. Let consider it an scalar
1502                            // value with no written bits.
1503                            ScalarValueData::UNINITIALIZED
1504                        }
1505                    };
1506                    let sub_index = offset.sub_index();
1507                    let value =
1508                        Self::insert_sub_value(old_data.value, data.value, width, sub_index);
1509                    let unknown_mask = Self::insert_sub_value(
1510                        old_data.unknown_mask,
1511                        data.unknown_mask,
1512                        width,
1513                        sub_index,
1514                    );
1515                    let unwritten_mask = Self::insert_sub_value(
1516                        old_data.unwritten_mask,
1517                        data.unwritten_mask,
1518                        width,
1519                        sub_index,
1520                    );
1521                    let urange = U64Range::compute_range_for_bytes_swap(
1522                        old_data.urange,
1523                        data.urange,
1524                        sub_index,
1525                        0,
1526                        width.bytes(),
1527                    );
1528                    self.set(
1529                        index,
1530                        Type::ScalarValue(ScalarValueData::new(
1531                            value,
1532                            unknown_mask,
1533                            unwritten_mask,
1534                            urange,
1535                        )),
1536                    );
1537                }
1538                _ => {
1539                    return Err(VerifierErrorKind::PartialNonScalarStackStore);
1540                }
1541            }
1542        }
1543        Ok(())
1544    }
1545
1546    fn load(&self, offset: StackOffset, width: DataWidth) -> Result<Type, VerifierErrorKind> {
1547        offset.check_valid_offset()?;
1548        if offset.sub_index() % width.bytes() != 0 {
1549            return Err(VerifierErrorKind::MisalignedStackAccess);
1550        }
1551
1552        let index = offset.array_index();
1553        let loaded_type = self.get(index).clone();
1554        let result = if width == DataWidth::U64 {
1555            loaded_type
1556        } else {
1557            match loaded_type {
1558                Type::ScalarValue(data) => {
1559                    let sub_index = offset.sub_index();
1560                    let value = Self::extract_sub_value(data.value, sub_index, width.bytes());
1561                    let unknown_mask =
1562                        Self::extract_sub_value(data.unknown_mask, sub_index, width.bytes());
1563                    let unwritten_mask =
1564                        Self::extract_sub_value(data.unwritten_mask, sub_index, width.bytes());
1565                    let urange = U64Range::compute_range_for_bytes_swap(
1566                        0.into(),
1567                        data.urange,
1568                        0,
1569                        sub_index,
1570                        width.bytes(),
1571                    );
1572                    Type::ScalarValue(ScalarValueData::new(
1573                        value,
1574                        unknown_mask,
1575                        unwritten_mask,
1576                        urange,
1577                    ))
1578                }
1579                _ => {
1580                    return Err(VerifierErrorKind::PartialNonScalarStackLoad);
1581                }
1582            }
1583        };
1584        if !result.is_initialized() {
1585            return Err(VerifierErrorKind::UninitializedStackRead);
1586        }
1587        Ok(result)
1588    }
1589}
1590
1591/// Two types are ordered with `t1` > `t2` if a proof that a program in a state `t1` finish is also
1592/// a proof that a program in a state `t2` finish.
1593impl PartialOrd for Stack {
1594    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1595        if self.mask == other.mask && Arc::ptr_eq(&self.data, &other.data) {
1596            return Some(Ordering::Equal);
1597        }
1598        let mut result = match (self.mask & !other.mask != 0, !self.mask & other.mask != 0) {
1599            (true, true) => return None,
1600            (true, false) => Ordering::Less,
1601            (false, true) => Ordering::Greater,
1602            (false, false) => Ordering::Equal,
1603        };
1604        for index in bit_indices(self.mask & other.mask) {
1605            result = associate_orderings(result, self.get(index).partial_cmp(other.get(index))?)?;
1606        }
1607        Some(result)
1608    }
1609}
1610
1611macro_rules! bpf_log {
1612    ($context:ident, $verification_context:ident, $($msg:tt)*) => {
1613        if $verification_context.logger.is_enabled() {
1614            let line = format!(
1615                "{}: ({:02x}) {}",
1616                $context.pc,
1617                $verification_context.code[$context.pc].code(),
1618                format_args!($($msg)*)
1619            );
1620            $verification_context.logger.log(line.as_bytes());
1621        }
1622    };
1623}
1624
1625/// The state of the computation as known by the verifier at a given point in time.
1626#[derive(Debug, Default)]
1627struct ComputationContext {
1628    /// The program counter.
1629    pc: ProgramCounter,
1630    /// Register 0 to 9.
1631    registers: [Type; GENERAL_REGISTER_COUNT as usize],
1632    /// The state of the stack.
1633    stack: Stack,
1634    /// The dynamically known bounds of buffers indexed by their ids.
1635    array_bounds: BTreeMap<MemoryId, u64>,
1636    /// The currently allocated resources.
1637    resources: BTreeSet<MemoryId>,
1638    /// The previous context in the computation.
1639    parent: Option<Rc<ComputationContext>>,
1640    /// The data dependencies of this context. This is used to broaden a known ending context to
1641    /// help cutting computation branches. The verification is single-threaded, and the successors
1642    /// holding a reference to their parent add their dependencies to it.
1643    dependencies: Cell<DataDependencies>,
1644}
1645
1646#[cfg(test)]
1647impl Clone for ComputationContext {
1648    fn clone(&self) -> Self {
1649        Self {
1650            pc: self.pc,
1651            registers: self.registers.clone(),
1652            stack: self.stack.clone(),
1653            array_bounds: self.array_bounds.clone(),
1654            resources: self.resources.clone(),
1655            parent: None,
1656            // dependencies are erased as they must always be used on the same instance of the
1657            // context.
1658            dependencies: Default::default(),
1659        }
1660    }
1661}
1662
1663/// parent and dependencies are ignored for the comparison, as they do not matter for termination.
1664impl PartialEq for ComputationContext {
1665    fn eq(&self, other: &Self) -> bool {
1666        self.pc == other.pc
1667            && self.registers == other.registers
1668            && self.stack == other.stack
1669            && self.array_bounds == other.array_bounds
1670    }
1671}
1672
1673impl ComputationContext {
1674    /// Replace all instances of the NullOr type with the given `null_id` to either 0 or the
1675    /// subtype depending on `is_null`
1676    fn set_null(&mut self, null_id: &MemoryId, is_null: bool) {
1677        for i in 0..self.registers.len() {
1678            self.registers[i].set_null(null_id, is_null);
1679        }
1680        self.stack.set_null(null_id, is_null);
1681    }
1682
1683    fn reg(&self, index: Register) -> Result<Type, VerifierErrorKind> {
1684        if index >= REGISTER_COUNT {
1685            return Err(VerifierErrorKind::InvalidRegister(index));
1686        }
1687        if index < GENERAL_REGISTER_COUNT {
1688            Ok(self.registers[index as usize].clone())
1689        } else {
1690            Ok(Type::PtrToStack { offset: StackOffset::default() })
1691        }
1692    }
1693
1694    fn set_reg(&mut self, index: Register, reg_type: Type) -> Result<(), VerifierErrorKind> {
1695        if index >= REGISTER_COUNT {
1696            return Err(VerifierErrorKind::InvalidRegister(index));
1697        }
1698        if index >= GENERAL_REGISTER_COUNT {
1699            return Err(VerifierErrorKind::WriteToReadOnlyRegister(index));
1700        }
1701        self.registers[index as usize] = reg_type;
1702        Ok(())
1703    }
1704
1705    fn update_array_bounds(&mut self, id: MemoryId, new_bound: ScalarValueData) {
1706        let new_bound_min = new_bound.min();
1707        self.array_bounds
1708            .entry(id)
1709            .and_modify(|v| *v = std::cmp::max(*v, new_bound_min))
1710            .or_insert(new_bound_min);
1711    }
1712
1713    fn get_map_schema(&self, argument: u8) -> Result<MapSchema, VerifierErrorKind> {
1714        match self.reg(argument + 1)? {
1715            Type::ConstPtrToMap { schema, .. } => Ok(schema),
1716            _ => Err(VerifierErrorKind::HelperArgumentNotMap(argument)),
1717        }
1718    }
1719
1720    fn next(&self) -> Result<Self, VerifierErrorKind> {
1721        self.jump_with_offset(0)
1722    }
1723
1724    /// Returns a new `ComputationContext` where the pc has jump by `offset + 1`. In particular,
1725    /// the next instruction is reached with `jump_with_offset(0)`.
1726    ///
1727    /// This does not check that the target is inside the program: falling off the end of the
1728    /// program is reported by the main verification loop. Use `checked_jump_with_offset` for
1729    /// explicit jumps.
1730    ///
1731    /// The returned context has no parent. The parent is set by `verify_program` once the
1732    /// current instruction has been fully visited, by moving `self` into an `Rc` shared by all
1733    /// its successors, instead of cloning it.
1734    fn jump_with_offset(&self, offset: i16) -> Result<Self, VerifierErrorKind> {
1735        let pc = self
1736            .pc
1737            .checked_add_signed(offset.into())
1738            .and_then(|v| v.checked_add_signed(1))
1739            .ok_or(VerifierErrorKind::JumpOutOfBounds)?;
1740        let result = Self {
1741            pc,
1742            registers: self.registers.clone(),
1743            stack: self.stack.clone(),
1744            array_bounds: self.array_bounds.clone(),
1745            resources: self.resources.clone(),
1746            parent: None,
1747            dependencies: Default::default(),
1748        };
1749        Ok(result)
1750    }
1751
1752    /// Same as `jump_with_offset`, but also fails if the target of the jump is outside of the
1753    /// program.
1754    fn checked_jump_with_offset(
1755        &self,
1756        verification_context: &VerificationContext<'_>,
1757        offset: i16,
1758    ) -> Result<Self, VerifierErrorKind> {
1759        let result = self.jump_with_offset(offset)?;
1760        if result.pc >= verification_context.code.len() {
1761            return Err(VerifierErrorKind::JumpOutOfBounds);
1762        }
1763        Ok(result)
1764    }
1765
1766    fn check_memory_access(
1767        &self,
1768        dst_offset: ScalarValueData,
1769        dst_buffer_size: u64,
1770        instruction_offset: i16,
1771        width: usize,
1772    ) -> Result<(), VerifierErrorKind> {
1773        let memory_range = dst_offset.urange + instruction_offset + U64Range::new(0, width as u64);
1774        if memory_range.max > dst_buffer_size {
1775            return Err(VerifierErrorKind::OutOfBoundsMemoryAccess);
1776        }
1777        Ok(())
1778    }
1779
1780    /// Checks that the atomic access to `addr + field.offset` is allowed: it
1781    /// must target either the stack or a map value, and it must be naturally
1782    /// aligned for `field.width`.
1783    ///
1784    /// The stack and map values are at least 8-byte aligned, so it is
1785    /// sufficient to check the offset of the access relative to the start of
1786    /// the buffer. The offset is tracked as a `ScalarValueData`, so the check
1787    /// requires the low bits of the offset to be known and zero.
1788    fn check_atomic_access(&self, addr: &Type, field: Field) -> Result<(), VerifierErrorKind> {
1789        let addr = addr.inner(self)?;
1790        let base_offset = match *addr {
1791            Type::PtrToStack { offset } => offset.reg(),
1792            Type::PtrToMemory { offset, origin, .. } if origin.is_map_value() => offset,
1793            _ => return Err(VerifierErrorKind::AtomicAccessNotAllowed),
1794        };
1795        let alignment_mask = (field.width.bytes() as u64) - 1;
1796        // Adding a constant preserves the low bits of the result only if the
1797        // low bits of the other operand are known.
1798        if base_offset.unknown_mask & alignment_mask != 0
1799            || base_offset.value.wrapping_add_signed(field.offset.into()) & alignment_mask != 0
1800        {
1801            return Err(VerifierErrorKind::MisalignedAtomicAccess);
1802        }
1803        Ok(())
1804    }
1805
1806    fn store_memory(
1807        &mut self,
1808        context: &mut VerificationContext<'_>,
1809        pc: ProgramCounter,
1810        addr: &Type,
1811        field: Field,
1812        value: Type,
1813    ) -> Result<(), VerifierErrorKind> {
1814        let addr = addr.inner(self)?;
1815        match *addr {
1816            Type::PtrToStack { offset } => {
1817                let offset_sum = offset.add(field.offset);
1818                return self.stack.store(offset_sum, value, field.width);
1819            }
1820            Type::PtrToMemory { offset, buffer_size, origin, .. } if origin.is_writable() => {
1821                self.check_memory_access(offset, buffer_size, field.offset, field.width.bytes())?;
1822            }
1823            Type::PtrToMemory { .. } => {
1824                return Err(VerifierErrorKind::WriteToReadOnlyMemory);
1825            }
1826            Type::PtrToStruct { ref id, offset, ref descriptor, .. } => {
1827                let field_desc = descriptor
1828                    .find_field(offset, field)
1829                    .ok_or(VerifierErrorKind::InvalidStructFieldAccess)?;
1830
1831                if !matches!(field_desc.field_type, FieldType::MutableScalar { .. }) {
1832                    return Err(VerifierErrorKind::WriteToReadOnlyField);
1833                }
1834
1835                context.register_struct_access(StructAccess {
1836                    pc,
1837                    memory_id: id.clone(),
1838                    field_offset: field_desc.offset,
1839                    is_32_bit_ptr_load: false,
1840                })?;
1841            }
1842            Type::PtrToArray { ref id, offset } => {
1843                self.check_memory_access(
1844                    offset,
1845                    *self.array_bounds.get(&id).unwrap_or(&0),
1846                    field.offset,
1847                    field.width.bytes(),
1848                )?;
1849            }
1850            _ => return Err(VerifierErrorKind::StoreToNonPointer),
1851        }
1852
1853        match value {
1854            Type::ScalarValue(data) if data.is_fully_initialized() => {}
1855            // Private data should not be leaked.
1856            _ => return Err(VerifierErrorKind::InvalidStoredValue),
1857        }
1858        Ok(())
1859    }
1860
1861    fn load_memory(
1862        &self,
1863        context: &mut VerificationContext<'_>,
1864        addr: &Type,
1865        field: Field,
1866    ) -> Result<Type, VerifierErrorKind> {
1867        let addr = addr.inner(self)?;
1868        match *addr {
1869            Type::PtrToStack { offset } => {
1870                let offset_sum = offset.add(field.offset);
1871                self.stack.load(offset_sum, field.width)
1872            }
1873            Type::PtrToMemory { ref id, offset, buffer_size, .. } => {
1874                self.check_memory_access(offset, buffer_size, field.offset, field.width.bytes())?;
1875                Ok(Type::UNKNOWN_SCALAR)
1876            }
1877            Type::PtrToStruct { ref id, offset, ref descriptor, .. } => {
1878                let field_desc = descriptor
1879                    .find_field(offset, field)
1880                    .ok_or(VerifierErrorKind::InvalidStructFieldAccess)?;
1881
1882                let (return_type, is_32_bit_ptr_load) = match &field_desc.field_type {
1883                    FieldType::Scalar { .. } | FieldType::MutableScalar { .. } => {
1884                        (Type::UNKNOWN_SCALAR, false)
1885                    }
1886                    FieldType::PtrToArray { id: array_id, is_32_bit } => (
1887                        Type::PtrToArray { id: array_id.prepended(id.clone()), offset: 0.into() },
1888                        *is_32_bit,
1889                    ),
1890                    FieldType::PtrToEndArray { id: array_id, is_32_bit } => {
1891                        (Type::PtrToEndArray { id: array_id.prepended(id.clone()) }, *is_32_bit)
1892                    }
1893                    FieldType::PtrToMemory { id: memory_id, buffer_size, is_32_bit } => (
1894                        Type::PtrToMemory {
1895                            id: memory_id.prepended(id.clone()),
1896                            offset: 0.into(),
1897                            buffer_size: *buffer_size as u64,
1898                            origin: PointerOrigin::KernelReadOnly,
1899                        },
1900                        *is_32_bit,
1901                    ),
1902                    FieldType::NullablePtrToMemory { id: memory_id, buffer_size, is_32_bit } => {
1903                        let id = memory_id.prepended(id.clone());
1904                        (
1905                            Type::NullOr {
1906                                id: id.clone(),
1907                                inner: Arc::new(Type::PtrToMemory {
1908                                    id,
1909                                    offset: 0.into(),
1910                                    buffer_size: *buffer_size as u64,
1911                                    origin: PointerOrigin::KernelReadOnly,
1912                                }),
1913                            },
1914                            *is_32_bit,
1915                        )
1916                    }
1917                };
1918
1919                context.register_struct_access(StructAccess {
1920                    pc: self.pc,
1921                    memory_id: id.clone(),
1922                    field_offset: field_desc.offset,
1923                    is_32_bit_ptr_load,
1924                })?;
1925
1926                Ok(return_type)
1927            }
1928            Type::PtrToArray { ref id, offset } => {
1929                self.check_memory_access(
1930                    offset,
1931                    *self.array_bounds.get(&id).unwrap_or(&0),
1932                    field.offset,
1933                    field.width.bytes(),
1934                )?;
1935                Ok(Type::UNKNOWN_SCALAR)
1936            }
1937            _ => Err(VerifierErrorKind::LoadFromNonPointer),
1938        }
1939    }
1940
1941    /**
1942     * Given the given `return_value` in a method signature, return the concrete type to use,
1943     * updating the `next` context is needed.
1944     *
1945     * `maybe_null` is true is the computed value will be a descendant of a `NullOr` type.
1946     */
1947    fn resolve_return_value(
1948        &self,
1949        verification_context: &mut VerificationContext<'_>,
1950        return_value: &Type,
1951        next: &mut ComputationContext,
1952        maybe_null: bool,
1953    ) -> Result<Type, VerifierErrorKind> {
1954        match return_value {
1955            Type::AliasParameter { parameter_index } => self.reg(parameter_index + 1),
1956            Type::ReleasableParameter { id, inner } => {
1957                let id = verification_context.next_id().prepended(id.clone());
1958                if !maybe_null {
1959                    next.resources.insert(id.clone());
1960                }
1961                Ok(Type::Releasable {
1962                    id,
1963                    inner: Arc::new(self.resolve_return_value(
1964                        verification_context,
1965                        inner,
1966                        next,
1967                        maybe_null,
1968                    )?),
1969                })
1970            }
1971            Type::NullOrParameter(t) => {
1972                let id = verification_context.next_id();
1973                Ok(Type::NullOr {
1974                    id,
1975                    inner: Arc::new(self.resolve_return_value(
1976                        verification_context,
1977                        t,
1978                        next,
1979                        true,
1980                    )?),
1981                })
1982            }
1983            Type::MapValueParameter { map_ptr_index } => {
1984                let schema = self.get_map_schema(*map_ptr_index)?;
1985                let id = verification_context.next_id();
1986                let origin = if schema.flags.contains(MapFlags::ProgReadOnly) {
1987                    PointerOrigin::MapReadOnly
1988                } else {
1989                    PointerOrigin::MapWritable
1990                };
1991                Ok(Type::PtrToMemory {
1992                    id,
1993                    offset: 0.into(),
1994                    buffer_size: schema.value_size as u64,
1995                    origin,
1996                })
1997            }
1998            Type::MemoryParameter { size, .. } => {
1999                let buffer_size = size.size(self)?;
2000                let id = verification_context.next_id();
2001                Ok(Type::PtrToMemory {
2002                    id,
2003                    offset: 0.into(),
2004                    buffer_size,
2005                    origin: PointerOrigin::MapWritable,
2006                })
2007            }
2008            Type::PtrToMemory { id, offset, buffer_size, origin } => {
2009                let id = id.prepended(verification_context.next_id());
2010                Ok(Type::PtrToMemory {
2011                    id,
2012                    offset: *offset,
2013                    buffer_size: *buffer_size,
2014                    origin: *origin,
2015                })
2016            }
2017            Type::PtrToStruct { id, offset, descriptor } => {
2018                let id = id.prepended(verification_context.next_id());
2019                Ok(Type::PtrToStruct { id, offset: *offset, descriptor: descriptor.clone() })
2020            }
2021            t => Ok(t.clone()),
2022        }
2023    }
2024
2025    fn compute_source(&self, src: Source) -> Result<Type, VerifierErrorKind> {
2026        match src {
2027            Source::Reg(reg) => self.reg(reg),
2028            Source::Value(v) => Ok(v.into()),
2029        }
2030    }
2031
2032    fn apply_computation(
2033        context: &ComputationContext,
2034        op1: Type,
2035        op2: Type,
2036        alu_type: AluType,
2037        op: impl Fn(ScalarValueData, ScalarValueData) -> ScalarValueData,
2038    ) -> Result<Type, VerifierErrorKind> {
2039        let result: Type = match (alu_type, op1.inner(context)?, op2.inner(context)?) {
2040            (_, Type::ScalarValue(data1), Type::ScalarValue(data2)) => op(*data1, *data2).into(),
2041            (
2042                AluType::Add,
2043                Type::ScalarValue(_),
2044                Type::PtrToStack { .. }
2045                | Type::PtrToMemory { .. }
2046                | Type::PtrToStruct { .. }
2047                | Type::PtrToArray { .. },
2048            ) => {
2049                return Self::apply_computation(context, op2, op1, alu_type, op);
2050            }
2051            // Pointer arithmetic with a scalar that is not fully initialized (either never
2052            // written, or derived from a pointer value) is prohibited: it would allow the
2053            // pointer value to leak through the resulting offset.
2054            (
2055                alu_type,
2056                Type::PtrToStack { .. }
2057                | Type::PtrToMemory { .. }
2058                | Type::PtrToStruct { .. }
2059                | Type::PtrToArray { .. },
2060                Type::ScalarValue(data),
2061            ) if alu_type.is_ptr_compatible() && !data.is_fully_initialized() => {
2062                return Err(VerifierErrorKind::UninitializedPointerArithmetic);
2063            }
2064            (alu_type, Type::PtrToStack { offset: x }, Type::ScalarValue(data))
2065                if alu_type.is_ptr_compatible() =>
2066            {
2067                Type::PtrToStack { offset: run_on_stack_offset(*x, |x| op(x, *data)) }
2068            }
2069            (
2070                alu_type,
2071                Type::PtrToMemory { offset: x, .. } | Type::PtrToStruct { offset: x, .. },
2072                Type::ScalarValue(data),
2073            ) if alu_type.is_ptr_compatible() => op1.with_offset(op(*x, *data)),
2074            (AluType::Add, Type::PtrToArray { offset: x, .. }, Type::ScalarValue(data)) => {
2075                let offset =
2076                    x.checked_add(*data).ok_or(VerifierErrorKind::PointerOffsetOverflow)?;
2077                op1.with_offset(offset)
2078            }
2079            (AluType::Sub, Type::PtrToArray { offset: x, .. }, Type::ScalarValue(data)) => {
2080                let offset =
2081                    x.checked_sub(*data).ok_or(VerifierErrorKind::PointerOffsetOverflow)?;
2082                op1.with_offset(offset)
2083            }
2084            (
2085                AluType::Sub,
2086                Type::PtrToMemory { id: id1, offset: x1, .. },
2087                Type::PtrToMemory { id: id2, offset: x2, .. },
2088            )
2089            | (
2090                AluType::Sub,
2091                Type::PtrToStruct { id: id1, offset: x1, .. },
2092                Type::PtrToStruct { id: id2, offset: x2, .. },
2093            )
2094            | (
2095                AluType::Sub,
2096                Type::PtrToArray { id: id1, offset: x1 },
2097                Type::PtrToArray { id: id2, offset: x2 },
2098            ) if id1 == id2 => Type::from(op(*x1, *x2)),
2099            (AluType::Sub, Type::PtrToStack { offset: x1 }, Type::PtrToStack { offset: x2 }) => {
2100                Type::from(op(x1.reg(), x2.reg()))
2101            }
2102            (
2103                AluType::Sub,
2104                Type::PtrToArray { id: id1, .. },
2105                Type::PtrToEndArray { id: id2, .. },
2106            )
2107            | (
2108                AluType::Sub,
2109                Type::PtrToEndArray { id: id1, .. },
2110                Type::PtrToArray { id: id2, .. },
2111            ) if id1 == id2 => Type::UNKNOWN_SCALAR,
2112            _ => Type::default(),
2113        };
2114        Ok(result)
2115    }
2116
2117    fn alu(
2118        &self,
2119        op_name: Option<&str>,
2120        verification_context: &mut VerificationContext<'_>,
2121        dst: Register,
2122        src: Source,
2123        alu_type: AluType,
2124        op: impl Fn(ScalarValueData, ScalarValueData) -> ScalarValueData,
2125    ) -> Result<(), VerifierErrorKind> {
2126        if let Some(op_name) = op_name {
2127            bpf_log!(
2128                self,
2129                verification_context,
2130                "{op_name} {}, {}",
2131                display_register(dst),
2132                display_source(src)
2133            );
2134        }
2135        let op1 = self.reg(dst)?;
2136        let op2 = self.compute_source(src)?;
2137        let result = Self::apply_computation(self, op1, op2, alu_type, op)?;
2138        let mut next = self.next()?;
2139        next.set_reg(dst, result)?;
2140        verification_context.states.push(next);
2141        Ok(())
2142    }
2143
2144    fn log_atomic_operation(
2145        &self,
2146        op_name: &str,
2147        verification_context: &mut VerificationContext<'_>,
2148        fetch: bool,
2149        dst: Register,
2150        offset: i16,
2151        src: Register,
2152    ) {
2153        bpf_log!(
2154            self,
2155            verification_context,
2156            "lock {}{} [{}{}], {}",
2157            if fetch { "fetch " } else { "" },
2158            op_name,
2159            display_register(dst),
2160            print_offset(offset),
2161            display_register(src),
2162        );
2163    }
2164
2165    fn raw_atomic_operation(
2166        &self,
2167        op_name: &str,
2168        verification_context: &mut VerificationContext<'_>,
2169        width: DataWidth,
2170        fetch: bool,
2171        dst: Register,
2172        offset: i16,
2173        src: Register,
2174        op: impl FnOnce(&ComputationContext, Type, Type) -> Result<Type, VerifierErrorKind>,
2175    ) -> Result<(), VerifierErrorKind> {
2176        self.log_atomic_operation(op_name, verification_context, fetch, dst, offset, src);
2177        let addr = self.reg(dst)?;
2178        let value = self.reg(src)?;
2179        let field = Field::new(offset, width);
2180        self.check_atomic_access(&addr, field)?;
2181        let loaded_type = self.load_memory(verification_context, &addr, field)?;
2182        let result = op(self, loaded_type.clone(), value)?;
2183        let mut next = self.next()?;
2184        next.store_memory(verification_context, self.pc, &addr, field, result)?;
2185        if fetch {
2186            next.set_reg(src, loaded_type)?;
2187        }
2188        verification_context.states.push(next);
2189        Ok(())
2190    }
2191
2192    fn atomic_operation(
2193        &self,
2194        op_name: &str,
2195        verification_context: &mut VerificationContext<'_>,
2196        width: DataWidth,
2197        fetch: bool,
2198        dst: Register,
2199        offset: i16,
2200        src: Register,
2201        alu_type: AluType,
2202        op: impl Fn(ScalarValueData, ScalarValueData) -> ScalarValueData,
2203    ) -> Result<(), VerifierErrorKind> {
2204        self.raw_atomic_operation(
2205            op_name,
2206            verification_context,
2207            width,
2208            fetch,
2209            dst,
2210            offset,
2211            src,
2212            |context: &ComputationContext, v1: Type, v2: Type| {
2213                Self::apply_computation(context, v1, v2, alu_type, op)
2214            },
2215        )
2216    }
2217
2218    fn raw_atomic_cmpxchg(
2219        &self,
2220        op_name: &str,
2221        verification_context: &mut VerificationContext<'_>,
2222        dst: Register,
2223        offset: i16,
2224        src: Register,
2225        jump_width: JumpWidth,
2226        op: impl Fn(ScalarValueData, ScalarValueData) -> Result<Option<bool>, ()>,
2227    ) -> Result<(), VerifierErrorKind> {
2228        self.log_atomic_operation(op_name, verification_context, true, dst, offset, src);
2229        let width = match jump_width {
2230            JumpWidth::W32 => DataWidth::U32,
2231            JumpWidth::W64 => DataWidth::U64,
2232        };
2233        let addr = self.reg(dst)?;
2234        let field = Field::new(offset, width);
2235        self.check_atomic_access(&addr, field)?;
2236        let dst = self.load_memory(verification_context, &addr, field)?;
2237        let value = self.reg(src)?;
2238        let r0 = self.reg(0)?;
2239        let branch = self.compute_branch(jump_width, &dst, &r0, op)?;
2240        // r0 = dst
2241        if branch.unwrap_or(true) {
2242            let mut next = self.next()?;
2243            let (dst, r0) =
2244                Type::constraint(&mut next, JumpType::Eq, jump_width, dst.clone(), r0.clone())?;
2245            next.set_reg(0, dst)?;
2246            next.store_memory(verification_context, self.pc, &addr, field, value)?;
2247            verification_context.states.push(next);
2248        }
2249        // r0 != dst
2250        if !branch.unwrap_or(false) {
2251            let mut next = self.next()?;
2252            let (dst, r0) = Type::constraint(&mut next, JumpType::Ne, jump_width, dst, r0)?;
2253            next.set_reg(0, dst.clone())?;
2254            next.store_memory(verification_context, self.pc, &addr, field, dst)?;
2255            verification_context.states.push(next);
2256        }
2257
2258        Ok(())
2259    }
2260    fn endianness<BO: ByteOrder>(
2261        &self,
2262        op_name: &str,
2263        verification_context: &mut VerificationContext<'_>,
2264        dst: Register,
2265        width: DataWidth,
2266    ) -> Result<(), VerifierErrorKind> {
2267        bpf_log!(self, verification_context, "{op_name}{} {}", width.bits(), display_register(dst),);
2268        let bit_op = |value: u64| match width {
2269            DataWidth::U16 => BO::read_u16((value as u16).as_bytes()) as u64,
2270            DataWidth::U32 => BO::read_u32((value as u32).as_bytes()) as u64,
2271            DataWidth::U64 => BO::read_u64(value.as_bytes()),
2272            _ => {
2273                panic!("Unexpected bit width for endianness operation");
2274            }
2275        };
2276        let value = self.reg(dst)?;
2277        let new_value = match value {
2278            Type::ScalarValue(data) => Type::ScalarValue(ScalarValueData::new(
2279                bit_op(data.value),
2280                bit_op(data.unknown_mask),
2281                bit_op(data.unwritten_mask),
2282                U64Range::max(),
2283            )),
2284            _ => Type::default(),
2285        };
2286        let mut next = self.next()?;
2287        next.set_reg(dst, new_value)?;
2288        verification_context.states.push(next);
2289        Ok(())
2290    }
2291
2292    fn compute_branch(
2293        &self,
2294        jump_width: JumpWidth,
2295        op1: &Type,
2296        op2: &Type,
2297        op: impl Fn(ScalarValueData, ScalarValueData) -> Result<Option<bool>, ()>,
2298    ) -> Result<Option<bool>, VerifierErrorKind> {
2299        // Unwrap Releasable operands (verifying they have not been released) so
2300        // pointer comparisons work on their underlying pointer types.
2301        match (jump_width, op1.inner(self)?, op2.inner(self)?) {
2302            (_, Type::ScalarValue(data1), Type::ScalarValue(data2)) => op(*data1, *data2),
2303            (JumpWidth::W64, Type::ScalarValue(data), Type::NullOr { .. })
2304            | (JumpWidth::W64, Type::NullOr { .. }, Type::ScalarValue(data))
2305                if data.is_zero() =>
2306            {
2307                Ok(None)
2308            }
2309
2310            (JumpWidth::W64, Type::ScalarValue(data), t) if data.is_zero() && t.is_non_zero() => {
2311                let non_zero =
2312                    ScalarValueData::UNKNOWN_WRITTEN.update_range(U64Range::new(1, u64::MAX));
2313                op(0.into(), non_zero)
2314            }
2315
2316            (JumpWidth::W64, t, Type::ScalarValue(data)) if data.is_zero() && t.is_non_zero() => {
2317                let non_zero =
2318                    ScalarValueData::UNKNOWN_WRITTEN.update_range(U64Range::new(1, u64::MAX));
2319                op(non_zero, 0.into())
2320            }
2321
2322            (JumpWidth::W64, Type::PtrToStack { offset: x }, Type::PtrToStack { offset: y }) => {
2323                op(x.reg(), y.reg())
2324            }
2325
2326            (
2327                JumpWidth::W64,
2328                Type::PtrToMemory { id: id1, offset: x, .. },
2329                Type::PtrToMemory { id: id2, offset: y, .. },
2330            )
2331            | (
2332                JumpWidth::W64,
2333                Type::PtrToStruct { id: id1, offset: x, .. },
2334                Type::PtrToStruct { id: id2, offset: y, .. },
2335            )
2336            | (
2337                JumpWidth::W64,
2338                Type::PtrToArray { id: id1, offset: x, .. },
2339                Type::PtrToArray { id: id2, offset: y, .. },
2340            ) if *id1 == *id2 => op(*x, *y),
2341
2342            (JumpWidth::W64, Type::PtrToArray { id: id1, .. }, Type::PtrToEndArray { id: id2 })
2343            | (JumpWidth::W64, Type::PtrToEndArray { id: id1 }, Type::PtrToArray { id: id2, .. })
2344                if *id1 == *id2 =>
2345            {
2346                Ok(None)
2347            }
2348
2349            _ => Err(()),
2350        }
2351        .map_err(|_| VerifierErrorKind::InvalidComparison)
2352    }
2353
2354    fn conditional_jump(
2355        &self,
2356        op_name: &str,
2357        verification_context: &mut VerificationContext<'_>,
2358        dst: Register,
2359        src: Source,
2360        offset: i16,
2361        jump_type: JumpType,
2362        jump_width: JumpWidth,
2363        op: impl Fn(ScalarValueData, ScalarValueData) -> Result<Option<bool>, ()>,
2364    ) -> Result<(), VerifierErrorKind> {
2365        bpf_log!(
2366            self,
2367            verification_context,
2368            "{op_name} {}, {}, {}",
2369            display_register(dst),
2370            display_source(src),
2371            print_jump_offset(offset),
2372        );
2373        let op1 = self.reg(dst)?;
2374        let op2 = self.compute_source(src.clone())?;
2375        let apply_constraints_and_register = |mut next: Self,
2376                                              jump_type: JumpType|
2377         -> Result<Self, VerifierErrorKind> {
2378            if jump_type != JumpType::Unknown {
2379                let (new_op1, new_op2) =
2380                    Type::constraint(&mut next, jump_type, jump_width, op1.clone(), op2.clone())?;
2381                if dst < REGISTER_COUNT {
2382                    next.set_reg(dst, new_op1)?;
2383                }
2384                match src {
2385                    Source::Reg(r) => {
2386                        next.set_reg(r, new_op2)?;
2387                    }
2388                    _ => {
2389                        // Nothing to do
2390                    }
2391                }
2392            }
2393            Ok(next)
2394        };
2395        let branch = self.compute_branch(jump_width, &op1, &op2, op)?;
2396        if branch.unwrap_or(true) {
2397            // Do the jump
2398            verification_context.states.push(apply_constraints_and_register(
2399                self.checked_jump_with_offset(verification_context, offset)?,
2400                jump_type,
2401            )?);
2402        }
2403        if !branch.unwrap_or(false) {
2404            // Skip the jump
2405            verification_context.states.push(apply_constraints_and_register(
2406                self.jump_with_offset(0)?,
2407                jump_type.invert(),
2408            )?);
2409        }
2410        Ok(())
2411    }
2412
2413    /// Handles the termination of a `ComputationContext`, performing branch cutting optimization.
2414    ///
2415    /// This method is called when it has been proven that the current context terminates (e.g.,
2416    /// reaches an `exit` instruction).
2417    ///
2418    /// The following steps are performed:
2419    /// 1. **Dependency Calculation:** The data dependencies of the context are computed based on
2420    ///    the dependencies of its terminated children and the instruction at the current PC.
2421    /// 2. **Context Broadening:** At a prune point, the context's state is broadened by clearing
2422    ///    registers and stack slots that are *not* in the calculated data dependencies. This
2423    ///    optimization assumes that data not used by the terminated branch is irrelevant for
2424    ///    future execution paths.
2425    /// 3. **Termination Registration:** At a prune point, the broadened context is added to the
2426    ///    set of terminating contexts if it is not less than any existing terminating context at
2427    ///    the same PC.
2428    /// 4. **Parent Termination:** If all the children of the current context have terminated,
2429    ///    its parent context is recursively terminated.
2430    fn terminate(
2431        self,
2432        verification_context: &mut VerificationContext<'_>,
2433    ) -> Result<(), VerifierError> {
2434        let mut next = Some(self);
2435        // Because of the potential length of the parent chain, do not use recursion.
2436        while let Some(mut current) = next.take() {
2437            // Take the parent to process it at the end and not keep it in the terminating
2438            // contexts.
2439            let parent = current.parent.take();
2440
2441            // 1. Compute the dependencies of the context using the dependencies of its children
2442            //    and the actual operation.
2443            let mut dependencies = current.dependencies.get();
2444
2445            dependencies
2446                .visit(
2447                    &mut DataDependenciesVisitorContext {
2448                        calling_context: &verification_context.calling_context,
2449                        computation_context: &current,
2450                    },
2451                    verification_context.code[current.pc],
2452                )
2453                .map_err(|kind| VerifierError { kind, pc: Some(current.pc) })?;
2454
2455            if let Some(terminating_contexts) =
2456                &mut verification_context.terminating_contexts[current.pc]
2457            {
2458                // 2. Clear the state depending on the dependencies states
2459                for register in 0..GENERAL_REGISTER_COUNT {
2460                    if !dependencies.has_reg(register) {
2461                        current
2462                            .set_reg(register, Default::default())
2463                            .map_err(|kind| VerifierError { kind, pc: Some(current.pc) })?;
2464                    }
2465                }
2466                current.stack.retain(dependencies.stack);
2467
2468                // 3. Add the cleared state to the set of `terminating_contexts`
2469                terminating_contexts.insert(current, dependencies);
2470            }
2471
2472            // 4. Register the computed dependencies in our parent, and terminate it if all
2473            //    dependencies has been computed.
2474            if let Some(parent) = parent {
2475                parent.dependencies.update(|d| d.merge(dependencies));
2476                // To check whether all dependencies have been computed, rely on the fact that the
2477                // Rc count of the parent keep track of how many dependencies are left.
2478                next = Rc::into_inner(parent);
2479            }
2480        }
2481        Ok(())
2482    }
2483}
2484
2485impl Drop for ComputationContext {
2486    fn drop(&mut self) {
2487        let mut next = self.parent.take().and_then(Rc::into_inner);
2488        // Because of the potential length of the parent chain, do not use recursion.
2489        while let Some(mut current) = next {
2490            next = current.parent.take().and_then(Rc::into_inner);
2491        }
2492    }
2493}
2494
2495/// Two types are ordered with `t1` > `t2` if a proof that a program in a state `t1` finish is also
2496/// a proof that a program in a state `t2` finish.
2497impl PartialOrd for ComputationContext {
2498    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2499        if self.pc != other.pc || self.resources.len() != other.resources.len() {
2500            return None;
2501        }
2502        let mut result = Type::compare_list(self.registers.iter(), other.registers.iter())?;
2503        result = associate_orderings(result, self.stack.partial_cmp(&other.stack)?)?;
2504        if self.resources != other.resources {
2505            return None;
2506        }
2507        let mut array_bound_iter1 = self.array_bounds.iter().peekable();
2508        let mut array_bound_iter2 = other.array_bounds.iter().peekable();
2509        let result = loop {
2510            match (array_bound_iter1.peek().cloned(), array_bound_iter2.peek().cloned()) {
2511                (None, None) => break result,
2512                (None, _) => break associate_orderings(result, Ordering::Greater)?,
2513                (_, None) => break associate_orderings(result, Ordering::Less)?,
2514                (Some((k1, v1)), Some((k2, v2))) => match k1.cmp(k2) {
2515                    Ordering::Equal => {
2516                        array_bound_iter1.next();
2517                        array_bound_iter2.next();
2518                        // The comparison is intentionally reversed (v2.cmp(v1) instead of
2519                        // v1.cmp(v2)). `v1` is the bound of the explored state (`self`) and `v2` is
2520                        // the bound of the current state (`other`). A smaller bound value
2521                        // represents a stronger safety guarantee (fewer assumed valid bytes,
2522                        // meaning a more restricted program). Therefore, a state with a smaller
2523                        // bound dominates (is "Greater" than) a state with a larger bound.
2524                        result = associate_orderings(result, v2.cmp(v1))?;
2525                    }
2526                    v @ Ordering::Less => {
2527                        array_bound_iter1.next();
2528                        result = associate_orderings(result, v)?;
2529                    }
2530                    v @ Ordering::Greater => {
2531                        array_bound_iter2.next();
2532                        result = associate_orderings(result, v)?;
2533                    }
2534                },
2535            }
2536        };
2537        Some(result)
2538    }
2539}
2540
2541/// Represents the read data dependencies of an eBPF program branch.
2542///
2543/// This struct tracks which registers and stack positions are *read* by the
2544/// instructions within a branch of the eBPF program.  This information is used
2545/// during branch cutting optimization to broaden terminating contexts.
2546///
2547/// The verifier assumes that data not read by a terminated branch is irrelevant
2548/// for future execution paths and can be safely cleared.
2549#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
2550struct DataDependencies {
2551    /// A bitset of registers read by the children of a context.
2552    registers: u16,
2553    /// A bitset of stack positions read by the children of a context.
2554    stack: u64,
2555}
2556
2557impl DataDependencies {
2558    #[inline]
2559    fn has_reg(&self, reg: Register) -> bool {
2560        (self.registers & (1_u16 << reg)) != 0
2561    }
2562
2563    #[inline]
2564    fn add_reg(&mut self, reg: Register) {
2565        self.registers |= 1_u16 << reg;
2566    }
2567
2568    #[inline]
2569    fn remove_reg(&mut self, reg: Register) -> bool {
2570        let mask = 1_u16 << reg;
2571        let was_set = (self.registers & mask) != 0;
2572        self.registers &= !mask;
2573        was_set
2574    }
2575
2576    #[inline]
2577    fn has_stack(&self, slot: usize) -> bool {
2578        (self.stack & (1_u64 << slot)) != 0
2579    }
2580
2581    #[inline]
2582    fn add_stack(&mut self, slot: usize) {
2583        self.stack |= 1_u64 << slot;
2584    }
2585
2586    #[inline]
2587    fn remove_stack(&mut self, slot: usize) -> bool {
2588        let mask = 1_u64 << slot;
2589        let was_set = (self.stack & mask) != 0;
2590        self.stack &= !mask;
2591        was_set
2592    }
2593
2594    #[inline]
2595    fn add_stack_range(&mut self, start: usize, end: usize) {
2596        if end > start {
2597            let count = end - start;
2598            let mask = if count >= 64 { !0 } else { ((1_u64 << count) - 1) << start };
2599            self.stack |= mask;
2600        }
2601    }
2602
2603    fn merge(self, other: Self) -> Self {
2604        Self { registers: self.registers | other.registers, stack: self.stack | other.stack }
2605    }
2606
2607    fn alu(&mut self, dst: Register, src: Source) -> Result<(), VerifierErrorKind> {
2608        // Only do something if the dst is read, otherwise the computation doesn't matter.
2609        if self.has_reg(dst) {
2610            if let Source::Reg(src) = src {
2611                self.add_reg(src);
2612            }
2613        }
2614        Ok(())
2615    }
2616
2617    fn jmp(&mut self, dst: Register, src: Source) -> Result<(), VerifierErrorKind> {
2618        self.add_reg(dst);
2619        if let Source::Reg(src) = src {
2620            self.add_reg(src);
2621        }
2622        Ok(())
2623    }
2624
2625    fn atomic(
2626        &mut self,
2627        context: &ComputationContext,
2628        fetch: bool,
2629        dst: Register,
2630        offset: i16,
2631        src: Register,
2632        width: DataWidth,
2633        is_cmpxchg: bool,
2634    ) -> Result<(), VerifierErrorKind> {
2635        let mut is_read = false;
2636        if is_cmpxchg && self.has_reg(0) {
2637            is_read = true;
2638        }
2639        if fetch && self.has_reg(src) {
2640            is_read = true;
2641        }
2642        let addr = context.reg(dst)?;
2643        if let Type::PtrToStack { offset: stack_offset } = addr {
2644            let stack_offset = stack_offset.add(offset);
2645            stack_offset.check_valid_offset()?;
2646            let slot = stack_offset.array_index();
2647            if is_read || self.has_stack(slot) {
2648                is_read = true;
2649                self.add_stack(slot);
2650            }
2651        }
2652        if is_read {
2653            self.add_reg(0);
2654            self.add_reg(src);
2655        }
2656        self.add_reg(dst);
2657        Ok(())
2658    }
2659}
2660
2661struct DataDependenciesVisitorContext<'a> {
2662    calling_context: &'a CallingContext,
2663    computation_context: &'a ComputationContext,
2664}
2665
2666impl BpfVisitor for DataDependencies {
2667    type Context<'a> = DataDependenciesVisitorContext<'a>;
2668    type Error = VerifierErrorKind;
2669
2670    fn add<'a>(
2671        &mut self,
2672        _context: &mut Self::Context<'a>,
2673        dst: Register,
2674        src: Source,
2675    ) -> Result<(), VerifierErrorKind> {
2676        self.alu(dst, src)
2677    }
2678    fn add64<'a>(
2679        &mut self,
2680        _context: &mut Self::Context<'a>,
2681        dst: Register,
2682        src: Source,
2683    ) -> Result<(), VerifierErrorKind> {
2684        self.alu(dst, src)
2685    }
2686    fn and<'a>(
2687        &mut self,
2688        _context: &mut Self::Context<'a>,
2689        dst: Register,
2690        src: Source,
2691    ) -> Result<(), VerifierErrorKind> {
2692        self.alu(dst, src)
2693    }
2694    fn and64<'a>(
2695        &mut self,
2696        _context: &mut Self::Context<'a>,
2697        dst: Register,
2698        src: Source,
2699    ) -> Result<(), VerifierErrorKind> {
2700        self.alu(dst, src)
2701    }
2702    fn arsh<'a>(
2703        &mut self,
2704        _context: &mut Self::Context<'a>,
2705        dst: Register,
2706        src: Source,
2707    ) -> Result<(), VerifierErrorKind> {
2708        self.alu(dst, src)
2709    }
2710    fn arsh64<'a>(
2711        &mut self,
2712        _context: &mut Self::Context<'a>,
2713        dst: Register,
2714        src: Source,
2715    ) -> Result<(), VerifierErrorKind> {
2716        self.alu(dst, src)
2717    }
2718    fn div<'a>(
2719        &mut self,
2720        _context: &mut Self::Context<'a>,
2721        dst: Register,
2722        src: Source,
2723    ) -> Result<(), VerifierErrorKind> {
2724        self.alu(dst, src)
2725    }
2726    fn div64<'a>(
2727        &mut self,
2728        _context: &mut Self::Context<'a>,
2729        dst: Register,
2730        src: Source,
2731    ) -> Result<(), VerifierErrorKind> {
2732        self.alu(dst, src)
2733    }
2734    fn lsh<'a>(
2735        &mut self,
2736        _context: &mut Self::Context<'a>,
2737        dst: Register,
2738        src: Source,
2739    ) -> Result<(), VerifierErrorKind> {
2740        self.alu(dst, src)
2741    }
2742    fn lsh64<'a>(
2743        &mut self,
2744        _context: &mut Self::Context<'a>,
2745        dst: Register,
2746        src: Source,
2747    ) -> Result<(), VerifierErrorKind> {
2748        self.alu(dst, src)
2749    }
2750    fn r#mod<'a>(
2751        &mut self,
2752        _context: &mut Self::Context<'a>,
2753        dst: Register,
2754        src: Source,
2755    ) -> Result<(), VerifierErrorKind> {
2756        self.alu(dst, src)
2757    }
2758    fn mod64<'a>(
2759        &mut self,
2760        _context: &mut Self::Context<'a>,
2761        dst: Register,
2762        src: Source,
2763    ) -> Result<(), VerifierErrorKind> {
2764        self.alu(dst, src)
2765    }
2766    fn mul<'a>(
2767        &mut self,
2768        _context: &mut Self::Context<'a>,
2769        dst: Register,
2770        src: Source,
2771    ) -> Result<(), VerifierErrorKind> {
2772        self.alu(dst, src)
2773    }
2774    fn mul64<'a>(
2775        &mut self,
2776        _context: &mut Self::Context<'a>,
2777        dst: Register,
2778        src: Source,
2779    ) -> Result<(), VerifierErrorKind> {
2780        self.alu(dst, src)
2781    }
2782    fn or<'a>(
2783        &mut self,
2784        _context: &mut Self::Context<'a>,
2785        dst: Register,
2786        src: Source,
2787    ) -> Result<(), VerifierErrorKind> {
2788        self.alu(dst, src)
2789    }
2790    fn or64<'a>(
2791        &mut self,
2792        _context: &mut Self::Context<'a>,
2793        dst: Register,
2794        src: Source,
2795    ) -> Result<(), VerifierErrorKind> {
2796        self.alu(dst, src)
2797    }
2798    fn rsh<'a>(
2799        &mut self,
2800        _context: &mut Self::Context<'a>,
2801        dst: Register,
2802        src: Source,
2803    ) -> Result<(), VerifierErrorKind> {
2804        self.alu(dst, src)
2805    }
2806    fn rsh64<'a>(
2807        &mut self,
2808        _context: &mut Self::Context<'a>,
2809        dst: Register,
2810        src: Source,
2811    ) -> Result<(), VerifierErrorKind> {
2812        self.alu(dst, src)
2813    }
2814    fn sub<'a>(
2815        &mut self,
2816        _context: &mut Self::Context<'a>,
2817        dst: Register,
2818        src: Source,
2819    ) -> Result<(), VerifierErrorKind> {
2820        self.alu(dst, src)
2821    }
2822    fn sub64<'a>(
2823        &mut self,
2824        _context: &mut Self::Context<'a>,
2825        dst: Register,
2826        src: Source,
2827    ) -> Result<(), VerifierErrorKind> {
2828        self.alu(dst, src)
2829    }
2830    fn xor<'a>(
2831        &mut self,
2832        _context: &mut Self::Context<'a>,
2833        dst: Register,
2834        src: Source,
2835    ) -> Result<(), VerifierErrorKind> {
2836        self.alu(dst, src)
2837    }
2838    fn xor64<'a>(
2839        &mut self,
2840        _context: &mut Self::Context<'a>,
2841        dst: Register,
2842        src: Source,
2843    ) -> Result<(), VerifierErrorKind> {
2844        self.alu(dst, src)
2845    }
2846
2847    fn mov<'a>(
2848        &mut self,
2849        _context: &mut Self::Context<'a>,
2850        dst: Register,
2851        src: Source,
2852    ) -> Result<(), VerifierErrorKind> {
2853        if src == Source::Reg(dst) || !self.has_reg(dst) {
2854            return Ok(());
2855        }
2856        if let Source::Reg(src) = src {
2857            self.add_reg(src);
2858        }
2859        self.remove_reg(dst);
2860        Ok(())
2861    }
2862    fn mov64<'a>(
2863        &mut self,
2864        context: &mut Self::Context<'a>,
2865        dst: Register,
2866        src: Source,
2867    ) -> Result<(), VerifierErrorKind> {
2868        self.mov(context, dst, src)
2869    }
2870
2871    fn neg<'a>(
2872        &mut self,
2873        _context: &mut Self::Context<'a>,
2874        _dst: Register,
2875    ) -> Result<(), VerifierErrorKind> {
2876        // This is reading and writing the same value. This induces no change in dependencies.
2877        Ok(())
2878    }
2879    fn neg64<'a>(
2880        &mut self,
2881        _context: &mut Self::Context<'a>,
2882        _dst: Register,
2883    ) -> Result<(), VerifierErrorKind> {
2884        // This is reading and writing the same value. This induces no change in dependencies.
2885        Ok(())
2886    }
2887
2888    fn be<'a>(
2889        &mut self,
2890        _context: &mut Self::Context<'a>,
2891        _dst: Register,
2892        _width: DataWidth,
2893    ) -> Result<(), VerifierErrorKind> {
2894        // This is reading and writing the same value. This induces no change in dependencies.
2895        Ok(())
2896    }
2897    fn le<'a>(
2898        &mut self,
2899        _context: &mut Self::Context<'a>,
2900        _dst: Register,
2901        _width: DataWidth,
2902    ) -> Result<(), VerifierErrorKind> {
2903        // This is reading and writing the same value. This induces no change in dependencies.
2904        Ok(())
2905    }
2906
2907    fn call_external<'a>(
2908        &mut self,
2909        context: &mut Self::Context<'a>,
2910        index: u32,
2911    ) -> Result<(), VerifierErrorKind> {
2912        let Some(helper) = context.calling_context.helpers.get(&index).cloned() else {
2913            return Err(VerifierErrorKind::UnknownHelper(index));
2914        };
2915        // A helper reads the stack region each `input` memory-parameter argument
2916        // points at, so those stack slots are data dependencies, like load/store.
2917        let comp = &context.computation_context;
2918        for (arg_index, arg) in helper.signature.args.iter().enumerate() {
2919            if let Type::MemoryParameter { size, input: true, .. } = arg {
2920                if let Type::PtrToStack { offset } = comp.reg((arg_index + 1) as Register)? {
2921                    let end = offset.add(size.size(comp)?);
2922                    if offset.check_valid_offset().is_ok() && end.is_within_stack() {
2923                        let start_idx = offset.array_index();
2924                        let end_idx = end.array_index();
2925                        self.add_stack_range(start_idx, end_idx);
2926                        if end.sub_index() != 0 {
2927                            self.add_stack(end_idx);
2928                        }
2929                    }
2930                }
2931            }
2932        }
2933        // 0 is overwritten and 1 to 5 are scratch registers
2934        self.registers &= !0b0011_1111;
2935        // 1 to k are parameters.
2936        let num_args = helper.signature.args.len();
2937        self.registers |= ((1_u16 << num_args) - 1) << 1;
2938        Ok(())
2939    }
2940
2941    fn exit<'a>(&mut self, _context: &mut Self::Context<'a>) -> Result<(), VerifierErrorKind> {
2942        // This read r0 unconditionally.
2943        self.add_reg(0);
2944        Ok(())
2945    }
2946
2947    fn jump<'a>(
2948        &mut self,
2949        _context: &mut Self::Context<'a>,
2950        _offset: i16,
2951    ) -> Result<(), VerifierErrorKind> {
2952        // This doesn't do anything with values.
2953        Ok(())
2954    }
2955
2956    fn jeq<'a>(
2957        &mut self,
2958        _context: &mut Self::Context<'a>,
2959        dst: Register,
2960        src: Source,
2961        offset: i16,
2962    ) -> Result<(), VerifierErrorKind> {
2963        self.jmp(dst, src)
2964    }
2965    fn jeq64<'a>(
2966        &mut self,
2967        _context: &mut Self::Context<'a>,
2968        dst: Register,
2969        src: Source,
2970        offset: i16,
2971    ) -> Result<(), VerifierErrorKind> {
2972        self.jmp(dst, src)
2973    }
2974    fn jne<'a>(
2975        &mut self,
2976        _context: &mut Self::Context<'a>,
2977        dst: Register,
2978        src: Source,
2979        offset: i16,
2980    ) -> Result<(), VerifierErrorKind> {
2981        self.jmp(dst, src)
2982    }
2983    fn jne64<'a>(
2984        &mut self,
2985        _context: &mut Self::Context<'a>,
2986        dst: Register,
2987        src: Source,
2988        offset: i16,
2989    ) -> Result<(), VerifierErrorKind> {
2990        self.jmp(dst, src)
2991    }
2992    fn jge<'a>(
2993        &mut self,
2994        _context: &mut Self::Context<'a>,
2995        dst: Register,
2996        src: Source,
2997        offset: i16,
2998    ) -> Result<(), VerifierErrorKind> {
2999        self.jmp(dst, src)
3000    }
3001    fn jge64<'a>(
3002        &mut self,
3003        _context: &mut Self::Context<'a>,
3004        dst: Register,
3005        src: Source,
3006        offset: i16,
3007    ) -> Result<(), VerifierErrorKind> {
3008        self.jmp(dst, src)
3009    }
3010    fn jgt<'a>(
3011        &mut self,
3012        _context: &mut Self::Context<'a>,
3013        dst: Register,
3014        src: Source,
3015        offset: i16,
3016    ) -> Result<(), VerifierErrorKind> {
3017        self.jmp(dst, src)
3018    }
3019    fn jgt64<'a>(
3020        &mut self,
3021        _context: &mut Self::Context<'a>,
3022        dst: Register,
3023        src: Source,
3024        offset: i16,
3025    ) -> Result<(), VerifierErrorKind> {
3026        self.jmp(dst, src)
3027    }
3028    fn jle<'a>(
3029        &mut self,
3030        _context: &mut Self::Context<'a>,
3031        dst: Register,
3032        src: Source,
3033        offset: i16,
3034    ) -> Result<(), VerifierErrorKind> {
3035        self.jmp(dst, src)
3036    }
3037    fn jle64<'a>(
3038        &mut self,
3039        _context: &mut Self::Context<'a>,
3040        dst: Register,
3041        src: Source,
3042        offset: i16,
3043    ) -> Result<(), VerifierErrorKind> {
3044        self.jmp(dst, src)
3045    }
3046    fn jlt<'a>(
3047        &mut self,
3048        _context: &mut Self::Context<'a>,
3049        dst: Register,
3050        src: Source,
3051        offset: i16,
3052    ) -> Result<(), VerifierErrorKind> {
3053        self.jmp(dst, src)
3054    }
3055    fn jlt64<'a>(
3056        &mut self,
3057        _context: &mut Self::Context<'a>,
3058        dst: Register,
3059        src: Source,
3060        offset: i16,
3061    ) -> Result<(), VerifierErrorKind> {
3062        self.jmp(dst, src)
3063    }
3064    fn jsge<'a>(
3065        &mut self,
3066        _context: &mut Self::Context<'a>,
3067        dst: Register,
3068        src: Source,
3069        offset: i16,
3070    ) -> Result<(), VerifierErrorKind> {
3071        self.jmp(dst, src)
3072    }
3073    fn jsge64<'a>(
3074        &mut self,
3075        _context: &mut Self::Context<'a>,
3076        dst: Register,
3077        src: Source,
3078        offset: i16,
3079    ) -> Result<(), VerifierErrorKind> {
3080        self.jmp(dst, src)
3081    }
3082    fn jsgt<'a>(
3083        &mut self,
3084        _context: &mut Self::Context<'a>,
3085        dst: Register,
3086        src: Source,
3087        offset: i16,
3088    ) -> Result<(), VerifierErrorKind> {
3089        self.jmp(dst, src)
3090    }
3091    fn jsgt64<'a>(
3092        &mut self,
3093        _context: &mut Self::Context<'a>,
3094        dst: Register,
3095        src: Source,
3096        offset: i16,
3097    ) -> Result<(), VerifierErrorKind> {
3098        self.jmp(dst, src)
3099    }
3100    fn jsle<'a>(
3101        &mut self,
3102        _context: &mut Self::Context<'a>,
3103        dst: Register,
3104        src: Source,
3105        offset: i16,
3106    ) -> Result<(), VerifierErrorKind> {
3107        self.jmp(dst, src)
3108    }
3109    fn jsle64<'a>(
3110        &mut self,
3111        _context: &mut Self::Context<'a>,
3112        dst: Register,
3113        src: Source,
3114        offset: i16,
3115    ) -> Result<(), VerifierErrorKind> {
3116        self.jmp(dst, src)
3117    }
3118    fn jslt<'a>(
3119        &mut self,
3120        _context: &mut Self::Context<'a>,
3121        dst: Register,
3122        src: Source,
3123        offset: i16,
3124    ) -> Result<(), VerifierErrorKind> {
3125        self.jmp(dst, src)
3126    }
3127    fn jslt64<'a>(
3128        &mut self,
3129        _context: &mut Self::Context<'a>,
3130        dst: Register,
3131        src: Source,
3132        offset: i16,
3133    ) -> Result<(), VerifierErrorKind> {
3134        self.jmp(dst, src)
3135    }
3136    fn jset<'a>(
3137        &mut self,
3138        _context: &mut Self::Context<'a>,
3139        dst: Register,
3140        src: Source,
3141        offset: i16,
3142    ) -> Result<(), VerifierErrorKind> {
3143        self.jmp(dst, src)
3144    }
3145    fn jset64<'a>(
3146        &mut self,
3147        _context: &mut Self::Context<'a>,
3148        dst: Register,
3149        src: Source,
3150        offset: i16,
3151    ) -> Result<(), VerifierErrorKind> {
3152        self.jmp(dst, src)
3153    }
3154
3155    fn atomic_add<'a>(
3156        &mut self,
3157        context: &mut Self::Context<'a>,
3158        fetch: bool,
3159        dst: Register,
3160        offset: i16,
3161        src: Register,
3162    ) -> Result<(), VerifierErrorKind> {
3163        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U32, false)
3164    }
3165
3166    fn atomic_add64<'a>(
3167        &mut self,
3168        context: &mut Self::Context<'a>,
3169        fetch: bool,
3170        dst: Register,
3171        offset: i16,
3172        src: Register,
3173    ) -> Result<(), VerifierErrorKind> {
3174        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U64, false)
3175    }
3176
3177    fn atomic_and<'a>(
3178        &mut self,
3179        context: &mut Self::Context<'a>,
3180        fetch: bool,
3181        dst: Register,
3182        offset: i16,
3183        src: Register,
3184    ) -> Result<(), VerifierErrorKind> {
3185        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U32, false)
3186    }
3187
3188    fn atomic_and64<'a>(
3189        &mut self,
3190        context: &mut Self::Context<'a>,
3191        fetch: bool,
3192        dst: Register,
3193        offset: i16,
3194        src: Register,
3195    ) -> Result<(), VerifierErrorKind> {
3196        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U64, false)
3197    }
3198
3199    fn atomic_or<'a>(
3200        &mut self,
3201        context: &mut Self::Context<'a>,
3202        fetch: bool,
3203        dst: Register,
3204        offset: i16,
3205        src: Register,
3206    ) -> Result<(), VerifierErrorKind> {
3207        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U32, false)
3208    }
3209
3210    fn atomic_or64<'a>(
3211        &mut self,
3212        context: &mut Self::Context<'a>,
3213        fetch: bool,
3214        dst: Register,
3215        offset: i16,
3216        src: Register,
3217    ) -> Result<(), VerifierErrorKind> {
3218        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U64, false)
3219    }
3220
3221    fn atomic_xor<'a>(
3222        &mut self,
3223        context: &mut Self::Context<'a>,
3224        fetch: bool,
3225        dst: Register,
3226        offset: i16,
3227        src: Register,
3228    ) -> Result<(), VerifierErrorKind> {
3229        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U32, false)
3230    }
3231
3232    fn atomic_xor64<'a>(
3233        &mut self,
3234        context: &mut Self::Context<'a>,
3235        fetch: bool,
3236        dst: Register,
3237        offset: i16,
3238        src: Register,
3239    ) -> Result<(), VerifierErrorKind> {
3240        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U64, false)
3241    }
3242
3243    fn atomic_xchg<'a>(
3244        &mut self,
3245        context: &mut Self::Context<'a>,
3246        fetch: bool,
3247        dst: Register,
3248        offset: i16,
3249        src: Register,
3250    ) -> Result<(), VerifierErrorKind> {
3251        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U32, false)
3252    }
3253
3254    fn atomic_xchg64<'a>(
3255        &mut self,
3256        context: &mut Self::Context<'a>,
3257        fetch: bool,
3258        dst: Register,
3259        offset: i16,
3260        src: Register,
3261    ) -> Result<(), VerifierErrorKind> {
3262        self.atomic(&context.computation_context, fetch, dst, offset, src, DataWidth::U64, false)
3263    }
3264
3265    fn atomic_cmpxchg<'a>(
3266        &mut self,
3267        context: &mut Self::Context<'a>,
3268        dst: Register,
3269        offset: i16,
3270        src: Register,
3271    ) -> Result<(), VerifierErrorKind> {
3272        self.atomic(&context.computation_context, true, dst, offset, src, DataWidth::U32, true)
3273    }
3274
3275    fn atomic_cmpxchg64<'a>(
3276        &mut self,
3277        context: &mut Self::Context<'a>,
3278        dst: Register,
3279        offset: i16,
3280        src: Register,
3281    ) -> Result<(), VerifierErrorKind> {
3282        self.atomic(&context.computation_context, true, dst, offset, src, DataWidth::U64, true)
3283    }
3284
3285    fn load<'a>(
3286        &mut self,
3287        context: &mut Self::Context<'a>,
3288        dst: Register,
3289        offset: i16,
3290        src: Register,
3291        width: DataWidth,
3292    ) -> Result<(), VerifierErrorKind> {
3293        let context = &context.computation_context;
3294        if self.has_reg(dst) {
3295            let addr = context.reg(src)?;
3296            if let Type::PtrToStack { offset: stack_offset } = addr {
3297                let stack_offset = stack_offset.add(offset);
3298                stack_offset.check_valid_offset()?;
3299                self.add_stack(stack_offset.array_index());
3300            }
3301        }
3302        self.add_reg(src);
3303        Ok(())
3304    }
3305
3306    fn load64<'a>(
3307        &mut self,
3308        _context: &mut Self::Context<'a>,
3309        dst: Register,
3310        _src: u8,
3311        _lower: u32,
3312    ) -> Result<(), VerifierErrorKind> {
3313        self.remove_reg(dst);
3314        Ok(())
3315    }
3316
3317    fn load_from_packet<'a>(
3318        &mut self,
3319        _context: &mut Self::Context<'a>,
3320        dst: Register,
3321        src: Register,
3322        _offset: i32,
3323        register_offset: Option<Register>,
3324        _width: DataWidth,
3325    ) -> Result<(), VerifierErrorKind> {
3326        // 1 to 5 are scratch registers (bits 1..=5)
3327        self.registers &= !0b0011_1110;
3328        self.remove_reg(dst);
3329        // The types of the packet and of the offset are always checked, and r0 is checked to be a
3330        // written scalar on the exit path taken when the load fails.
3331        self.add_reg(src);
3332        if let Some(reg) = register_offset {
3333            self.add_reg(reg);
3334        }
3335        self.add_reg(0);
3336        Ok(())
3337    }
3338
3339    fn store<'a>(
3340        &mut self,
3341        context: &mut Self::Context<'a>,
3342        dst: Register,
3343        offset: i16,
3344        src: Source,
3345        width: DataWidth,
3346    ) -> Result<(), VerifierErrorKind> {
3347        let context = &context.computation_context;
3348        let addr = context.reg(dst)?;
3349        if let Type::PtrToStack { offset: stack_offset } = addr {
3350            let stack_offset = stack_offset.add(offset);
3351            stack_offset.check_valid_offset()?;
3352            if self.remove_stack(stack_offset.array_index()) {
3353                if let Source::Reg(src) = src {
3354                    self.add_reg(src);
3355                }
3356            }
3357        } else {
3358            if let Source::Reg(src) = src {
3359                self.add_reg(src);
3360            }
3361            self.add_reg(dst);
3362        }
3363
3364        Ok(())
3365    }
3366}
3367
3368#[derive(Debug)]
3369struct TerminatingContext {
3370    computation_context: ComputationContext,
3371    dependencies: DataDependencies,
3372    /// Known scalar register values of `computation_context`, cached to filter comparisons.
3373    known_scalars: KnownScalars,
3374}
3375
3376/// The contexts known to terminate at a given pc. The contexts are pairwise incomparable: each
3377/// time a bigger context is inserted, the smaller ones are removed.
3378#[derive(Debug, Default)]
3379struct TerminatingContexts {
3380    contexts: Vec<TerminatingContext>,
3381}
3382
3383impl TerminatingContexts {
3384    /// Returns the most recently inserted context that is greater than or equal to `context`.
3385    fn find_greater_or_equal(&self, context: &ComputationContext) -> Option<&TerminatingContext> {
3386        if self.contexts.is_empty() {
3387            return None;
3388        }
3389        let known_scalars = KnownScalars::new(context);
3390        self.contexts
3391            .iter()
3392            .rev()
3393            .find(|c| c.known_scalars >= known_scalars && c.computation_context >= *context)
3394    }
3395
3396    /// Inserts `context` unless a context greater than or equal to it is already present, and
3397    /// removes the contexts smaller than it.
3398    fn insert(&mut self, context: ComputationContext, dependencies: DataDependencies) {
3399        let known_scalars = KnownScalars::new(&context);
3400        let mut is_dominated = false;
3401        self.contexts.retain(|c| {
3402            // Skip the full comparison if the contexts are known to be incomparable. This does not
3403            // change the result: the full comparison would return `None`.
3404            if c.known_scalars.partial_cmp(&known_scalars).is_none() {
3405                return true;
3406            }
3407            match c.computation_context.partial_cmp(&context) {
3408                Some(Ordering::Less) => false,
3409                Some(Ordering::Equal) | Some(Ordering::Greater) => {
3410                    // If the list contains a context greater or equal to the current one, it
3411                    // should not be added.
3412                    is_dominated = true;
3413                    true
3414                }
3415                None => true,
3416            }
3417        });
3418        if !is_dominated {
3419            self.contexts.push(TerminatingContext {
3420                computation_context: context,
3421                dependencies,
3422                known_scalars,
3423            });
3424        }
3425    }
3426}
3427
3428/// Tracks which general registers of a `ComputationContext` are constrained to a single known
3429/// constant scalar.
3430///
3431/// Used as a fast pre-filter before `ComputationContext::partial_cmp`: if `c1` has a known
3432/// constant `v` in register `i`, then `c1 >= c2` requires `c2` to also have `v` in register `i`.
3433#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
3434struct KnownScalars {
3435    /// Bitmask of the registers holding a known scalar.
3436    mask: u16,
3437    /// The value of the registers in `mask`. The other values are 0.
3438    values: [u64; GENERAL_REGISTER_COUNT as usize],
3439}
3440
3441impl KnownScalars {
3442    fn new(context: &ComputationContext) -> Self {
3443        let mut result = Self::default();
3444        for (i, reg) in context.registers.iter().enumerate() {
3445            if let Type::ScalarValue(data) = reg {
3446                if data.is_known() {
3447                    result.mask |= 1 << i;
3448                    result.values[i] = data.value;
3449                }
3450            }
3451        }
3452        result
3453    }
3454}
3455
3456impl PartialOrd for KnownScalars {
3457    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
3458        let result = match (self.mask & !other.mask != 0, !self.mask & other.mask != 0) {
3459            (true, true) => return None,
3460            (true, false) => Ordering::Less,
3461            (false, true) => Ordering::Greater,
3462            (false, false) => Ordering::Equal,
3463        };
3464        bit_indices((self.mask & other.mask).into())
3465            .all(|i| self.values[i] == other.values[i])
3466            .then_some(result)
3467    }
3468}
3469
3470#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3471enum AluType {
3472    Plain,
3473    Sub,
3474    Add,
3475}
3476
3477impl AluType {
3478    /// Can this operation be done one a pointer and a scalar.
3479    fn is_ptr_compatible(&self) -> bool {
3480        match self {
3481            Self::Sub | Self::Add => true,
3482            _ => false,
3483        }
3484    }
3485}
3486
3487#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3488enum JumpWidth {
3489    W32,
3490    W64,
3491}
3492
3493#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3494enum JumpType {
3495    Eq,
3496    Ge,
3497    Gt,
3498    Le,
3499    LooseComparaison,
3500    Lt,
3501    Ne,
3502    StrictComparaison,
3503    Unknown,
3504}
3505
3506impl JumpType {
3507    fn invert(&self) -> Self {
3508        match self {
3509            Self::Eq => Self::Ne,
3510            Self::Ge => Self::Lt,
3511            Self::Gt => Self::Le,
3512            Self::Le => Self::Gt,
3513            Self::LooseComparaison => Self::StrictComparaison,
3514            Self::Lt => Self::Ge,
3515            Self::Ne => Self::Eq,
3516            Self::StrictComparaison => Self::LooseComparaison,
3517            Self::Unknown => Self::Unknown,
3518        }
3519    }
3520
3521    fn is_strict(&self) -> bool {
3522        match self {
3523            Self::Gt | Self::Lt | Self::Ne | Self::StrictComparaison => true,
3524            _ => false,
3525        }
3526    }
3527}
3528
3529#[derive(Clone, Copy)]
3530struct DisplayRegister(Register);
3531
3532impl std::fmt::Display for DisplayRegister {
3533    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3534        write!(f, "%r{}", self.0)
3535    }
3536}
3537
3538fn display_register(register: Register) -> DisplayRegister {
3539    DisplayRegister(register)
3540}
3541
3542#[derive(Clone, Copy)]
3543struct DisplaySource(Source);
3544
3545impl std::fmt::Display for DisplaySource {
3546    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3547        match self.0 {
3548            Source::Reg(r) => display_register(r).fmt(f),
3549            Source::Value(v) => write!(f, "0x{v:x}"),
3550        }
3551    }
3552}
3553
3554fn display_source(src: Source) -> DisplaySource {
3555    DisplaySource(src)
3556}
3557
3558/// Visiting an instruction pushes the successors of `self` to `VerificationContext::states`.
3559impl BpfVisitor for &ComputationContext {
3560    type Context<'a> = VerificationContext<'a>;
3561    type Error = VerifierErrorKind;
3562
3563    fn add<'a>(
3564        &mut self,
3565        context: &mut Self::Context<'a>,
3566        dst: Register,
3567        src: Source,
3568    ) -> Result<(), VerifierErrorKind> {
3569        self.alu(Some("add32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x + y))
3570    }
3571    fn add64<'a>(
3572        &mut self,
3573        context: &mut Self::Context<'a>,
3574        dst: Register,
3575        src: Source,
3576    ) -> Result<(), VerifierErrorKind> {
3577        self.alu(Some("add"), context, dst, src, AluType::Add, |x, y| x + y)
3578    }
3579    fn and<'a>(
3580        &mut self,
3581        context: &mut Self::Context<'a>,
3582        dst: Register,
3583        src: Source,
3584    ) -> Result<(), VerifierErrorKind> {
3585        self.alu(Some("and32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x & y))
3586    }
3587    fn and64<'a>(
3588        &mut self,
3589        context: &mut Self::Context<'a>,
3590        dst: Register,
3591        src: Source,
3592    ) -> Result<(), VerifierErrorKind> {
3593        self.alu(Some("and"), context, dst, src, AluType::Plain, |x, y| x & y)
3594    }
3595    fn arsh<'a>(
3596        &mut self,
3597        context: &mut Self::Context<'a>,
3598        dst: Register,
3599        src: Source,
3600    ) -> Result<(), VerifierErrorKind> {
3601        self.alu(Some("arsh32"), context, dst, src, AluType::Plain, |x, y| {
3602            alu32(x, y, |x, y| x.ashr(y))
3603        })
3604    }
3605    fn arsh64<'a>(
3606        &mut self,
3607        context: &mut Self::Context<'a>,
3608        dst: Register,
3609        src: Source,
3610    ) -> Result<(), VerifierErrorKind> {
3611        self.alu(Some("arsh"), context, dst, src, AluType::Plain, |x, y| x.ashr(y))
3612    }
3613    fn div<'a>(
3614        &mut self,
3615        context: &mut Self::Context<'a>,
3616        dst: Register,
3617        src: Source,
3618    ) -> Result<(), VerifierErrorKind> {
3619        self.alu(Some("div32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x / y))
3620    }
3621    fn div64<'a>(
3622        &mut self,
3623        context: &mut Self::Context<'a>,
3624        dst: Register,
3625        src: Source,
3626    ) -> Result<(), VerifierErrorKind> {
3627        self.alu(Some("div"), context, dst, src, AluType::Plain, |x, y| x / y)
3628    }
3629    fn lsh<'a>(
3630        &mut self,
3631        context: &mut Self::Context<'a>,
3632        dst: Register,
3633        src: Source,
3634    ) -> Result<(), VerifierErrorKind> {
3635        self.alu(Some("lsh32"), context, dst, src, AluType::Plain, |x, y| {
3636            alu32(x, y, |x, y| x << y)
3637        })
3638    }
3639    fn lsh64<'a>(
3640        &mut self,
3641        context: &mut Self::Context<'a>,
3642        dst: Register,
3643        src: Source,
3644    ) -> Result<(), VerifierErrorKind> {
3645        self.alu(Some("lsh"), context, dst, src, AluType::Plain, |x, y| x << y)
3646    }
3647    fn r#mod<'a>(
3648        &mut self,
3649        context: &mut Self::Context<'a>,
3650        dst: Register,
3651        src: Source,
3652    ) -> Result<(), VerifierErrorKind> {
3653        self.alu(Some("mod32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x % y))
3654    }
3655    fn mod64<'a>(
3656        &mut self,
3657        context: &mut Self::Context<'a>,
3658        dst: Register,
3659        src: Source,
3660    ) -> Result<(), VerifierErrorKind> {
3661        self.alu(Some("mod"), context, dst, src, AluType::Plain, |x, y| x % y)
3662    }
3663    fn mov<'a>(
3664        &mut self,
3665        context: &mut Self::Context<'a>,
3666        dst: Register,
3667        src: Source,
3668    ) -> Result<(), VerifierErrorKind> {
3669        bpf_log!(self, context, "mov32 {}, {}", display_register(dst), display_source(src));
3670        let src = self.compute_source(src)?;
3671        let value = match src {
3672            Type::ScalarValue(data) => {
3673                let value = (data.value as u32) as u64;
3674                let unknown_mask = (data.unknown_mask as u32) as u64;
3675                let unwritten_mask = (data.unwritten_mask as u32) as u64;
3676                let urange = U64Range::compute_range_for_bytes_swap(0.into(), data.urange, 0, 0, 4);
3677                Type::ScalarValue(ScalarValueData::new(value, unknown_mask, unwritten_mask, urange))
3678            }
3679            _ => Type::default(),
3680        };
3681        let mut next = self.next()?;
3682        next.set_reg(dst, value)?;
3683        context.states.push(next);
3684        Ok(())
3685    }
3686    fn mov64<'a>(
3687        &mut self,
3688        context: &mut Self::Context<'a>,
3689        dst: Register,
3690        src: Source,
3691    ) -> Result<(), VerifierErrorKind> {
3692        bpf_log!(self, context, "mov {}, {}", display_register(dst), display_source(src));
3693        let src = self.compute_source(src)?;
3694        let mut next = self.next()?;
3695        next.set_reg(dst, src)?;
3696        context.states.push(next);
3697        Ok(())
3698    }
3699    fn mul<'a>(
3700        &mut self,
3701        context: &mut Self::Context<'a>,
3702        dst: Register,
3703        src: Source,
3704    ) -> Result<(), VerifierErrorKind> {
3705        self.alu(Some("mul32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x * y))
3706    }
3707    fn mul64<'a>(
3708        &mut self,
3709        context: &mut Self::Context<'a>,
3710        dst: Register,
3711        src: Source,
3712    ) -> Result<(), VerifierErrorKind> {
3713        self.alu(Some("mul"), context, dst, src, AluType::Plain, |x, y| x * y)
3714    }
3715    fn or<'a>(
3716        &mut self,
3717        context: &mut Self::Context<'a>,
3718        dst: Register,
3719        src: Source,
3720    ) -> Result<(), VerifierErrorKind> {
3721        self.alu(Some("or32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x | y))
3722    }
3723    fn or64<'a>(
3724        &mut self,
3725        context: &mut Self::Context<'a>,
3726        dst: Register,
3727        src: Source,
3728    ) -> Result<(), VerifierErrorKind> {
3729        self.alu(Some("or"), context, dst, src, AluType::Plain, |x, y| x | y)
3730    }
3731    fn rsh<'a>(
3732        &mut self,
3733        context: &mut Self::Context<'a>,
3734        dst: Register,
3735        src: Source,
3736    ) -> Result<(), VerifierErrorKind> {
3737        self.alu(Some("rsh32"), context, dst, src, AluType::Plain, |x, y| {
3738            alu32(x, y, |x, y| x >> y)
3739        })
3740    }
3741    fn rsh64<'a>(
3742        &mut self,
3743        context: &mut Self::Context<'a>,
3744        dst: Register,
3745        src: Source,
3746    ) -> Result<(), VerifierErrorKind> {
3747        self.alu(Some("rsh"), context, dst, src, AluType::Plain, |x, y| x >> y)
3748    }
3749    fn sub<'a>(
3750        &mut self,
3751        context: &mut Self::Context<'a>,
3752        dst: Register,
3753        src: Source,
3754    ) -> Result<(), VerifierErrorKind> {
3755        self.alu(Some("sub32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x - y))
3756    }
3757    fn sub64<'a>(
3758        &mut self,
3759        context: &mut Self::Context<'a>,
3760        dst: Register,
3761        src: Source,
3762    ) -> Result<(), VerifierErrorKind> {
3763        self.alu(Some("sub"), context, dst, src, AluType::Sub, |x, y| x - y)
3764    }
3765    fn xor<'a>(
3766        &mut self,
3767        context: &mut Self::Context<'a>,
3768        dst: Register,
3769        src: Source,
3770    ) -> Result<(), VerifierErrorKind> {
3771        self.alu(Some("xor32"), context, dst, src, AluType::Plain, |x, y| alu32(x, y, |x, y| x ^ y))
3772    }
3773    fn xor64<'a>(
3774        &mut self,
3775        context: &mut Self::Context<'a>,
3776        dst: Register,
3777        src: Source,
3778    ) -> Result<(), VerifierErrorKind> {
3779        self.alu(Some("xor"), context, dst, src, AluType::Plain, |x, y| x ^ y)
3780    }
3781
3782    fn neg<'a>(
3783        &mut self,
3784        context: &mut Self::Context<'a>,
3785        dst: Register,
3786    ) -> Result<(), VerifierErrorKind> {
3787        bpf_log!(self, context, "neg32 {}", display_register(dst));
3788        self.alu(None, context, dst, Source::Value(0), AluType::Plain, |x, y| {
3789            alu32(x, y, |x, _y| -x)
3790        })
3791    }
3792    fn neg64<'a>(
3793        &mut self,
3794        context: &mut Self::Context<'a>,
3795        dst: Register,
3796    ) -> Result<(), VerifierErrorKind> {
3797        bpf_log!(self, context, "neg {}", display_register(dst));
3798        self.alu(None, context, dst, Source::Value(0), AluType::Plain, |x, _y| -x)
3799    }
3800
3801    fn be<'a>(
3802        &mut self,
3803        context: &mut Self::Context<'a>,
3804        dst: Register,
3805        width: DataWidth,
3806    ) -> Result<(), VerifierErrorKind> {
3807        self.endianness::<BigEndian>("be", context, dst, width)
3808    }
3809
3810    fn le<'a>(
3811        &mut self,
3812        context: &mut Self::Context<'a>,
3813        dst: Register,
3814        width: DataWidth,
3815    ) -> Result<(), VerifierErrorKind> {
3816        self.endianness::<LittleEndian>("le", context, dst, width)
3817    }
3818
3819    fn call_external<'a>(
3820        &mut self,
3821        context: &mut Self::Context<'a>,
3822        index: u32,
3823    ) -> Result<(), VerifierErrorKind> {
3824        bpf_log!(self, context, "call 0x{:x}", index);
3825        let Some(helper) = context.calling_context.helpers.get(&index).cloned() else {
3826            return Err(VerifierErrorKind::UnknownHelper(index));
3827        };
3828        let HelperDefinition { signature, name, .. } = helper;
3829        debug_assert!(signature.args.len() <= 5);
3830        let mut next = self.next()?;
3831        for (arg_index, arg) in signature.args.iter().enumerate() {
3832            let reg = (arg_index + 1) as u8;
3833            self.reg(reg)?.match_parameter_type(context, self, name, arg, arg_index, &mut next)?
3834        }
3835        // Parameters have been validated, specify the return value on return.
3836        if signature.invalidate_array_bounds {
3837            next.array_bounds.clear();
3838        }
3839        let value =
3840            self.resolve_return_value(context, &signature.return_value, &mut next, false)?;
3841        next.set_reg(0, value)?;
3842        for i in 1..=5 {
3843            next.set_reg(i, Type::default())?;
3844        }
3845        context.states.push(next);
3846        Ok(())
3847    }
3848
3849    fn exit<'a>(&mut self, context: &mut Self::Context<'a>) -> Result<(), VerifierErrorKind> {
3850        bpf_log!(self, context, "exit");
3851        if !self.reg(0)?.is_written_scalar() {
3852            return Err(VerifierErrorKind::InvalidReturnValue);
3853        }
3854        if !self.resources.is_empty() {
3855            return Err(VerifierErrorKind::ResourceLeak);
3856        }
3857        Ok(())
3858    }
3859
3860    fn jump<'a>(
3861        &mut self,
3862        context: &mut Self::Context<'a>,
3863        offset: i16,
3864    ) -> Result<(), VerifierErrorKind> {
3865        bpf_log!(self, context, "ja {}", offset);
3866        let next = self.checked_jump_with_offset(context, offset)?;
3867        context.states.push(next);
3868        Ok(())
3869    }
3870
3871    fn jeq<'a>(
3872        &mut self,
3873        context: &mut Self::Context<'a>,
3874        dst: Register,
3875        src: Source,
3876        offset: i16,
3877    ) -> Result<(), VerifierErrorKind> {
3878        self.conditional_jump(
3879            "jeq32",
3880            context,
3881            dst,
3882            src,
3883            offset,
3884            JumpType::Eq,
3885            JumpWidth::W32,
3886            |x, y| {
3887                comp32(x, y, |x, y| {
3888                    // x == y
3889                    if x.min == x.max && x.min == y.min && x.min == y.max {
3890                        return Some(true);
3891                    }
3892                    if x.max < y.min || y.max < x.min {
3893                        return Some(false);
3894                    }
3895                    None
3896                })
3897            },
3898        )
3899    }
3900    fn jeq64<'a>(
3901        &mut self,
3902        context: &mut Self::Context<'a>,
3903        dst: Register,
3904        src: Source,
3905        offset: i16,
3906    ) -> Result<(), VerifierErrorKind> {
3907        self.conditional_jump(
3908            "jeq",
3909            context,
3910            dst,
3911            src,
3912            offset,
3913            JumpType::Eq,
3914            JumpWidth::W64,
3915            |x, y| {
3916                comp64(x, y, |x, y| {
3917                    // x == y
3918                    if x.min == x.max && x.min == y.min && x.min == y.max {
3919                        return Some(true);
3920                    }
3921                    if x.max < y.min || y.max < x.min {
3922                        return Some(false);
3923                    }
3924                    None
3925                })
3926            },
3927        )
3928    }
3929    fn jne<'a>(
3930        &mut self,
3931        context: &mut Self::Context<'a>,
3932        dst: Register,
3933        src: Source,
3934        offset: i16,
3935    ) -> Result<(), VerifierErrorKind> {
3936        self.conditional_jump(
3937            "jne32",
3938            context,
3939            dst,
3940            src,
3941            offset,
3942            JumpType::Ne,
3943            JumpWidth::W32,
3944            |x, y| {
3945                comp32(x, y, |x, y| {
3946                    // x != y
3947                    if x.min == x.max && x.min == y.min && x.min == y.max {
3948                        return Some(false);
3949                    }
3950                    if x.max < y.min || y.max < x.min {
3951                        return Some(true);
3952                    }
3953                    None
3954                })
3955            },
3956        )
3957    }
3958    fn jne64<'a>(
3959        &mut self,
3960        context: &mut Self::Context<'a>,
3961        dst: Register,
3962        src: Source,
3963        offset: i16,
3964    ) -> Result<(), VerifierErrorKind> {
3965        self.conditional_jump(
3966            "jne",
3967            context,
3968            dst,
3969            src,
3970            offset,
3971            JumpType::Ne,
3972            JumpWidth::W64,
3973            |x, y| {
3974                comp64(x, y, |x, y| {
3975                    // x != y
3976                    if x.min == x.max && x.min == y.min && x.min == y.max {
3977                        return Some(false);
3978                    }
3979                    if x.max < y.min || y.max < x.min {
3980                        return Some(true);
3981                    }
3982                    None
3983                })
3984            },
3985        )
3986    }
3987    fn jge<'a>(
3988        &mut self,
3989        context: &mut Self::Context<'a>,
3990        dst: Register,
3991        src: Source,
3992        offset: i16,
3993    ) -> Result<(), VerifierErrorKind> {
3994        self.conditional_jump(
3995            "jge32",
3996            context,
3997            dst,
3998            src,
3999            offset,
4000            JumpType::Ge,
4001            JumpWidth::W32,
4002            |x, y| {
4003                comp32(x, y, |x, y| {
4004                    // x >= y
4005                    if x.min >= y.max {
4006                        return Some(true);
4007                    }
4008                    if y.min > x.max {
4009                        return Some(false);
4010                    }
4011                    None
4012                })
4013            },
4014        )
4015    }
4016    fn jge64<'a>(
4017        &mut self,
4018        context: &mut Self::Context<'a>,
4019        dst: Register,
4020        src: Source,
4021        offset: i16,
4022    ) -> Result<(), VerifierErrorKind> {
4023        self.conditional_jump(
4024            "jge",
4025            context,
4026            dst,
4027            src,
4028            offset,
4029            JumpType::Ge,
4030            JumpWidth::W64,
4031            |x, y| {
4032                comp64(x, y, |x, y| {
4033                    // x >= y
4034                    if x.min >= y.max {
4035                        return Some(true);
4036                    }
4037                    if y.min > x.max {
4038                        return Some(false);
4039                    }
4040                    None
4041                })
4042            },
4043        )
4044    }
4045    fn jgt<'a>(
4046        &mut self,
4047        context: &mut Self::Context<'a>,
4048        dst: Register,
4049        src: Source,
4050        offset: i16,
4051    ) -> Result<(), VerifierErrorKind> {
4052        self.conditional_jump(
4053            "jgt32",
4054            context,
4055            dst,
4056            src,
4057            offset,
4058            JumpType::Gt,
4059            JumpWidth::W32,
4060            |x, y| {
4061                comp32(x, y, |x, y| {
4062                    // x > y
4063                    if x.min > y.max {
4064                        return Some(true);
4065                    }
4066                    if y.min >= x.max {
4067                        return Some(false);
4068                    }
4069                    None
4070                })
4071            },
4072        )
4073    }
4074    fn jgt64<'a>(
4075        &mut self,
4076        context: &mut Self::Context<'a>,
4077        dst: Register,
4078        src: Source,
4079        offset: i16,
4080    ) -> Result<(), VerifierErrorKind> {
4081        self.conditional_jump(
4082            "jgt",
4083            context,
4084            dst,
4085            src,
4086            offset,
4087            JumpType::Gt,
4088            JumpWidth::W64,
4089            |x, y| {
4090                comp64(x, y, |x, y| {
4091                    // x > y
4092                    if x.min > y.max {
4093                        return Some(true);
4094                    }
4095                    if y.min >= x.max {
4096                        return Some(false);
4097                    }
4098                    None
4099                })
4100            },
4101        )
4102    }
4103    fn jle<'a>(
4104        &mut self,
4105        context: &mut Self::Context<'a>,
4106        dst: Register,
4107        src: Source,
4108        offset: i16,
4109    ) -> Result<(), VerifierErrorKind> {
4110        self.conditional_jump(
4111            "jle32",
4112            context,
4113            dst,
4114            src,
4115            offset,
4116            JumpType::Le,
4117            JumpWidth::W32,
4118            |x, y| {
4119                comp32(x, y, |x, y| {
4120                    // x <= y
4121                    if x.max <= y.min {
4122                        return Some(true);
4123                    }
4124                    if y.max < x.min {
4125                        return Some(false);
4126                    }
4127                    None
4128                })
4129            },
4130        )
4131    }
4132    fn jle64<'a>(
4133        &mut self,
4134        context: &mut Self::Context<'a>,
4135        dst: Register,
4136        src: Source,
4137        offset: i16,
4138    ) -> Result<(), VerifierErrorKind> {
4139        self.conditional_jump(
4140            "jle",
4141            context,
4142            dst,
4143            src,
4144            offset,
4145            JumpType::Le,
4146            JumpWidth::W64,
4147            |x, y| {
4148                comp64(x, y, |x, y| {
4149                    // x <= y
4150                    if x.max <= y.min {
4151                        return Some(true);
4152                    }
4153                    if y.max < x.min {
4154                        return Some(false);
4155                    }
4156                    None
4157                })
4158            },
4159        )
4160    }
4161    fn jlt<'a>(
4162        &mut self,
4163        context: &mut Self::Context<'a>,
4164        dst: Register,
4165        src: Source,
4166        offset: i16,
4167    ) -> Result<(), VerifierErrorKind> {
4168        self.conditional_jump(
4169            "jlt32",
4170            context,
4171            dst,
4172            src,
4173            offset,
4174            JumpType::Lt,
4175            JumpWidth::W32,
4176            |x, y| {
4177                comp32(x, y, |x, y| {
4178                    // x < y
4179                    if x.max < y.min {
4180                        return Some(true);
4181                    }
4182                    if y.max <= x.min {
4183                        return Some(false);
4184                    }
4185                    None
4186                })
4187            },
4188        )
4189    }
4190    fn jlt64<'a>(
4191        &mut self,
4192        context: &mut Self::Context<'a>,
4193        dst: Register,
4194        src: Source,
4195        offset: i16,
4196    ) -> Result<(), VerifierErrorKind> {
4197        self.conditional_jump(
4198            "jlt",
4199            context,
4200            dst,
4201            src,
4202            offset,
4203            JumpType::Lt,
4204            JumpWidth::W64,
4205            |x, y| {
4206                comp64(x, y, |x, y| {
4207                    // x < y
4208                    if x.max < y.min {
4209                        return Some(true);
4210                    }
4211                    if y.max <= x.min {
4212                        return Some(false);
4213                    }
4214                    None
4215                })
4216            },
4217        )
4218    }
4219    fn jsge<'a>(
4220        &mut self,
4221        context: &mut Self::Context<'a>,
4222        dst: Register,
4223        src: Source,
4224        offset: i16,
4225    ) -> Result<(), VerifierErrorKind> {
4226        self.conditional_jump(
4227            "jsge32",
4228            context,
4229            dst,
4230            src,
4231            offset,
4232            JumpType::LooseComparaison,
4233            JumpWidth::W32,
4234            |x, y| scomp32(x, y, |x, y| x >= y),
4235        )
4236    }
4237    fn jsge64<'a>(
4238        &mut self,
4239        context: &mut Self::Context<'a>,
4240        dst: Register,
4241        src: Source,
4242        offset: i16,
4243    ) -> Result<(), VerifierErrorKind> {
4244        self.conditional_jump(
4245            "jsge",
4246            context,
4247            dst,
4248            src,
4249            offset,
4250            JumpType::LooseComparaison,
4251            JumpWidth::W64,
4252            |x, y| scomp64(x, y, |x, y| x >= y),
4253        )
4254    }
4255    fn jsgt<'a>(
4256        &mut self,
4257        context: &mut Self::Context<'a>,
4258        dst: Register,
4259        src: Source,
4260        offset: i16,
4261    ) -> Result<(), VerifierErrorKind> {
4262        self.conditional_jump(
4263            "jsgt32",
4264            context,
4265            dst,
4266            src,
4267            offset,
4268            JumpType::StrictComparaison,
4269            JumpWidth::W32,
4270            |x, y| scomp32(x, y, |x, y| x > y),
4271        )
4272    }
4273    fn jsgt64<'a>(
4274        &mut self,
4275        context: &mut Self::Context<'a>,
4276        dst: Register,
4277        src: Source,
4278        offset: i16,
4279    ) -> Result<(), VerifierErrorKind> {
4280        self.conditional_jump(
4281            "jsgt",
4282            context,
4283            dst,
4284            src,
4285            offset,
4286            JumpType::StrictComparaison,
4287            JumpWidth::W64,
4288            |x, y| scomp64(x, y, |x, y| x > y),
4289        )
4290    }
4291    fn jsle<'a>(
4292        &mut self,
4293        context: &mut Self::Context<'a>,
4294        dst: Register,
4295        src: Source,
4296        offset: i16,
4297    ) -> Result<(), VerifierErrorKind> {
4298        self.conditional_jump(
4299            "jsle32",
4300            context,
4301            dst,
4302            src,
4303            offset,
4304            JumpType::LooseComparaison,
4305            JumpWidth::W32,
4306            |x, y| scomp32(x, y, |x, y| x <= y),
4307        )
4308    }
4309    fn jsle64<'a>(
4310        &mut self,
4311        context: &mut Self::Context<'a>,
4312        dst: Register,
4313        src: Source,
4314        offset: i16,
4315    ) -> Result<(), VerifierErrorKind> {
4316        self.conditional_jump(
4317            "jsle",
4318            context,
4319            dst,
4320            src,
4321            offset,
4322            JumpType::LooseComparaison,
4323            JumpWidth::W64,
4324            |x, y| scomp64(x, y, |x, y| x <= y),
4325        )
4326    }
4327    fn jslt<'a>(
4328        &mut self,
4329        context: &mut Self::Context<'a>,
4330        dst: Register,
4331        src: Source,
4332        offset: i16,
4333    ) -> Result<(), VerifierErrorKind> {
4334        self.conditional_jump(
4335            "jslt32",
4336            context,
4337            dst,
4338            src,
4339            offset,
4340            JumpType::StrictComparaison,
4341            JumpWidth::W32,
4342            |x, y| scomp32(x, y, |x, y| x < y),
4343        )
4344    }
4345    fn jslt64<'a>(
4346        &mut self,
4347        context: &mut Self::Context<'a>,
4348        dst: Register,
4349        src: Source,
4350        offset: i16,
4351    ) -> Result<(), VerifierErrorKind> {
4352        self.conditional_jump(
4353            "jslt",
4354            context,
4355            dst,
4356            src,
4357            offset,
4358            JumpType::StrictComparaison,
4359            JumpWidth::W64,
4360            |x, y| scomp64(x, y, |x, y| x < y),
4361        )
4362    }
4363    fn jset<'a>(
4364        &mut self,
4365        context: &mut Self::Context<'a>,
4366        dst: Register,
4367        src: Source,
4368        offset: i16,
4369    ) -> Result<(), VerifierErrorKind> {
4370        self.conditional_jump(
4371            "jset32",
4372            context,
4373            dst,
4374            src,
4375            offset,
4376            JumpType::Unknown,
4377            JumpWidth::W32,
4378            |x, y| {
4379                comp32(x, y, |x, y| {
4380                    // x & y != 0
4381                    if x.min != x.max || y.min != y.max {
4382                        return None;
4383                    }
4384                    Some(x.min & y.min != 0)
4385                })
4386            },
4387        )
4388    }
4389    fn jset64<'a>(
4390        &mut self,
4391        context: &mut Self::Context<'a>,
4392        dst: Register,
4393        src: Source,
4394        offset: i16,
4395    ) -> Result<(), VerifierErrorKind> {
4396        self.conditional_jump(
4397            "jset",
4398            context,
4399            dst,
4400            src,
4401            offset,
4402            JumpType::Unknown,
4403            JumpWidth::W64,
4404            |x, y| {
4405                comp64(x, y, |x, y| {
4406                    // x & y != 0
4407                    if x.min != x.max || y.min != y.max {
4408                        return None;
4409                    }
4410                    Some(x.min & y.min != 0)
4411                })
4412            },
4413        )
4414    }
4415
4416    fn atomic_add<'a>(
4417        &mut self,
4418        context: &mut Self::Context<'a>,
4419        fetch: bool,
4420        dst: Register,
4421        offset: i16,
4422        src: Register,
4423    ) -> Result<(), VerifierErrorKind> {
4424        self.atomic_operation(
4425            "add32",
4426            context,
4427            DataWidth::U32,
4428            fetch,
4429            dst,
4430            offset,
4431            src,
4432            AluType::Add,
4433            |x, y| alu32(x, y, |x, y| x + y),
4434        )
4435    }
4436
4437    fn atomic_add64<'a>(
4438        &mut self,
4439        context: &mut Self::Context<'a>,
4440        fetch: bool,
4441        dst: Register,
4442        offset: i16,
4443        src: Register,
4444    ) -> Result<(), VerifierErrorKind> {
4445        self.atomic_operation(
4446            "add",
4447            context,
4448            DataWidth::U64,
4449            fetch,
4450            dst,
4451            offset,
4452            src,
4453            AluType::Add,
4454            |x, y| x + y,
4455        )
4456    }
4457
4458    fn atomic_and<'a>(
4459        &mut self,
4460        context: &mut Self::Context<'a>,
4461        fetch: bool,
4462        dst: Register,
4463        offset: i16,
4464        src: Register,
4465    ) -> Result<(), VerifierErrorKind> {
4466        self.atomic_operation(
4467            "and32",
4468            context,
4469            DataWidth::U32,
4470            fetch,
4471            dst,
4472            offset,
4473            src,
4474            AluType::Plain,
4475            |x, y| alu32(x, y, |x, y| x & y),
4476        )
4477    }
4478
4479    fn atomic_and64<'a>(
4480        &mut self,
4481        context: &mut Self::Context<'a>,
4482        fetch: bool,
4483        dst: Register,
4484        offset: i16,
4485        src: Register,
4486    ) -> Result<(), VerifierErrorKind> {
4487        self.atomic_operation(
4488            "and",
4489            context,
4490            DataWidth::U64,
4491            fetch,
4492            dst,
4493            offset,
4494            src,
4495            AluType::Plain,
4496            |x, y| x & y,
4497        )
4498    }
4499
4500    fn atomic_or<'a>(
4501        &mut self,
4502        context: &mut Self::Context<'a>,
4503        fetch: bool,
4504        dst: Register,
4505        offset: i16,
4506        src: Register,
4507    ) -> Result<(), VerifierErrorKind> {
4508        self.atomic_operation(
4509            "or32",
4510            context,
4511            DataWidth::U32,
4512            fetch,
4513            dst,
4514            offset,
4515            src,
4516            AluType::Plain,
4517            |x, y| alu32(x, y, |x, y| x | y),
4518        )
4519    }
4520
4521    fn atomic_or64<'a>(
4522        &mut self,
4523        context: &mut Self::Context<'a>,
4524        fetch: bool,
4525        dst: Register,
4526        offset: i16,
4527        src: Register,
4528    ) -> Result<(), VerifierErrorKind> {
4529        self.atomic_operation(
4530            "or",
4531            context,
4532            DataWidth::U64,
4533            fetch,
4534            dst,
4535            offset,
4536            src,
4537            AluType::Plain,
4538            |x, y| x | y,
4539        )
4540    }
4541
4542    fn atomic_xor<'a>(
4543        &mut self,
4544        context: &mut Self::Context<'a>,
4545        fetch: bool,
4546        dst: Register,
4547        offset: i16,
4548        src: Register,
4549    ) -> Result<(), VerifierErrorKind> {
4550        self.atomic_operation(
4551            "xor32",
4552            context,
4553            DataWidth::U32,
4554            fetch,
4555            dst,
4556            offset,
4557            src,
4558            AluType::Plain,
4559            |x, y| alu32(x, y, |x, y| x ^ y),
4560        )
4561    }
4562
4563    fn atomic_xor64<'a>(
4564        &mut self,
4565        context: &mut Self::Context<'a>,
4566        fetch: bool,
4567        dst: Register,
4568        offset: i16,
4569        src: Register,
4570    ) -> Result<(), VerifierErrorKind> {
4571        self.atomic_operation(
4572            "xor",
4573            context,
4574            DataWidth::U64,
4575            fetch,
4576            dst,
4577            offset,
4578            src,
4579            AluType::Plain,
4580            |x, y| x ^ y,
4581        )
4582    }
4583
4584    fn atomic_xchg<'a>(
4585        &mut self,
4586        context: &mut Self::Context<'a>,
4587        fetch: bool,
4588        dst: Register,
4589        offset: i16,
4590        src: Register,
4591    ) -> Result<(), VerifierErrorKind> {
4592        self.atomic_operation(
4593            "xchg32",
4594            context,
4595            DataWidth::U32,
4596            fetch,
4597            dst,
4598            offset,
4599            src,
4600            AluType::Plain,
4601            |_, x| x,
4602        )
4603    }
4604
4605    fn atomic_xchg64<'a>(
4606        &mut self,
4607        context: &mut Self::Context<'a>,
4608        fetch: bool,
4609        dst: Register,
4610        offset: i16,
4611        src: Register,
4612    ) -> Result<(), VerifierErrorKind> {
4613        self.raw_atomic_operation(
4614            "xchg",
4615            context,
4616            DataWidth::U64,
4617            fetch,
4618            dst,
4619            offset,
4620            src,
4621            |_, _, x| Ok(x),
4622        )
4623    }
4624
4625    fn atomic_cmpxchg<'a>(
4626        &mut self,
4627        context: &mut Self::Context<'a>,
4628        dst: Register,
4629        offset: i16,
4630        src: Register,
4631    ) -> Result<(), VerifierErrorKind> {
4632        self.raw_atomic_cmpxchg("cmpxchg32", context, dst, offset, src, JumpWidth::W32, |x, y| {
4633            comp32(x, y, |x, y| {
4634                // x == y
4635                if x.min == x.max && x.min == y.min && x.min == y.max {
4636                    return Some(true);
4637                }
4638                if x.max < y.min || y.max < x.min {
4639                    return Some(false);
4640                }
4641                None
4642            })
4643        })
4644    }
4645
4646    fn atomic_cmpxchg64<'a>(
4647        &mut self,
4648        context: &mut Self::Context<'a>,
4649        dst: Register,
4650        offset: i16,
4651        src: Register,
4652    ) -> Result<(), VerifierErrorKind> {
4653        self.raw_atomic_cmpxchg("cmpxchg", context, dst, offset, src, JumpWidth::W64, |x, y| {
4654            comp64(x, y, |x, y| {
4655                // x == y
4656                if x.min == x.max && x.min == y.min && x.min == y.max {
4657                    return Some(true);
4658                }
4659                if x.max < y.min || y.max < x.min {
4660                    return Some(false);
4661                }
4662                None
4663            })
4664        })
4665    }
4666
4667    fn load<'a>(
4668        &mut self,
4669        context: &mut Self::Context<'a>,
4670        dst: Register,
4671        offset: i16,
4672        src: Register,
4673        width: DataWidth,
4674    ) -> Result<(), VerifierErrorKind> {
4675        bpf_log!(
4676            self,
4677            context,
4678            "ldx{} {}, [{}{}]",
4679            width.str(),
4680            display_register(dst),
4681            display_register(src),
4682            print_offset(offset)
4683        );
4684        let addr = self.reg(src)?;
4685        let loaded_type = self.load_memory(context, &addr, Field::new(offset, width))?;
4686        let mut next = self.next()?;
4687        next.set_reg(dst, loaded_type)?;
4688        context.states.push(next);
4689        Ok(())
4690    }
4691
4692    fn load64<'a>(
4693        &mut self,
4694        context: &mut Self::Context<'a>,
4695        dst: Register,
4696        src: u8,
4697        lower: u32,
4698    ) -> Result<(), VerifierErrorKind> {
4699        // The pre-scan has already guaranteed that `pc + 1` is valid and structurally well-formed.
4700        let next_instruction = &context.code[self.pc + 1];
4701
4702        let value = match src {
4703            0 => {
4704                let value = (lower as u64) | (((next_instruction.imm() as u32) as u64) << 32);
4705                bpf_log!(self, context, "lddw {}, 0x{:x}", display_register(dst), value);
4706                Type::from(value)
4707            }
4708            BPF_PSEUDO_MAP_IDX => {
4709                let map_index = lower;
4710                bpf_log!(
4711                    self,
4712                    context,
4713                    "lddw {}, map_by_index({:x})",
4714                    display_register(dst),
4715                    map_index
4716                );
4717                context
4718                    .calling_context
4719                    .maps
4720                    .get(usize::try_from(map_index).unwrap())
4721                    .map(|schema| Type::ConstPtrToMap { id: map_index.into(), schema: *schema })
4722                    .ok_or(VerifierErrorKind::InvalidMapIndex(map_index))?
4723            }
4724            BPF_PSEUDO_MAP_IDX_VALUE => {
4725                let map_index = lower;
4726                let offset = next_instruction.imm();
4727                bpf_log!(
4728                    self,
4729                    context,
4730                    "lddw {}, map_value_by_index({:x})+{offset}",
4731                    display_register(dst),
4732                    map_index
4733                );
4734                let id = context.next_id();
4735                let map_schema = context
4736                    .calling_context
4737                    .maps
4738                    .get(usize::try_from(map_index).unwrap())
4739                    .ok_or(VerifierErrorKind::InvalidMapIndex(map_index))?;
4740
4741                if map_schema.map_type != bpf_map_type_BPF_MAP_TYPE_ARRAY {
4742                    return Err(VerifierErrorKind::LddwMapValueNotArray(
4743                        map_index,
4744                        map_schema.map_type,
4745                    ));
4746                }
4747                if map_schema.max_entries == 0 {
4748                    return Err(VerifierErrorKind::EmptyArrayMap);
4749                }
4750
4751                let origin = if map_schema.flags.contains(MapFlags::ProgReadOnly) {
4752                    PointerOrigin::MapReadOnly
4753                } else {
4754                    PointerOrigin::MapWritable
4755                };
4756                Type::PtrToMemory {
4757                    id: MemoryId::from(id),
4758                    offset: offset.into(),
4759                    buffer_size: map_schema.value_size.into(),
4760                    origin,
4761                }
4762            }
4763            _ => {
4764                return Err(VerifierErrorKind::UnsupportedLddwSource(src));
4765            }
4766        };
4767
4768        let mut next = self.jump_with_offset(1)?;
4769        next.set_reg(dst, value.into())?;
4770
4771        context.states.push(next);
4772        Ok(())
4773    }
4774
4775    fn load_from_packet<'a>(
4776        &mut self,
4777        context: &mut Self::Context<'a>,
4778        dst: Register,
4779        src: Register,
4780        offset: i32,
4781        register_offset: Option<Register>,
4782        width: DataWidth,
4783    ) -> Result<(), VerifierErrorKind> {
4784        if let Some(reg) = register_offset {
4785            bpf_log!(
4786                self,
4787                context,
4788                "ldp{} {}{}",
4789                width.str(),
4790                display_register(reg),
4791                print_offset(offset)
4792            );
4793        } else {
4794            bpf_log!(self, context, "ldp{} {}", width.str(), print_offset(offset));
4795        }
4796
4797        // Verify that `src` refers to a packet.
4798        let src_type = self.reg(src)?;
4799        let src_is_packet = match &context.calling_context.packet_type {
4800            Some(packet_type) => src_type == *packet_type,
4801            None => false,
4802        };
4803        if !src_is_packet {
4804            return Err(VerifierErrorKind::RegisterNotPacket(src));
4805        }
4806
4807        if let Some(reg) = register_offset {
4808            let reg = self.reg(reg)?;
4809            if !reg.is_written_scalar() {
4810                return Err(VerifierErrorKind::UninitializedPacketOffset);
4811            }
4812        }
4813        // Handle the case where the load fails.
4814        if !self.reg(0)?.is_written_scalar() {
4815            return Err(VerifierErrorKind::InvalidReturnValue);
4816        }
4817        if !self.resources.is_empty() {
4818            return Err(VerifierErrorKind::ResourceLeak);
4819        }
4820        // `self` is not terminated here: it becomes the parent of `next`, and terminates with the
4821        // dependencies of both paths once `next` terminates. Terminating `self` here would record
4822        // it as a terminating context with the dependencies of the exit path only.
4823
4824        // Handle the case where the load succeed.
4825        let mut next = self.next()?;
4826        next.set_reg(dst, Type::UNKNOWN_SCALAR)?;
4827        for i in 1..=5 {
4828            next.set_reg(i, Type::default())?;
4829        }
4830        context.states.push(next);
4831        Ok(())
4832    }
4833
4834    fn store<'a>(
4835        &mut self,
4836        context: &mut Self::Context<'a>,
4837        dst: Register,
4838        offset: i16,
4839        src: Source,
4840        width: DataWidth,
4841    ) -> Result<(), VerifierErrorKind> {
4842        let value = match src {
4843            Source::Reg(r) => {
4844                bpf_log!(
4845                    self,
4846                    context,
4847                    "stx{} [{}{}], {}",
4848                    width.str(),
4849                    display_register(dst),
4850                    print_offset(offset),
4851                    display_register(r),
4852                );
4853                self.reg(r)?
4854            }
4855            Source::Value(v) => {
4856                bpf_log!(
4857                    self,
4858                    context,
4859                    "st{} [{}{}], 0x{:x}",
4860                    width.str(),
4861                    display_register(dst),
4862                    print_offset(offset),
4863                    v,
4864                );
4865                Type::from(v & Type::mask(width))
4866            }
4867        };
4868        let mut next = self.next()?;
4869        let addr = self.reg(dst)?;
4870        next.store_memory(context, self.pc, &addr, Field::new(offset, width), value)?;
4871        context.states.push(next);
4872        Ok(())
4873    }
4874}
4875
4876fn alu32(
4877    x: ScalarValueData,
4878    y: ScalarValueData,
4879    op: impl FnOnce(U32ScalarValueData, U32ScalarValueData) -> U32ScalarValueData,
4880) -> ScalarValueData {
4881    op(U32ScalarValueData::from(x), U32ScalarValueData::from(y)).into()
4882}
4883
4884fn comp64(
4885    x: ScalarValueData,
4886    y: ScalarValueData,
4887    op: impl FnOnce(U64Range, U64Range) -> Option<bool>,
4888) -> Result<Option<bool>, ()> {
4889    if !x.is_fully_initialized() || !y.is_fully_initialized() {
4890        return Err(());
4891    }
4892    Ok(op(x.urange, y.urange))
4893}
4894
4895fn comp32(
4896    x: ScalarValueData,
4897    y: ScalarValueData,
4898    op: impl FnOnce(U32Range, U32Range) -> Option<bool>,
4899) -> Result<Option<bool>, ()> {
4900    let x = U32ScalarValueData::from(x);
4901    let y = U32ScalarValueData::from(y);
4902    if !x.is_fully_initialized() || !y.is_fully_initialized() {
4903        return Err(());
4904    }
4905    Ok(op(x.urange, y.urange))
4906}
4907
4908fn scomp64(
4909    x: ScalarValueData,
4910    y: ScalarValueData,
4911    op: impl FnOnce(i64, i64) -> bool,
4912) -> Result<Option<bool>, ()> {
4913    if !x.is_fully_initialized() || !y.is_fully_initialized() {
4914        return Err(());
4915    }
4916    if !x.is_known() || !y.is_known() {
4917        return Ok(None);
4918    }
4919    Ok(Some(op(x.value as i64, y.value as i64)))
4920}
4921
4922fn scomp32(
4923    x: ScalarValueData,
4924    y: ScalarValueData,
4925    op: impl FnOnce(i32, i32) -> bool,
4926) -> Result<Option<bool>, ()> {
4927    let x = U32ScalarValueData::from(x);
4928    let y = U32ScalarValueData::from(y);
4929    if !x.is_fully_initialized() || !y.is_fully_initialized() {
4930        return Err(());
4931    }
4932    if !x.is_known() || !y.is_known() {
4933        return Ok(None);
4934    }
4935    Ok(Some(op(x.value as i32, y.value as i32)))
4936}
4937
4938#[derive(Clone, Copy)]
4939struct DisplayOffset {
4940    offset: i32,
4941    show_zero: bool,
4942}
4943
4944impl std::fmt::Display for DisplayOffset {
4945    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4946        if self.offset > 0 {
4947            write!(f, "+{}", self.offset)
4948        } else if self.offset < 0 || self.show_zero {
4949            write!(f, "{}", self.offset)
4950        } else {
4951            Ok(())
4952        }
4953    }
4954}
4955
4956fn print_offset<T: Into<i32>>(offset: T) -> DisplayOffset {
4957    DisplayOffset { offset: offset.into(), show_zero: false }
4958}
4959
4960fn print_jump_offset<T: Into<i32>>(offset: T) -> DisplayOffset {
4961    DisplayOffset { offset: offset.into(), show_zero: true }
4962}
4963
4964fn run_on_stack_offset<F>(v: StackOffset, f: F) -> StackOffset
4965where
4966    F: FnOnce(ScalarValueData) -> ScalarValueData,
4967{
4968    StackOffset(f(v.reg()))
4969}
4970
4971fn error_and_log<T>(
4972    logger: &mut dyn VerifierLogger,
4973    err: VerifierError,
4974) -> Result<T, VerifierError> {
4975    logger.log(err.to_string().as_bytes());
4976    Err(err)
4977}
4978
4979fn associate_orderings(o1: Ordering, o2: Ordering) -> Option<Ordering> {
4980    match (o1, o2) {
4981        (o1, o2) if o1 == o2 => Some(o1),
4982        (o, Ordering::Equal) | (Ordering::Equal, o) => Some(o),
4983        _ => None,
4984    }
4985}
4986
4987#[cfg(test)]
4988mod tests {
4989    use super::*;
4990    use crate::{BPF_ADD, BPF_ALU64, BPF_K, BPF_LDX, BPF_MEM, BPF_MOV, BPF_ST};
4991    use test_util::{assert_geq, assert_leq};
4992
4993    #[test]
4994    fn test_type_ordering() {
4995        let t0 = Type::from(0);
4996        let t1 = Type::from(1);
4997        let random = Type::AliasParameter { parameter_index: 8 };
4998        let unknown_written = Type::UNKNOWN_SCALAR;
4999        let unwritten = Type::default();
5000
5001        assert_eq!(t0.partial_cmp(&t0), Some(Ordering::Equal));
5002        assert_eq!(t0.partial_cmp(&t1), None);
5003        assert_eq!(t0.partial_cmp(&random), None);
5004        assert_eq!(t0.partial_cmp(&unknown_written), Some(Ordering::Less));
5005        assert_eq!(t0.partial_cmp(&unwritten), Some(Ordering::Less));
5006
5007        assert_eq!(t1.partial_cmp(&t0), None);
5008        assert_eq!(t1.partial_cmp(&t1), Some(Ordering::Equal));
5009        assert_eq!(t1.partial_cmp(&random), None);
5010        assert_eq!(t1.partial_cmp(&unknown_written), Some(Ordering::Less));
5011        assert_eq!(t1.partial_cmp(&unwritten), Some(Ordering::Less));
5012
5013        assert_eq!(random.partial_cmp(&t0), None);
5014        assert_eq!(random.partial_cmp(&t1), None);
5015        assert_eq!(random.partial_cmp(&random), Some(Ordering::Equal));
5016        assert_eq!(random.partial_cmp(&unknown_written), None);
5017        assert_eq!(random.partial_cmp(&unwritten), Some(Ordering::Less));
5018
5019        assert_eq!(unknown_written.partial_cmp(&t0), Some(Ordering::Greater));
5020        assert_eq!(unknown_written.partial_cmp(&t1), Some(Ordering::Greater));
5021        assert_eq!(unknown_written.partial_cmp(&random), None);
5022        assert_eq!(unknown_written.partial_cmp(&unknown_written), Some(Ordering::Equal));
5023        assert_eq!(unknown_written.partial_cmp(&unwritten), Some(Ordering::Less));
5024
5025        assert_eq!(unwritten.partial_cmp(&t0), Some(Ordering::Greater));
5026        assert_eq!(unwritten.partial_cmp(&t1), Some(Ordering::Greater));
5027        assert_eq!(unwritten.partial_cmp(&random), Some(Ordering::Greater));
5028        assert_eq!(unwritten.partial_cmp(&unknown_written), Some(Ordering::Greater));
5029        assert_eq!(unwritten.partial_cmp(&unwritten), Some(Ordering::Equal));
5030    }
5031
5032    #[test]
5033    fn test_stack_ordering() {
5034        let mut s1 = Stack::default();
5035        let mut s2 = Stack::default();
5036
5037        assert_eq!(s1.partial_cmp(&s2), Some(Ordering::Equal));
5038        s1.set(0, 0.into());
5039        assert_eq!(s1.partial_cmp(&s1.clone()), Some(Ordering::Equal));
5040        assert_eq!(s1.partial_cmp(&s2), Some(Ordering::Less));
5041        assert_eq!(s2.partial_cmp(&s1), Some(Ordering::Greater));
5042        s2.set(0, Type::UNKNOWN_SCALAR);
5043        assert_eq!(s1.partial_cmp(&s2), Some(Ordering::Less));
5044        assert_eq!(s2.partial_cmp(&s1), Some(Ordering::Greater));
5045        s1.set(1, 1.into());
5046        assert_eq!(s1.partial_cmp(&s2), Some(Ordering::Less));
5047        assert_eq!(s2.partial_cmp(&s1), Some(Ordering::Greater));
5048        s2.set(2, 1.into());
5049        assert_eq!(s1.partial_cmp(&s2), None);
5050        assert_eq!(s2.partial_cmp(&s1), None);
5051    }
5052
5053    #[test]
5054    fn test_stack_set_and_retain() {
5055        let mut s = Stack::default();
5056        for index in [5, 63, 0, 9] {
5057            s.set(index, (index as u64).into());
5058        }
5059        let mut copy = s.clone();
5060        s.set(5, 5.into());
5061        assert!(Arc::ptr_eq(&s.data, &copy.data));
5062        s.set_null(&MemoryId::new(), true);
5063        assert!(Arc::ptr_eq(&s.data, &copy.data));
5064        let mut copy_to_clear = copy.clone();
5065        assert_eq!(Arc::strong_count(&copy.data), 3);
5066        copy_to_clear.retain(0);
5067        assert_eq!(copy_to_clear.mask, 0);
5068        assert!(copy_to_clear.data.is_empty());
5069        assert_eq!(Arc::strong_count(&copy.data), 2);
5070        s.set(9, Type::UNINITIALIZED);
5071        s.set(10, Type::UNINITIALIZED);
5072        s.set(63, 1.into());
5073        assert_eq!(copy.get(9), &Type::from(9));
5074        assert_eq!(copy.get(63), &Type::from(63));
5075        let id = MemoryId::new();
5076        copy.set(5, Type::NullOr { id: id.clone(), inner: Type::from(42).into() });
5077        copy.set_null(&id, false);
5078        assert_eq!(copy.get(5), &Type::from(42));
5079        s.retain(!(1 << 5));
5080        assert_eq!(s.mask, 1 | (1 << 63));
5081        assert_eq!(*s.data, vec![Type::from(0), Type::from(1)]);
5082        for index in [5, 9, 10] {
5083            assert_eq!(s.get(index), &Type::UNINITIALIZED);
5084        }
5085        s.retain(0);
5086        assert_eq!(s.mask, 0);
5087        assert!(s.data.is_empty());
5088        assert_eq!(copy.mask, 1 | (1 << 5) | (1 << 9) | (1 << 63));
5089    }
5090
5091    #[test]
5092    fn test_context_ordering() {
5093        let mut c1 = ComputationContext::default();
5094        let mut c2 = ComputationContext::default();
5095
5096        assert_eq!(c1.partial_cmp(&c2), Some(Ordering::Equal));
5097
5098        c1.array_bounds.insert(1.into(), 5);
5099        assert_eq!(c1.partial_cmp(&c2), Some(Ordering::Less));
5100        assert_eq!(c2.partial_cmp(&c1), Some(Ordering::Greater));
5101
5102        c2.array_bounds.insert(1.into(), 7);
5103        assert_eq!(c1.partial_cmp(&c2), Some(Ordering::Greater));
5104        assert_eq!(c2.partial_cmp(&c1), Some(Ordering::Less));
5105
5106        c1.array_bounds.insert(2.into(), 9);
5107        assert_eq!(c1.partial_cmp(&c2), None);
5108        assert_eq!(c2.partial_cmp(&c1), None);
5109
5110        c2.array_bounds.insert(2.into(), 9);
5111        assert_eq!(c1.partial_cmp(&c2), Some(Ordering::Greater));
5112        assert_eq!(c2.partial_cmp(&c1), Some(Ordering::Less));
5113
5114        c2.array_bounds.insert(3.into(), 12);
5115        assert_eq!(c1.partial_cmp(&c2), Some(Ordering::Greater));
5116        assert_eq!(c2.partial_cmp(&c1), Some(Ordering::Less));
5117
5118        c1.pc = 8;
5119        assert_eq!(c1.partial_cmp(&c2), None);
5120        assert_eq!(c2.partial_cmp(&c1), None);
5121    }
5122
5123    /// A sample of types covering the cases distinguished by `Type::partial_cmp`.
5124    fn sample_types() -> Vec<Type> {
5125        let id = MemoryId::from(1);
5126        vec![
5127            Type::UNINITIALIZED,
5128            Type::UNKNOWN_SCALAR,
5129            Type::from(0),
5130            Type::from(1),
5131            Type::from(u64::MAX),
5132            Type::ScalarValue(ScalarValueData::new(0, 0xff, 0, U64Range::new(0, 0xff))),
5133            Type::ScalarValue(ScalarValueData::new(0, 0xff, 0, U64Range::new(0, 1))),
5134            Type::ScalarValue(ScalarValueData::new(0, 0xff, 0xff, U64Range::new(0, 0xff))),
5135            Type::ScalarValue(ScalarValueData::new(0x100, 0xff, 0, U64Range::new(0x100, 0x1ff))),
5136            Type::PtrToMemory {
5137                id: id.clone(),
5138                offset: 0.into(),
5139                buffer_size: 8,
5140                origin: PointerOrigin::MapWritable,
5141            },
5142            Type::PtrToArray { id, offset: 1.into() },
5143        ]
5144    }
5145
5146    #[test]
5147    fn test_known_scalars_is_necessary_for_greater_or_equal() {
5148        let types = sample_types();
5149        // Check every pair of types in r0, and in r1 with a known scalar in r0.
5150        for t1 in types.iter() {
5151            for t2 in types.iter() {
5152                for r0 in [None, Some(Type::from(3))] {
5153                    let mut c1 = ComputationContext::default();
5154                    let mut c2 = ComputationContext::default();
5155                    let index = if r0.is_some() { 1 } else { 0 };
5156                    if let Some(r0) = r0 {
5157                        c1.set_reg(0, r0.clone()).unwrap();
5158                        c2.set_reg(0, r0).unwrap();
5159                    }
5160                    c1.set_reg(index, t1.clone()).unwrap();
5161                    c2.set_reg(index, t2.clone()).unwrap();
5162                    let k1 = KnownScalars::new(&c1);
5163                    let k2 = KnownScalars::new(&c2);
5164                    if matches!(c1.partial_cmp(&c2), Some(Ordering::Greater | Ordering::Equal)) {
5165                        assert!(k1 >= k2, "{t1:?} >= {t2:?}");
5166                    }
5167                    if matches!(c1.partial_cmp(&c2), Some(Ordering::Less | Ordering::Equal)) {
5168                        assert!(k1 <= k2, "{t1:?} <= {t2:?}");
5169                    }
5170                }
5171            }
5172        }
5173    }
5174
5175    #[test]
5176    fn test_known_scalars_filters_distinct_constants() {
5177        let mut c1 = ComputationContext::default();
5178        let mut c2 = ComputationContext::default();
5179        c1.set_reg(7, Type::from(1)).unwrap();
5180        c2.set_reg(7, Type::from(2)).unwrap();
5181        let k1 = KnownScalars::new(&c1);
5182        let k2 = KnownScalars::new(&c2);
5183        assert_eq!(k1.partial_cmp(&k2), None);
5184        assert_eq!(k2.partial_cmp(&k1), None);
5185
5186        // An unknown or uninitialized register may be greater than a known one, but not less.
5187        c2.set_reg(7, Type::UNKNOWN_SCALAR).unwrap();
5188        let k2 = KnownScalars::new(&c2);
5189        assert!(k1 < k2);
5190        assert!(k1 < KnownScalars::default());
5191    }
5192
5193    #[test]
5194    fn test_terminating_contexts_matches_unfiltered_search() {
5195        // Insert contexts in a fixed pseudo random order and check that the filtered search and
5196        // insertion behave exactly like a plain linear search over the full comparison.
5197        let types = sample_types();
5198        let mut contexts = TerminatingContexts::default();
5199        let mut reference: Vec<ComputationContext> = vec![];
5200        let mut seed = 17usize;
5201        for _ in 0..200 {
5202            let mut context = ComputationContext::default();
5203            for reg in 0..3 {
5204                seed = seed.wrapping_mul(1103515245).wrapping_add(12345);
5205                context.set_reg(reg, types[(seed >> 16) % types.len()].clone()).unwrap();
5206            }
5207
5208            let expected = reference.iter().rposition(|c| *c >= context);
5209            let found = contexts.find_greater_or_equal(&context).map(|found| {
5210                contexts.contexts.iter().position(|c| std::ptr::eq(c, found)).unwrap()
5211            });
5212            assert_eq!(found, expected);
5213
5214            let dominated = reference.iter().any(|c| *c >= context);
5215            reference.retain(|c| c.partial_cmp(&context) != Some(Ordering::Less));
5216            if !dominated {
5217                reference.push(context.clone());
5218            }
5219            contexts.insert(context, DataDependencies::default());
5220            assert_eq!(contexts.contexts.len(), reference.len());
5221            for (c, r) in contexts.contexts.iter().zip(reference.iter()) {
5222                assert_eq!(&c.computation_context, r);
5223            }
5224        }
5225    }
5226
5227    #[test]
5228    fn test_stack_access() {
5229        let mut s = Stack::default();
5230
5231        // Store data in the range [8, 26) and verify that `read_data_ptr()` fails for any
5232        // reads outside of that range.
5233        assert!(s.store(StackOffset(8.into()), Type::UNKNOWN_SCALAR, DataWidth::U64).is_ok());
5234        assert!(s.store(StackOffset(16.into()), Type::UNKNOWN_SCALAR, DataWidth::U64).is_ok());
5235        assert!(s.store(StackOffset(24.into()), Type::UNKNOWN_SCALAR, DataWidth::U16).is_ok());
5236
5237        for offset in 0..32 {
5238            for end in (offset + 1)..32 {
5239                assert_eq!(
5240                    s.read_data_ptr(StackOffset(offset.into()), (end - offset) as u64).is_ok(),
5241                    offset >= 8 && end <= 26
5242                );
5243            }
5244        }
5245
5246        // Verify that overflows are handled properly.
5247        assert!(s.read_data_ptr(StackOffset(12.into()), u64::MAX - 2).is_err());
5248    }
5249
5250    #[test]
5251    fn test_compute_range_for_bytes_swap() {
5252        // Build a list of interesting values. Interesting values are all possible combination of
5253        // bytes being either 0, max value, or an intermediary value.
5254        let mut values = BTreeSet::<u64>::default();
5255        for v1 in &[0x00, 0x1, u64::MAX] {
5256            for v2 in &[0x00, 0x1, u64::MAX] {
5257                for v3 in &[0x00, 0x1, u64::MAX] {
5258                    values.insert(U64Range::assemble_slices((*v1, *v2, *v3), 1, 1));
5259                }
5260            }
5261        }
5262        // Replace the second byte of old by the first byte of new and return the result.
5263        let store = |old: u64, new: u64| (old & !0xff00) | ((new & 0xff) << 8);
5264
5265        for old in &values {
5266            for new in &values {
5267                let s = store(*old, *new);
5268                for min_old in values.iter().filter(|v| *v <= old) {
5269                    for min_new in values.iter().filter(|v| *v <= new) {
5270                        for max_old in values.iter().filter(|v| *v >= old) {
5271                            for max_new in values.iter().filter(|v| *v >= new) {
5272                                let range = U64Range::compute_range_for_bytes_swap(
5273                                    U64Range::new(*min_old, *max_old),
5274                                    U64Range::new(*min_new, *max_new),
5275                                    1,
5276                                    0,
5277                                    1,
5278                                );
5279                                assert_leq!(range.min, s);
5280                                assert_geq!(range.max, s);
5281                            }
5282                        }
5283                    }
5284                }
5285            }
5286        }
5287    }
5288
5289    #[test]
5290    fn test_type_constraint_ge_gt() {
5291        let mut context = ComputationContext::default();
5292        let (new_lhs, new_rhs) = Type::constraint(
5293            &mut context,
5294            JumpType::Ge,
5295            JumpWidth::W64,
5296            Type::UNKNOWN_SCALAR,
5297            Type::from(10),
5298        )
5299        .unwrap();
5300
5301        if let Type::ScalarValue(data1) = new_lhs {
5302            assert_eq!(data1.min(), 10);
5303            assert_eq!(data1.max(), u64::MAX);
5304        } else {
5305            panic!("Expected ScalarValue");
5306        }
5307
5308        if let Type::ScalarValue(data2) = new_rhs {
5309            assert_eq!(data2.min(), 10);
5310            assert_eq!(data2.max(), 10);
5311        } else {
5312            panic!("Expected ScalarValue");
5313        }
5314
5315        let (new_lhs, _) = Type::constraint(
5316            &mut context,
5317            JumpType::Gt,
5318            JumpWidth::W64,
5319            Type::UNKNOWN_SCALAR,
5320            Type::from(10),
5321        )
5322        .unwrap();
5323
5324        if let Type::ScalarValue(data1) = new_lhs {
5325            assert_eq!(data1.min(), 11);
5326            assert_eq!(data1.max(), u64::MAX);
5327        } else {
5328            panic!("Expected ScalarValue");
5329        }
5330    }
5331
5332    #[test]
5333    fn test_type_constraint_lt_le() {
5334        let mut context = ComputationContext::default();
5335        let (new_lhs, _) = Type::constraint(
5336            &mut context,
5337            JumpType::Lt,
5338            JumpWidth::W64,
5339            Type::UNKNOWN_SCALAR,
5340            Type::from(10),
5341        )
5342        .unwrap();
5343
5344        if let Type::ScalarValue(data1) = new_lhs {
5345            assert_eq!(data1.min(), 0);
5346            assert_eq!(data1.max(), 9);
5347        } else {
5348            panic!("Expected ScalarValue");
5349        }
5350
5351        let (new_lhs, _) = Type::constraint(
5352            &mut context,
5353            JumpType::Le,
5354            JumpWidth::W64,
5355            Type::UNKNOWN_SCALAR,
5356            Type::from(10),
5357        )
5358        .unwrap();
5359
5360        if let Type::ScalarValue(data1) = new_lhs {
5361            assert_eq!(data1.min(), 0);
5362            assert_eq!(data1.max(), 10);
5363        } else {
5364            panic!("Expected ScalarValue");
5365        }
5366    }
5367
5368    #[test]
5369    fn test_type_constraint_w32() {
5370        let mut context = ComputationContext::default();
5371        let (new_lhs, _) = Type::constraint(
5372            &mut context,
5373            JumpType::Eq,
5374            JumpWidth::W32,
5375            Type::ScalarValue(ScalarValueData::UNKNOWN_WRITTEN),
5376            Type::from(10),
5377        )
5378        .unwrap();
5379        if let Type::ScalarValue(data1) = new_lhs {
5380            assert_eq!(data1.min(), 10);
5381            assert_eq!(data1.max(), 0xffff_ffff_0000_000a);
5382        } else {
5383            panic!("Expected ScalarValue");
5384        }
5385    }
5386
5387    #[test]
5388    fn test_type_constraint_w32_straddling_min() {
5389        // A 32-bit equality only constrains the low 32 bits. When the comparison
5390        // partner's range straddles 2^32 (min <= U32_MAX < max), its low 32 bits
5391        // can be smaller than its minimum, so the other register's minimum must
5392        // not be raised. `data1` is in [0, 0x40]; `data2` is in [0x30, u64::MAX].
5393        // On the equal branch `data1` can still be 0 (e.g. when `data2 == 2^32`,
5394        // whose low 32 bits are 0), so its minimum must remain 0.
5395        let mut context = ComputationContext::default();
5396        let data1 = ScalarValueData::new(0, u64::MAX, 0, U64Range::new(0, 0x40));
5397        let data2 = ScalarValueData::new(0, u64::MAX, 0, U64Range::new(0x30, u64::MAX));
5398        let (new_lhs, _) = Type::constraint(
5399            &mut context,
5400            JumpType::Eq,
5401            JumpWidth::W32,
5402            Type::ScalarValue(data1),
5403            Type::ScalarValue(data2),
5404        )
5405        .unwrap();
5406        if let Type::ScalarValue(data) = new_lhs {
5407            assert_eq!(data.min(), 0);
5408            assert_eq!(data.max(), 0x40);
5409        } else {
5410            panic!("Expected ScalarValue");
5411        }
5412    }
5413
5414    #[test]
5415    fn test_type_constraint_null_or() {
5416        let mut context = ComputationContext::default();
5417        let id = MemoryId::new();
5418        let null_or = Type::NullOr { id: id.clone(), inner: Arc::new(Type::UNKNOWN_SCALAR) };
5419        let (new_lhs, _) =
5420            Type::constraint(&mut context, JumpType::Eq, JumpWidth::W64, null_or, Type::from(0))
5421                .unwrap();
5422        if let Type::ScalarValue(data) = new_lhs {
5423            assert_eq!(data.value, 0);
5424        } else {
5425            panic!("Expected zero ScalarValue");
5426        }
5427    }
5428
5429    #[test]
5430    fn test_type_constraint_array_bounds() {
5431        let mut context = ComputationContext::default();
5432        let id = MemoryId::new();
5433        let _ = Type::constraint(
5434            &mut context,
5435            JumpType::Le,
5436            JumpWidth::W64,
5437            Type::PtrToArray { id: id.clone(), offset: 22.into() },
5438            Type::PtrToEndArray { id: id.clone() },
5439        )
5440        .unwrap();
5441        assert_eq!(context.array_bounds.get(&id), Some(&22));
5442    }
5443
5444    fn make_verification_context<'a>(
5445        logger: &'a mut dyn VerifierLogger,
5446    ) -> VerificationContext<'a> {
5447        VerificationContext::new(CallingContext::default(), logger, &[], vec![], vec![])
5448    }
5449
5450    /// Verifies that multiple invocations returning PtrToMemory receive
5451    /// distinct MemoryIds to prevent ASLR pointer arithmetic between separate
5452    /// allocations, while still matching the static parameter templates.
5453    #[test]
5454    fn test_resolve_return_value_unique_ids() {
5455        let mut logger = NullVerifierLogger;
5456        let mut verification_context = make_verification_context(&mut logger);
5457        let comp_ctx = ComputationContext::default();
5458
5459        let static_id = MemoryId::from_raw(42);
5460        let ret_template = Type::PtrToMemory {
5461            id: static_id.clone(),
5462            offset: 0.into(),
5463            buffer_size: 64,
5464            origin: PointerOrigin::KernelReadOnly,
5465        };
5466
5467        let mut next1 = ComputationContext::default();
5468        let t1 = comp_ctx
5469            .resolve_return_value(&mut verification_context, &ret_template, &mut next1, false)
5470            .unwrap();
5471        let mut next2 = ComputationContext::default();
5472        let t2 = comp_ctx
5473            .resolve_return_value(&mut verification_context, &ret_template, &mut next2, false)
5474            .unwrap();
5475
5476        assert_ne!(t1, t2);
5477        let (Type::PtrToMemory { id: id1, .. }, Type::PtrToMemory { id: id2, .. }) = (&t1, &t2)
5478        else {
5479            panic!("Expected PtrToMemory");
5480        };
5481        assert_ne!(id1, id2);
5482        assert!(static_id.matches(id1));
5483        assert!(static_id.matches(id2));
5484    }
5485
5486    /// Verifies that multiple releasable allocations are tracked individually
5487    /// in resources, releasing one does not remove the other, and passing a
5488    /// released resource to a helper parameter is rejected.
5489    #[test]
5490    fn test_resolve_return_value_releasable_resource_tracking() {
5491        let mut logger = NullVerifierLogger;
5492        let mut verification_context = make_verification_context(&mut logger);
5493        let comp_ctx = ComputationContext::default();
5494
5495        let static_id = MemoryId::from_raw(99);
5496        let releasable_template = Type::ReleasableParameter {
5497            id: static_id.clone(),
5498            inner: Box::new(Type::PtrToMemory {
5499                id: static_id.clone(),
5500                offset: 0.into(),
5501                buffer_size: 64,
5502                origin: PointerOrigin::KernelReadOnly,
5503            }),
5504        };
5505
5506        let mut next = ComputationContext::default();
5507        let t1 = comp_ctx
5508            .resolve_return_value(&mut verification_context, &releasable_template, &mut next, false)
5509            .unwrap();
5510        let t2 = comp_ctx
5511            .resolve_return_value(&mut verification_context, &releasable_template, &mut next, false)
5512            .unwrap();
5513
5514        assert_eq!(next.resources.len(), 2);
5515        let (Type::Releasable { id: id1, .. }, Type::Releasable { id: id2, .. }) = (&t1, &t2)
5516        else {
5517            panic!("Expected Releasable");
5518        };
5519        assert_ne!(id1, id2);
5520        assert!(next.resources.contains(id1));
5521        assert!(next.resources.contains(id2));
5522
5523        // Release one
5524        let mut next_after_free = next.clone();
5525        t1.match_parameter_type(
5526            &verification_context,
5527            &comp_ctx,
5528            "test_free",
5529            &Type::ReleaseParameter { id: static_id.clone() },
5530            0,
5531            &mut next_after_free,
5532        )
5533        .unwrap();
5534        assert_eq!(next_after_free.resources.len(), 1);
5535        assert!(!next_after_free.resources.contains(id1));
5536        assert!(next_after_free.resources.contains(id2));
5537
5538        // Passing released t1 to general helper must fail
5539        let mut next_uaf = next_after_free.clone();
5540        let err = t1.match_parameter_type(
5541            &verification_context,
5542            &comp_ctx,
5543            "test_helper",
5544            &Type::MemoryParameter {
5545                size: MemoryParameterSize::Value(64),
5546                input: true,
5547                output: false,
5548            },
5549            0,
5550            &mut next_uaf,
5551        );
5552        assert!(err.is_err());
5553        assert_eq!(err.unwrap_err(), VerifierErrorKind::UseAfterRelease);
5554    }
5555
5556    #[test]
5557    fn ptr_to_struct_ordering() {
5558        let desc1 = Arc::new(StructDescriptor {
5559            fields: vec![
5560                FieldDescriptor { offset: 0, field_type: FieldType::Scalar { size: 4 } },
5561                FieldDescriptor { offset: 4, field_type: FieldType::MutableScalar { size: 4 } },
5562            ],
5563        });
5564        let desc1_same_contents = Arc::new(StructDescriptor {
5565            fields: vec![
5566                FieldDescriptor { offset: 0, field_type: FieldType::Scalar { size: 4 } },
5567                FieldDescriptor { offset: 4, field_type: FieldType::MutableScalar { size: 4 } },
5568            ],
5569        });
5570        let desc_other = Arc::new(StructDescriptor {
5571            fields: vec![FieldDescriptor { offset: 0, field_type: FieldType::Scalar { size: 4 } }],
5572        });
5573
5574        let id = MemoryId::from_raw(1);
5575        let ptr_a =
5576            Type::PtrToStruct { id: id.clone(), offset: 0.into(), descriptor: Arc::clone(&desc1) };
5577        let ptr_b_shared_arc =
5578            Type::PtrToStruct { id: id.clone(), offset: 0.into(), descriptor: Arc::clone(&desc1) };
5579        let ptr_c_distinct_arc = Type::PtrToStruct {
5580            id: id.clone(),
5581            offset: 0.into(),
5582            descriptor: Arc::clone(&desc1_same_contents),
5583        };
5584        let ptr_different_offset =
5585            Type::PtrToStruct { id: id.clone(), offset: 4.into(), descriptor: Arc::clone(&desc1) };
5586        let ptr_other_desc =
5587            Type::PtrToStruct { id, offset: 0.into(), descriptor: Arc::clone(&desc_other) };
5588
5589        // Shared Arc (pointer-equal fast path) and distinct Arc with equal fields both compare
5590        // Equal.
5591        assert_eq!(ptr_a.partial_cmp(&ptr_b_shared_arc), Some(Ordering::Equal));
5592        assert_eq!(ptr_a.partial_cmp(&ptr_c_distinct_arc), Some(Ordering::Equal));
5593        assert_eq!(ptr_a.partial_cmp(&ptr_different_offset), None);
5594        assert_eq!(ptr_a.partial_cmp(&ptr_other_desc), None);
5595
5596        // Non-scalars are strictly narrower than UNINITIALIZED and incomparable with initialized
5597        // or narrowed uninitialized scalars.
5598        let narrowed_uninit = Type::ScalarValue(ScalarValueData::UNINITIALIZED & 7.into());
5599        assert_eq!(ptr_a.partial_cmp(&Type::UNINITIALIZED), Some(Ordering::Less));
5600        assert_eq!(Type::UNINITIALIZED.partial_cmp(&ptr_a), Some(Ordering::Greater));
5601        assert_eq!(ptr_a.partial_cmp(&Type::UNKNOWN_SCALAR), None);
5602        assert_eq!(Type::UNKNOWN_SCALAR.partial_cmp(&ptr_a), None);
5603        assert_eq!(ptr_a.partial_cmp(&narrowed_uninit), None);
5604        assert_eq!(narrowed_uninit.partial_cmp(&ptr_a), None);
5605
5606        // Narrowed uninitialized scalar (`unwritten_mask == u64::MAX`, `urange == [0, 7]`) is
5607        // strictly narrower than `Type::UNINITIALIZED`, broader than `5`, and incomparable with
5608        // out-of-range `1000`.
5609        assert_eq!(Type::UNINITIALIZED.partial_cmp(&narrowed_uninit), Some(Ordering::Greater));
5610        assert_eq!(narrowed_uninit.partial_cmp(&Type::UNINITIALIZED), Some(Ordering::Less));
5611        assert_eq!(narrowed_uninit.partial_cmp(&Type::from(5)), Some(Ordering::Greater));
5612        assert_eq!(Type::from(5).partial_cmp(&narrowed_uninit), Some(Ordering::Less));
5613        assert_eq!(narrowed_uninit.partial_cmp(&Type::from(1000)), None);
5614        assert_eq!(Type::from(1000).partial_cmp(&narrowed_uninit), None);
5615    }
5616
5617    #[test]
5618    fn stack_ordering_disjoint_and_overlapping_keys() {
5619        let mut s_broad = Stack::default();
5620        let mut s_narrow = Stack::default();
5621
5622        // Populate s_narrow at keys 1, 3, 5; keep s_broad empty (all UNINITIALIZED).
5623        s_narrow.set(1, Type::from(10));
5624        s_narrow.set(3, Type::from(20));
5625        s_narrow.set(5, Type::from(30));
5626        assert_eq!(s_broad.partial_cmp(&s_narrow), Some(Ordering::Greater));
5627        assert_eq!(s_narrow.partial_cmp(&s_broad), Some(Ordering::Less));
5628
5629        // Add middle key 3 to s_broad with a broader type (UNKNOWN_SCALAR > 20).
5630        // s_narrow has an earlier key (1 < 3) and a later key (5 > 3), exercising both
5631        // `Ordering::Greater` on `k1.cmp(k2)` and `(None, Some(_))` after `s_broad` is exhausted.
5632        s_broad.set(3, Type::UNKNOWN_SCALAR);
5633        assert_eq!(s_broad.partial_cmp(&s_narrow), Some(Ordering::Greater));
5634        assert_eq!(s_narrow.partial_cmp(&s_broad), Some(Ordering::Less));
5635
5636        // Setting a slot to UNINITIALIZED removes it from the map and preserves the ordering,
5637        // whereas a narrowed uninitialized scalar stays in the map and is strictly narrower than
5638        // an empty (all UNINITIALIZED) stack.
5639        s_broad.set(3, Type::UNINITIALIZED);
5640        assert!(s_broad.data.is_empty());
5641        assert_eq!(s_broad.partial_cmp(&s_narrow), Some(Ordering::Greater));
5642        let mut s_narrowed_uninit = Stack::default();
5643        s_narrowed_uninit.set(0, Type::ScalarValue(ScalarValueData::UNINITIALIZED & 7.into()));
5644        assert_eq!(s_broad.partial_cmp(&s_narrowed_uninit), Some(Ordering::Greater));
5645        assert_eq!(s_narrowed_uninit.partial_cmp(&s_broad), Some(Ordering::Less));
5646
5647        // Conflicting orderings at overlapping keys must return None.
5648        s_broad.set(1, Type::from(10));
5649        s_broad.set(3, Type::UNKNOWN_SCALAR);
5650        s_broad.set(5, Type::from(99));
5651        assert_eq!(s_broad.partial_cmp(&s_narrow), None);
5652        assert_eq!(s_narrow.partial_cmp(&s_broad), None);
5653    }
5654
5655    #[test]
5656    fn test_verifier_logger_is_enabled() {
5657        struct LimitedLogger {
5658            max_lines: usize,
5659            lines: Vec<String>,
5660        }
5661
5662        impl VerifierLogger for LimitedLogger {
5663            fn is_enabled(&self) -> bool {
5664                self.lines.len() < self.max_lines
5665            }
5666
5667            fn log(&mut self, line: &[u8]) {
5668                assert!(line.is_ascii());
5669                self.lines.push(std::str::from_utf8(line).unwrap().to_string());
5670            }
5671        }
5672
5673        let code = vec![
5674            EbpfInstruction::new(BPF_ALU64 | BPF_MOV | BPF_K, 0, 0, 0, 0),
5675            EbpfInstruction::new(BPF_ALU64 | BPF_ADD | BPF_K, 0, 0, 0, 1),
5676            EbpfInstruction::new(BPF_JMP | BPF_EXIT, 0, 0, 0, 0),
5677        ];
5678
5679        let mut full_logger = LimitedLogger { max_lines: usize::MAX, lines: vec![] };
5680        verify_program(code.clone(), CallingContext::default(), &mut full_logger).unwrap();
5681        assert_eq!(
5682            full_logger.lines,
5683            vec!["0: (b7) mov %r0, 0x0", "1: (07) add %r0, 0x1", "2: (95) exit"]
5684        );
5685
5686        let mut capped_logger = LimitedLogger { max_lines: 2, lines: vec![] };
5687        verify_program(code, CallingContext::default(), &mut capped_logger).unwrap();
5688        assert_eq!(capped_logger.lines, vec!["0: (b7) mov %r0, 0x0", "1: (07) add %r0, 0x1"]);
5689    }
5690
5691    /// Verifies that memory store instructions to read-only memory pointers are
5692    /// rejected, while stores to writable pointers succeed.
5693    #[test]
5694    fn test_read_only_map_value_store() {
5695        let mut logger = NullVerifierLogger;
5696        let mut context = make_verification_context(&mut logger);
5697        let mut next = ComputationContext::default();
5698
5699        let ptr_with_origin = |origin| Type::PtrToMemory {
5700            id: MemoryId::new(),
5701            offset: 0.into(),
5702            buffer_size: 4,
5703            origin,
5704        };
5705
5706        let u32_field = Field::new(0, DataWidth::U32);
5707
5708        // Store to read-only memory must fail.
5709        let ro_map_ptr = ptr_with_origin(PointerOrigin::MapReadOnly);
5710        let err = next.store_memory(&mut context, 0, &ro_map_ptr, u32_field, Type::from(42));
5711        assert_eq!(err, Err(VerifierErrorKind::WriteToReadOnlyMemory));
5712
5713        let ro_kernel_ptr = ptr_with_origin(PointerOrigin::KernelReadOnly);
5714        let err = next.store_memory(&mut context, 0, &ro_kernel_ptr, u32_field, Type::from(42));
5715        assert_eq!(err, Err(VerifierErrorKind::WriteToReadOnlyMemory));
5716
5717        // Store to writable memory must succeed.
5718        let rw_map_ptr = ptr_with_origin(PointerOrigin::MapWritable);
5719        let res = next.store_memory(&mut context, 0, &rw_map_ptr, u32_field, Type::from(42));
5720        assert!(res.is_ok());
5721
5722        let rw_kernel_ptr = ptr_with_origin(PointerOrigin::KernelWritable);
5723        let res = next.store_memory(&mut context, 0, &rw_kernel_ptr, u32_field, Type::from(42));
5724        assert!(res.is_ok());
5725    }
5726
5727    /// Verifies that atomic operations are only allowed on naturally aligned
5728    /// addresses within the stack or a map value.
5729    #[test]
5730    fn test_atomic_access_checks() {
5731        let context = ComputationContext::default();
5732
5733        let ptr_to_memory = |offset: ScalarValueData, origin| Type::PtrToMemory {
5734            id: MemoryId::new(),
5735            offset,
5736            buffer_size: 64,
5737            origin,
5738        };
5739        let ptr_to_stack =
5740            |offset: i16| Type::PtrToStack { offset: StackOffset::default().add(offset) };
5741        // An offset that is either 0 or 1: its low bit is unknown.
5742        let unknown_low_bit = ScalarValueData::new(0, 1, 0, U64Range::new(0, 1));
5743        // An offset that is either 0 or 8: its low 3 bits are known to be 0.
5744        let unknown_high_bits = ScalarValueData::new(0, 8, 0, U64Range::new(0, 8));
5745
5746        let u32_field = |offset| Field::new(offset, DataWidth::U32);
5747        let u64_field = |offset| Field::new(offset, DataWidth::U64);
5748
5749        // Aligned accesses to map values and the stack are allowed.
5750        for origin in [PointerOrigin::MapWritable, PointerOrigin::MapReadOnly] {
5751            let ptr = ptr_to_memory(0.into(), origin);
5752            assert_eq!(context.check_atomic_access(&ptr, u64_field(8)), Ok(()));
5753            assert_eq!(context.check_atomic_access(&ptr, u32_field(4)), Ok(()));
5754            let ptr = ptr_to_memory(4.into(), origin);
5755            assert_eq!(context.check_atomic_access(&ptr, u64_field(4)), Ok(()));
5756            assert_eq!(context.check_atomic_access(&ptr, u32_field(-4)), Ok(()));
5757            let ptr = ptr_to_memory(unknown_high_bits, origin);
5758            assert_eq!(context.check_atomic_access(&ptr, u64_field(16)), Ok(()));
5759        }
5760        assert_eq!(context.check_atomic_access(&ptr_to_stack(-8), u64_field(0)), Ok(()));
5761        assert_eq!(context.check_atomic_access(&ptr_to_stack(-4), u32_field(-4)), Ok(()));
5762
5763        // Misaligned accesses are rejected, including when the alignment cannot
5764        // be proven because the low bits of the offset are unknown.
5765        let map_ptr = ptr_to_memory(0.into(), PointerOrigin::MapWritable);
5766        assert_eq!(
5767            context.check_atomic_access(&map_ptr, u64_field(4)),
5768            Err(VerifierErrorKind::MisalignedAtomicAccess)
5769        );
5770        assert_eq!(
5771            context.check_atomic_access(&map_ptr, u32_field(2)),
5772            Err(VerifierErrorKind::MisalignedAtomicAccess)
5773        );
5774        let map_ptr = ptr_to_memory(4.into(), PointerOrigin::MapWritable);
5775        assert_eq!(
5776            context.check_atomic_access(&map_ptr, u64_field(0)),
5777            Err(VerifierErrorKind::MisalignedAtomicAccess)
5778        );
5779        let map_ptr = ptr_to_memory(unknown_low_bit, PointerOrigin::MapWritable);
5780        assert_eq!(
5781            context.check_atomic_access(&map_ptr, u64_field(0)),
5782            Err(VerifierErrorKind::MisalignedAtomicAccess)
5783        );
5784        assert_eq!(
5785            context.check_atomic_access(&ptr_to_stack(-6), u32_field(0)),
5786            Err(VerifierErrorKind::MisalignedAtomicAccess)
5787        );
5788        assert_eq!(
5789            context.check_atomic_access(&ptr_to_stack(-8), u64_field(4)),
5790            Err(VerifierErrorKind::MisalignedAtomicAccess)
5791        );
5792
5793        // Atomic accesses to other kinds of memory are rejected, even when
5794        // aligned.
5795        for origin in [PointerOrigin::KernelWritable, PointerOrigin::KernelReadOnly] {
5796            let ptr = ptr_to_memory(0.into(), origin);
5797            assert_eq!(
5798                context.check_atomic_access(&ptr, u64_field(8)),
5799                Err(VerifierErrorKind::AtomicAccessNotAllowed)
5800            );
5801        }
5802        assert_eq!(
5803            context.check_atomic_access(&Type::from(0), u64_field(0)),
5804            Err(VerifierErrorKind::AtomicAccessNotAllowed)
5805        );
5806    }
5807
5808    #[test]
5809    fn test_struct_access_pc() {
5810        let descriptor = Arc::new(StructDescriptor {
5811            fields: vec![
5812                FieldDescriptor { offset: 0, field_type: FieldType::MutableScalar { size: 4 } },
5813                FieldDescriptor { offset: 4, field_type: FieldType::Scalar { size: 4 } },
5814                FieldDescriptor { offset: 8, field_type: FieldType::MutableScalar { size: 4 } },
5815                FieldDescriptor {
5816                    offset: 12,
5817                    field_type: FieldType::PtrToMemory {
5818                        is_32_bit: true,
5819                        id: MemoryId::from_raw(20),
5820                        buffer_size: 64,
5821                    },
5822                },
5823            ],
5824        });
5825        let memory_id = MemoryId::from_raw(1);
5826        let ptr = Type::PtrToStruct { id: memory_id.clone(), offset: 0.into(), descriptor };
5827        let calling_context = CallingContext { args: vec![ptr], ..CallingContext::default() };
5828
5829        // Instructions:
5830        // PC 0: r0 = 0
5831        // PC 1: r2 = 42
5832        // PC 2: stw [r1 + 0], 10 (BPF_ST - store to offset 0)
5833        // PC 3: ldxw r3, [r1 + 4] (BPF_LDX - load scalar from offset 4 right after store at PC 2)
5834        // PC 4: stxw [r1 + 8], r2 (BPF_STX - store to offset 8)
5835        // PC 5: ldxw r4, [r1 + 12] (BPF_LDX - load 32-bit ptr from offset 12 right after store at PC 4)
5836        // PC 6: exit
5837        let code = vec![
5838            EbpfInstruction::new(BPF_ALU64 | BPF_MOV | BPF_K, 0, 0, 0, 0),
5839            EbpfInstruction::new(BPF_ALU64 | BPF_MOV | BPF_K, 2, 0, 0, 42),
5840            EbpfInstruction::new(BPF_ST | BPF_MEM | BPF_W, 1, 0, 0, 10),
5841            EbpfInstruction::new(BPF_LDX | BPF_MEM | BPF_W, 3, 1, 4, 0),
5842            EbpfInstruction::new(BPF_STX | BPF_MEM | BPF_W, 1, 2, 8, 0),
5843            EbpfInstruction::new(BPF_LDX | BPF_MEM | BPF_W, 4, 1, 12, 0),
5844            EbpfInstruction::new(BPF_JMP | BPF_EXIT, 0, 0, 0, 0),
5845        ];
5846
5847        let program = verify_program(code, calling_context, &mut NullVerifierLogger)
5848            .expect("verification failed");
5849
5850        let mut accesses = program.struct_access_instructions().to_vec();
5851        accesses.sort_by_key(|a| a.pc);
5852
5853        assert_eq!(
5854            accesses,
5855            vec![
5856                StructAccess {
5857                    pc: 2,
5858                    memory_id: memory_id.clone(),
5859                    field_offset: 0,
5860                    is_32_bit_ptr_load: false,
5861                },
5862                StructAccess {
5863                    pc: 3,
5864                    memory_id: memory_id.clone(),
5865                    field_offset: 4,
5866                    is_32_bit_ptr_load: false,
5867                },
5868                StructAccess {
5869                    pc: 4,
5870                    memory_id: memory_id.clone(),
5871                    field_offset: 8,
5872                    is_32_bit_ptr_load: false,
5873                },
5874                StructAccess {
5875                    pc: 5,
5876                    memory_id: memory_id.clone(),
5877                    field_offset: 12,
5878                    is_32_bit_ptr_load: true,
5879                },
5880            ]
5881        );
5882    }
5883}