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