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