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