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