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