1use crate::{
4 aligned_memory::Pod,
5 ebpf,
6 error::{EbpfError, ProgramResult, StableResult},
7 program::SBPFVersion,
8 vm::Config,
9};
10use std::fmt::Formatter;
11use std::{array, cell::UnsafeCell, fmt, mem, ops::Range, ptr};
12
13pub type AccessViolationHandler = Box<dyn Fn(&mut MemoryRegion, u64, AccessType, u64, u64)>;
32#[allow(clippy::result_unit_err)]
34pub fn default_access_violation_handler(
35 _region: &mut MemoryRegion,
36 _region_max_len: u64,
37 _access_type: AccessType,
38 _vm_addr: u64,
39 _len: u64,
40) {
41}
42
43pub unsafe trait HostMemoryObject {
50 fn host(self) -> HostBuffer;
54}
55
56pub trait VmExposable {}
61
62pub unsafe trait VmExposableMut {}
72
73unsafe impl VmExposableMut for u8 {}
74impl<T: VmExposableMut> VmExposable for T {}
75
76unsafe impl<T: VmExposable> HostMemoryObject for *const T {
77 fn host(self) -> HostBuffer {
78 HostBuffer::Immutable(ptr::slice_from_raw_parts(
79 self.cast(),
80 std::mem::size_of::<T>(),
81 ))
82 }
83}
84
85unsafe impl<T: VmExposableMut> HostMemoryObject for *mut T {
86 fn host(self) -> HostBuffer {
87 HostBuffer::Mutable(ptr::slice_from_raw_parts_mut(
88 self.cast(),
89 std::mem::size_of::<T>(),
90 ))
91 }
92}
93
94unsafe impl<T: VmExposable> HostMemoryObject for *const [T] {
95 fn host(self) -> HostBuffer {
96 HostBuffer::Immutable(ptr::slice_from_raw_parts(
97 self.cast(),
98 self.len().checked_mul(core::mem::size_of::<T>()).unwrap(),
99 ))
100 }
101}
102
103unsafe impl<T: VmExposableMut> HostMemoryObject for *mut [T] {
104 fn host(self) -> HostBuffer {
105 HostBuffer::Mutable(ptr::slice_from_raw_parts_mut(
106 self.cast(),
107 self.len().checked_mul(core::mem::size_of::<T>()).unwrap(),
108 ))
109 }
110}
111
112unsafe impl<T: VmExposable, const N: usize> HostMemoryObject for *const [T; N] {
113 fn host(self) -> HostBuffer {
114 HostBuffer::Immutable(ptr::slice_from_raw_parts(
115 self.cast(),
116 N.checked_mul(core::mem::size_of::<T>()).unwrap(),
117 ))
118 }
119}
120
121unsafe impl<T: VmExposableMut, const N: usize> HostMemoryObject for *mut [T; N] {
122 fn host(self) -> HostBuffer {
123 HostBuffer::Mutable(ptr::slice_from_raw_parts_mut(
124 self.cast(),
125 N.checked_mul(core::mem::size_of::<T>()).unwrap(),
126 ))
127 }
128}
129
130#[derive(PartialEq, Eq, Copy, Clone, Debug)]
132pub enum HostBuffer {
133 Immutable(*const [u8]),
135 Mutable(*mut [u8]),
137}
138
139impl HostBuffer {
140 pub fn len(&self) -> usize {
142 match self {
143 HostBuffer::Immutable(p) => p.len(),
144 HostBuffer::Mutable(p) => p.len(),
145 }
146 }
147
148 pub fn is_empty(&self) -> bool {
150 match self {
151 HostBuffer::Immutable(p) => p.is_empty(),
152 HostBuffer::Mutable(p) => p.is_empty(),
153 }
154 }
155
156 pub fn is_mutable(&self) -> bool {
158 matches!(self, HostBuffer::Mutable(_))
159 }
160
161 pub unsafe fn mutable(self) -> Self {
167 match self {
168 HostBuffer::Immutable(p) => HostBuffer::Mutable(p.cast_mut()),
169 HostBuffer::Mutable(_) => self,
170 }
171 }
172
173 pub fn immutable(self) -> Self {
175 match self {
176 HostBuffer::Immutable(_) => self,
177 HostBuffer::Mutable(p) => Self::Immutable(p.cast_const()),
178 }
179 }
180
181 #[inline]
183 pub fn get(self, range: std::ops::Range<usize>) -> Option<Self> {
184 if range.end > self.len() {
185 return None;
186 }
187 let new_len = range.len();
188 unsafe {
189 Some(match self {
208 HostBuffer::Immutable(p) => HostBuffer::Immutable(ptr::slice_from_raw_parts(
209 p.byte_add(range.start).cast(),
210 new_len,
211 )),
212 HostBuffer::Mutable(p) => HostBuffer::Mutable(ptr::slice_from_raw_parts_mut(
213 p.byte_add(range.start).cast(),
214 new_len,
215 )),
216 })
217 }
218 }
219
220 #[inline(always)]
222 pub fn ptr(self) -> *const [u8] {
223 match self {
224 HostBuffer::Immutable(p) => p,
225 HostBuffer::Mutable(p) => p,
226 }
227 }
228
229 #[inline(always)]
231 pub fn ptr_mut(self) -> *mut [u8] {
232 match self {
233 HostBuffer::Immutable(p) => {
234 debug_assert!(false, "ptr_mut, but buffer is immutable");
235 p.cast_mut()
236 }
237 HostBuffer::Mutable(p) => p,
238 }
239 }
240}
241
242unsafe impl HostMemoryObject for HostBuffer {
243 fn host(self) -> HostBuffer {
244 self
245 }
246}
247
248#[derive(Eq, PartialEq, Clone)]
250pub struct MemoryRegion {
251 host: HostBuffer,
252 vm_addr: u64,
254 vm_gap_shift: u8,
256 pub access_violation_handler_payload: Option<u16>,
258}
259
260impl MemoryRegion {
261 fn new_internal(host: HostBuffer, vm_addr: u64, vm_gap_size: u64) -> Self {
265 let mut vm_gap_shift = (std::mem::size_of::<u64>() as u8)
266 .saturating_mul(8)
267 .saturating_sub(1);
268 if vm_gap_size > 0 {
269 vm_gap_shift = vm_gap_shift.saturating_sub(vm_gap_size.leading_zeros() as u8);
270 debug_assert_eq!(Some(vm_gap_size), 1_u64.checked_shl(vm_gap_shift as u32));
271 };
272 MemoryRegion {
273 host,
274 vm_addr,
275 vm_gap_shift,
276 access_violation_handler_payload: None,
277 }
278 }
279
280 pub fn new_empty(vm_addr: u64) -> Self {
284 const EMPTY: &[u8] = &[];
285 Self::new_internal((&raw const *EMPTY).host(), vm_addr, 0)
286 }
287
288 pub fn new<HO: HostMemoryObject>(host: HO, vm_addr: u64) -> Self {
292 Self::new_internal(host.host(), vm_addr, 0)
293 }
294
295 pub fn new_gapped<HO: HostMemoryObject>(host: HO, vm_addr: u64, vm_gap_size: u64) -> Self {
299 Self::new_internal(host.host(), vm_addr, vm_gap_size)
300 }
301
302 pub unsafe fn redirect<HO: HostMemoryObject>(&mut self, host: HO) {
312 self.host = host.host();
313 }
314
315 pub fn make_immutable(&mut self) {
317 unsafe {
318 self.redirect(self.host_buffer().immutable());
330 }
331 }
332
333 pub fn vm_addr_range(&self) -> Range<u64> {
335 let bytes = self.len() as u64;
336 if self.vm_gap_shift == 63 {
337 self.vm_addr..self.vm_addr.saturating_add(bytes)
338 } else {
339 self.vm_addr..self.vm_addr.saturating_add(bytes.saturating_mul(2))
340 }
341 }
342
343 pub fn host_buffer(&self) -> HostBuffer {
347 self.host
348 }
349
350 pub fn len(&self) -> usize {
352 self.host.len()
353 }
354
355 pub fn is_empty(&self) -> bool {
357 self.host.is_empty()
358 }
359
360 pub fn gap_size(&self) -> u64 {
362 if self.vm_gap_shift == 63 {
363 0
364 } else {
365 1 << self.vm_gap_shift
366 }
367 }
368
369 #[inline]
374 pub(crate) fn vm_to_host_buffer(&self, vm_addr: u64, len: u64) -> Option<HostBuffer> {
375 if vm_addr < self.vm_addr {
379 return None;
380 }
381
382 let begin_offset = vm_addr.saturating_sub(self.vm_addr);
383 if self.vm_gap_shift == 63 {
384 if let Some(end_offset) = begin_offset.checked_add(len) {
386 return self.host.get(begin_offset as usize..end_offset as usize);
387 }
388 return None;
389 }
390
391 let is_in_gap = (begin_offset
392 .checked_shr(self.vm_gap_shift as u32)
393 .unwrap_or(0)
394 & 1)
395 == 1;
396 let gap_mask = (-1i64).checked_shl(self.vm_gap_shift as u32).unwrap_or(0) as u64;
397 let gapped_offset =
398 (begin_offset & gap_mask).checked_shr(1).unwrap_or(0) | (begin_offset & !gap_mask);
399 if let Some(end_offset) = gapped_offset.checked_add(len) {
400 if !is_in_gap {
401 return self.host.get(gapped_offset as usize..end_offset as usize);
402 }
403 }
404 None
405 }
406}
407
408impl fmt::Debug for MemoryRegion {
409 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
410 let vm_addr = self.vm_addr_range();
411 let (host_addr, len, writable) = match self.host {
412 HostBuffer::Immutable(p) => (p.addr() as u64, p.len() as u64, false),
413 HostBuffer::Mutable(p) => (p.addr() as u64, p.len() as u64, true),
414 };
415 write!(
416 f,
417 "host_addr: {:#x?}-{:#x?}, vm_addr: {:#x?}-{:#x?}, len: {}, writable: {}, payload {:?}",
418 host_addr,
419 host_addr.saturating_add(len),
420 vm_addr.start,
421 vm_addr.end,
422 len,
423 writable,
424 self.access_violation_handler_payload,
425 )
426 }
427}
428
429impl std::cmp::PartialOrd for MemoryRegion {
430 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
431 Some(self.cmp(other))
432 }
433}
434
435impl std::cmp::Ord for MemoryRegion {
436 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
437 self.vm_addr.cmp(&other.vm_addr)
438 }
439}
440
441#[derive(Clone, Copy, PartialEq, Eq, Debug)]
443pub enum AccessType {
444 Load,
446 Store,
448}
449
450impl std::fmt::Display for AccessType {
451 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
452 f.write_str(match self {
453 Self::Load => "reading",
454 Self::Store => "writing",
455 })
456 }
457}
458
459pub struct UnalignedMemoryMapping {
461 regions: Box<[MemoryRegion]>,
463 region_addresses: Box<[u64]>,
465 region_index_lookup: Box<[usize]>,
467 cache: UnsafeCell<MappingCache>,
469}
470
471impl fmt::Debug for UnalignedMemoryMapping {
472 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473 f.debug_struct("UnalignedMemoryMapping")
474 .field("regions", &self.regions)
475 .field("cache", &self.cache)
476 .finish()
477 }
478}
479
480impl UnalignedMemoryMapping {
481 fn construct_eytzinger_order(&mut self, mut in_index: usize, out_index: usize) -> usize {
482 if out_index >= self.regions.len() {
483 return in_index;
484 }
485 in_index =
486 self.construct_eytzinger_order(in_index, out_index.saturating_mul(2).saturating_add(1));
487 self.region_addresses[out_index] = self.regions[in_index].vm_addr;
488 self.region_index_lookup[out_index] = in_index;
489 self.construct_eytzinger_order(
490 in_index.saturating_add(1),
491 out_index.saturating_mul(2).saturating_add(2),
492 )
493 }
494
495 pub unsafe fn new_uninitialized(regions: Vec<MemoryRegion>) -> Self {
501 let number_of_regions = regions.len();
502 Self {
503 regions: regions.into_boxed_slice(),
504 region_addresses: vec![0; number_of_regions].into_boxed_slice(),
505 region_index_lookup: vec![0; number_of_regions].into_boxed_slice(),
506 cache: UnsafeCell::new(MappingCache::new()),
507 }
508 }
509
510 pub unsafe fn new(regions: Vec<MemoryRegion>) -> Result<Self, EbpfError> {
516 let mut mapping = Self::new_uninitialized(regions);
517 mapping.initialize()?;
518 Ok(mapping)
519 }
520
521 pub fn initialize(&mut self) -> Result<(), EbpfError> {
523 self.regions.sort();
524 let number_of_regions = self.regions.len();
525 for index in 1..number_of_regions {
526 let first = &self.regions[index.saturating_sub(1)];
527 let second = &self.regions[index];
528 if first.vm_addr_range().end > second.vm_addr {
529 return Err(EbpfError::InvalidMemoryRegion(index));
530 }
531 }
532
533 self.construct_eytzinger_order(0, 0);
534 Ok(())
535 }
536
537 #[allow(clippy::arithmetic_side_effects)]
539 #[inline(always)]
540 pub fn find_region(&self, vm_addr: u64) -> Option<(usize, &MemoryRegion)> {
541 let cache = unsafe { &mut *self.cache.get() };
546 if let Some(index) = cache.find(vm_addr) {
547 Some((index, unsafe { self.regions.get_unchecked(index) }))
551 } else {
552 let mut index = 1;
553 while index <= self.region_addresses.len() {
554 index = (index << 1)
558 + unsafe { *self.region_addresses.get_unchecked(index - 1) <= vm_addr }
559 as usize;
560 }
561 index >>= index.trailing_zeros() + 1;
562 if index == 0 {
563 return None;
564 }
565 index = unsafe { *self.region_index_lookup.get_unchecked(index - 1) };
569 let region = unsafe { self.regions.get_unchecked(index) };
570 cache.insert(region.vm_addr_range(), index);
571 Some((index, region))
572 }
573 }
574
575 #[inline(always)]
581 pub unsafe fn replace_region(
582 &mut self,
583 index: usize,
584 region: MemoryRegion,
585 ) -> Result<(), EbpfError> {
586 self.regions[index] = region;
587 self.cache.get_mut().flush();
588 Ok(())
589 }
590}
591
592#[derive(Debug)]
595pub struct AlignedMemoryMapping {
596 regions: Vec<MemoryRegion>,
597 allow_memory_region_zero: bool,
598}
599
600impl AlignedMemoryMapping {
601 pub unsafe fn new(regions: Vec<MemoryRegion>, config: &Config) -> Result<Self, EbpfError> {
607 let mut mapping = Self::new_uninitialized(regions, config);
608 mapping.initialize()?;
609 Ok(mapping)
610 }
611
612 pub unsafe fn new_uninitialized(regions: Vec<MemoryRegion>, config: &Config) -> Self {
618 Self {
619 regions,
620 allow_memory_region_zero: config.allow_memory_region_zero,
621 }
622 }
623
624 pub fn initialize(&mut self) -> Result<(), EbpfError> {
626 static EMPTY_SLICE: &[u8] = &[];
627 if self.allow_memory_region_zero {
628 self.regions.sort();
629 let mut expected_region_index = 0;
630 while expected_region_index < self.regions.len() {
631 let actual_region_index = self
632 .regions
633 .get(expected_region_index)
634 .unwrap()
635 .vm_addr
636 .checked_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32)
637 .unwrap_or(0) as usize;
638 if actual_region_index > expected_region_index {
639 self.regions.insert(
640 expected_region_index,
641 MemoryRegion::new(
642 &raw const *EMPTY_SLICE,
643 (expected_region_index as u64).saturating_mul(ebpf::MM_REGION_SIZE),
644 ),
645 );
646 } else if actual_region_index < expected_region_index {
647 return Err(EbpfError::InvalidMemoryRegion(actual_region_index));
648 }
649 expected_region_index = expected_region_index.saturating_add(1);
650 }
651 } else {
652 self.regions
653 .push(MemoryRegion::new(&raw const *EMPTY_SLICE, 0));
654 self.regions.sort();
655 for (index, region) in self.regions.iter().enumerate() {
656 if region
657 .vm_addr
658 .checked_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32)
659 .unwrap_or(0)
660 != index as u64
661 {
662 return Err(EbpfError::InvalidMemoryRegion(index));
663 }
664 }
665 }
666
667 Ok(())
668 }
669
670 #[inline(always)]
672 pub fn find_region(&self, vm_addr: u64) -> Option<(usize, &MemoryRegion)> {
673 let index = vm_addr.wrapping_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32) as usize;
674 if index < self.regions.len() && (index > 0 || self.allow_memory_region_zero) {
675 let region = unsafe { self.regions.get_unchecked(index) };
677 return Some((index, region));
678 }
679 None
680 }
681
682 #[inline(always)]
688 pub unsafe fn replace_region(
689 &mut self,
690 index: usize,
691 region: MemoryRegion,
692 ) -> Result<(), EbpfError> {
693 let begin_index = region
694 .vm_addr
695 .checked_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32)
696 .unwrap_or(0) as usize;
697 let end_index = region
698 .vm_addr
699 .saturating_add((region.len() as u64).saturating_sub(1))
700 .checked_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32)
701 .unwrap_or(0) as usize;
702 if begin_index != index || end_index != index {
703 return Err(EbpfError::InvalidMemoryRegion(index));
704 }
705 self.regions[index] = region;
706 Ok(())
707 }
708}
709
710pub struct MemoryMapping {
712 access_violation_handler: AccessViolationHandler,
714 max_call_depth: i64,
715 stack_frame_size: i64,
716 disable_address_translation: bool,
717 sbpf_version: SBPFVersion,
719 initialized: bool,
720 ty: MemoryMappingType,
721}
722
723impl fmt::Debug for MemoryMapping {
724 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
725 f.debug_struct("MemoryMapping")
726 .field("max_call_depth", &self.max_call_depth)
727 .field("stack_frame_size", &self.stack_frame_size)
728 .field("sbpf_version", &self.sbpf_version)
729 .field("ty", &self.ty)
730 .finish()
731 }
732}
733
734#[derive(Debug)]
736pub enum MemoryMappingType {
737 Aligned(AlignedMemoryMapping),
740 Unaligned(UnalignedMemoryMapping),
742}
743
744impl MemoryMapping {
745 pub unsafe fn new_with_access_violation_handler(
757 regions: Vec<MemoryRegion>,
758 config: &Config,
759 sbpf_version: SBPFVersion,
760 access_violation_handler: AccessViolationHandler,
761 ) -> Result<Self, EbpfError> {
762 let mut mapping =
763 Self::new_uninitialized(regions, config, sbpf_version, access_violation_handler);
764 mapping.initialize()?;
765 Ok(mapping)
766 }
767
768 pub unsafe fn new_uninitialized(
779 regions: Vec<MemoryRegion>,
780 config: &Config,
781 sbpf_version: SBPFVersion,
782 access_violation_handler: AccessViolationHandler,
783 ) -> Self {
784 let ty = if sbpf_version >= SBPFVersion::V4 || config.aligned_memory_mapping {
785 MemoryMappingType::Aligned(AlignedMemoryMapping::new_uninitialized(regions, config))
786 } else {
787 debug_assert!(
788 sbpf_version <= SBPFVersion::V3,
789 "SBPFv4 and later versions do not support unaligned memory"
790 );
791 MemoryMappingType::Unaligned(UnalignedMemoryMapping::new_uninitialized(regions))
792 };
793
794 Self {
795 access_violation_handler: Box::new(access_violation_handler),
796 max_call_depth: config.max_call_depth as i64,
797 stack_frame_size: config.stack_frame_size as i64,
798 disable_address_translation: !config.enable_address_translation,
799 sbpf_version,
800 initialized: false,
801 ty,
802 }
803 }
804
805 pub unsafe fn new(
813 regions: Vec<MemoryRegion>,
814 config: &Config,
815 sbpf_version: SBPFVersion,
816 ) -> Result<Self, EbpfError> {
817 Self::new_with_access_violation_handler(
818 regions,
819 config,
820 sbpf_version,
821 Box::new(default_access_violation_handler),
822 )
823 }
824
825 pub fn map(
827 &self,
828 access_type: AccessType,
829 vm_addr: u64,
830 len: u64,
831 ) -> StableResult<HostBuffer, EbpfError> {
832 debug_assert!(self.initialized);
833 if self.disable_address_translation {
834 let ptr = ptr::with_exposed_provenance_mut(vm_addr as usize);
840 let buffer = HostBuffer::Mutable(ptr::slice_from_raw_parts_mut(ptr, len as usize));
841 return StableResult::Ok(buffer);
842 }
843 if let Some((_index, region)) = self.find_region(vm_addr) {
844 if region.host_buffer().is_mutable() || access_type != AccessType::Store {
845 if let Some(host_buffer) = region.vm_to_host_buffer(vm_addr, len) {
846 return StableResult::Ok(host_buffer);
847 }
848 }
849 }
850 StableResult::Err(self.generate_access_violation(access_type, vm_addr, len))
851 }
852
853 #[inline(always)]
858 pub fn map_with_access_violation_handler(
859 &mut self,
860 access_type: AccessType,
861 vm_addr: u64,
862 len: u64,
863 ) -> StableResult<HostBuffer, EbpfError> {
864 debug_assert!(self.initialized);
865 if self.disable_address_translation {
866 let ptr = ptr::with_exposed_provenance_mut(vm_addr as usize);
872 let buffer = HostBuffer::Mutable(ptr::slice_from_raw_parts_mut(ptr, len as usize));
873 return StableResult::Ok(buffer);
874 }
875
876 if let Some((index, region)) = self.find_region(vm_addr) {
877 if region.host_buffer().is_mutable() || access_type != AccessType::Store {
878 if let Some(host_buffer) = region.vm_to_host_buffer(vm_addr, len) {
879 return StableResult::Ok(host_buffer);
880 }
881 }
882 let mut region = (*region).clone();
883 let max_len = self
884 .get_regions()
885 .get(index.saturating_add(1))
886 .map_or(u64::MAX, |next_region| next_region.vm_addr)
887 .saturating_sub(region.vm_addr);
888 (self.access_violation_handler)(&mut region, max_len, access_type, vm_addr, len);
889 if region.host_buffer().is_mutable() || access_type != AccessType::Store {
890 if let Some(host_buffer) = region.vm_to_host_buffer(vm_addr, len) {
891 if let Err(err) = unsafe { self.replace_region(index, region) } {
892 return StableResult::Err(err);
893 }
894 return StableResult::Ok(host_buffer);
895 }
896 }
897 }
898 StableResult::Err(self.generate_access_violation(access_type, vm_addr, len))
899 }
900
901 pub fn load<T: Pod + Into<u64>>(&mut self, vm_addr: u64) -> ProgramResult {
903 let len = mem::size_of::<T>() as u64;
904 debug_assert!(len <= mem::size_of::<u64>() as u64);
905 debug_assert!(self.initialized);
906 let ptr = match self.map_with_access_violation_handler(AccessType::Load, vm_addr, len) {
907 StableResult::Err(e) => return ProgramResult::Err(e),
908 StableResult::Ok(buf) => buf.ptr(),
909 };
910 ProgramResult::Ok(unsafe {
911 ptr::read_unaligned::<T>(ptr.cast()).into()
920 })
921 }
922
923 pub fn store<T: Pod>(&mut self, value: T, vm_addr: u64) -> ProgramResult {
925 let len = mem::size_of::<T>() as u64;
926 debug_assert!(len <= mem::size_of::<u64>() as u64);
927 debug_assert!(self.initialized);
928 let ptr = match self.map_with_access_violation_handler(AccessType::Store, vm_addr, len) {
929 StableResult::Err(e) => return ProgramResult::Err(e),
930 StableResult::Ok(buf) => buf.ptr_mut(),
931 };
932 StableResult::Ok(unsafe {
933 ptr::write_unaligned::<T>(ptr.cast(), value);
942 0
943 })
944 }
945
946 #[inline(always)]
948 pub fn find_region(&self, vm_addr: u64) -> Option<(usize, &MemoryRegion)> {
949 debug_assert!(self.initialized);
950 match &self.ty {
951 MemoryMappingType::Aligned(inner) => inner.find_region(vm_addr),
952 MemoryMappingType::Unaligned(inner) => inner.find_region(vm_addr),
953 }
954 }
955
956 #[inline(always)]
958 pub fn get_regions(&self) -> &[MemoryRegion] {
959 match &self.ty {
960 MemoryMappingType::Aligned(inner) => &inner.regions,
961 MemoryMappingType::Unaligned(inner) => &inner.regions,
962 }
963 }
964
965 pub fn get_regions_mut(&mut self) -> &mut [MemoryRegion] {
971 self.initialized = false;
972
973 let regions = match &mut self.ty {
974 MemoryMappingType::Aligned(inner) => inner.regions.as_mut_slice(),
975 MemoryMappingType::Unaligned(inner) => &mut inner.regions,
976 };
977
978 regions
979 }
980
981 #[inline(always)]
987 pub unsafe fn replace_region(
988 &mut self,
989 index: usize,
990 region: MemoryRegion,
991 ) -> Result<(), EbpfError> {
992 debug_assert!(self.initialized);
993 let regions = self.get_regions();
994 let next_region_start = regions
995 .get(index.saturating_add(1))
996 .map_or(u64::MAX, |next_region| next_region.vm_addr);
997 if index >= regions.len()
998 || regions[index].vm_addr != region.vm_addr
999 || region.vm_addr_range().end > next_region_start
1000 {
1001 return Err(EbpfError::InvalidMemoryRegion(index));
1002 }
1003 match &mut self.ty {
1004 MemoryMappingType::Aligned(inner) => inner.replace_region(index, region),
1005 MemoryMappingType::Unaligned(inner) => inner.replace_region(index, region),
1006 }
1007 }
1008
1009 pub fn initialize(&mut self) -> Result<(), EbpfError> {
1011 let result = match &mut self.ty {
1012 MemoryMappingType::Aligned(inner) => inner.initialize(),
1013 MemoryMappingType::Unaligned(inner) => inner.initialize(),
1014 };
1015 self.initialized = result.is_ok();
1016 result
1017 }
1018
1019 fn generate_access_violation(
1020 &self,
1021 access_type: AccessType,
1022 vm_addr: u64,
1023 len: u64,
1024 ) -> EbpfError {
1025 let stack_frame = (vm_addr as i64)
1026 .saturating_sub(ebpf::MM_STACK_START as i64)
1027 .checked_div(self.stack_frame_size)
1028 .unwrap_or(0);
1029 if !self.sbpf_version.manual_stack_frame_bump()
1030 && (-1..self.max_call_depth.saturating_add(1)).contains(&stack_frame)
1031 {
1032 EbpfError::StackAccessViolation(access_type, vm_addr, len, stack_frame)
1033 } else {
1034 let region = self.find_region(vm_addr);
1035 let region_name = match vm_addr & (!ebpf::MM_BYTECODE_START.saturating_sub(1)) {
1036 _ if region.map(|(_, r)| r.vm_addr_range().contains(&vm_addr)) != Some(true) => {
1037 "unallocated"
1038 }
1039 ebpf::MM_BYTECODE_START => "program",
1040 ebpf::MM_STACK_START => "stack",
1041 ebpf::MM_HEAP_START => "heap",
1042 ebpf::MM_INPUT_START => "input",
1043 _ => "allocated",
1044 };
1045 EbpfError::AccessViolation(access_type, vm_addr, len, region_name)
1046 }
1047 }
1048}
1049
1050#[derive(Debug)]
1052struct MappingCache {
1053 entries: [(Range<u64>, usize); MappingCache::SIZE],
1055 head: usize,
1060}
1061
1062impl MappingCache {
1063 const SIZE: usize = 4;
1065
1066 fn new() -> MappingCache {
1067 MappingCache {
1068 entries: array::from_fn(|_| (0..0, 0)),
1069 head: 0,
1070 }
1071 }
1072
1073 #[inline]
1074 fn find(&self, vm_addr: u64) -> Option<usize> {
1075 for i in 0..Self::SIZE {
1076 let index = self.head.wrapping_add(i) % Self::SIZE;
1077 let (vm_range, region_index) = unsafe { self.entries.get_unchecked(index) };
1080 if vm_range.contains(&vm_addr) {
1081 return Some(*region_index);
1082 }
1083 }
1084
1085 None
1086 }
1087
1088 #[inline]
1089 fn insert(&mut self, vm_range: Range<u64>, region_index: usize) {
1090 self.head = self.head.wrapping_sub(1) % Self::SIZE;
1091 unsafe { *self.entries.get_unchecked_mut(self.head) = (vm_range, region_index) };
1094 }
1095
1096 #[inline]
1097 fn flush(&mut self) {
1098 self.entries = array::from_fn(|_| (0..0, 0));
1099 self.head = 0;
1100 }
1101}
1102
1103#[cfg(test)]
1104mod test {
1105 use std::{cell::RefCell, rc::Rc};
1106 use test_utils::assert_error;
1107
1108 use super::*;
1109
1110 #[test]
1111 fn test_mapping_cache() {
1112 let mut cache = MappingCache::new();
1113 assert_eq!(cache.find(0), None);
1114
1115 let mut ranges = vec![10u64..20, 20..30, 30..40, 40..50];
1116 for (region, range) in ranges.iter().cloned().enumerate() {
1117 cache.insert(range, region);
1118 }
1119 for (region, range) in ranges.iter().enumerate() {
1120 if region > 0 {
1121 assert_eq!(cache.find(range.start - 1), Some(region - 1));
1122 } else {
1123 assert_eq!(cache.find(range.start - 1), None);
1124 }
1125 assert_eq!(cache.find(range.start), Some(region));
1126 assert_eq!(cache.find(range.start + 1), Some(region));
1127 assert_eq!(cache.find(range.end - 1), Some(region));
1128 if region < 3 {
1129 assert_eq!(cache.find(range.end), Some(region + 1));
1130 } else {
1131 assert_eq!(cache.find(range.end), None);
1132 }
1133 }
1134
1135 cache.insert(50..60, 4);
1136 ranges.push(50..60);
1137 for (region, range) in ranges.iter().enumerate() {
1138 if region == 0 {
1139 assert_eq!(cache.find(range.start), None);
1140 continue;
1141 }
1142 if region > 1 {
1143 assert_eq!(cache.find(range.start - 1), Some(region - 1));
1144 } else {
1145 assert_eq!(cache.find(range.start - 1), None);
1146 }
1147 assert_eq!(cache.find(range.start), Some(region));
1148 assert_eq!(cache.find(range.start + 1), Some(region));
1149 assert_eq!(cache.find(range.end - 1), Some(region));
1150 if region < 4 {
1151 assert_eq!(cache.find(range.end), Some(region + 1));
1152 } else {
1153 assert_eq!(cache.find(range.end), None);
1154 }
1155 }
1156 }
1157
1158 #[test]
1159 fn test_mapping_cache_flush() {
1160 let mut cache = MappingCache::new();
1161 assert_eq!(cache.find(0), None);
1162 cache.insert(0..10, 0);
1163 assert_eq!(cache.find(0), Some(0));
1164 cache.flush();
1165 assert_eq!(cache.find(0), None);
1166 }
1167
1168 #[test]
1169 fn test_map_empty() {
1170 for aligned_memory_mapping in [false, true] {
1171 let config = Config {
1172 aligned_memory_mapping,
1173 ..Config::default()
1174 };
1175 let m = unsafe { MemoryMapping::new(vec![], &config, SBPFVersion::V3) }.unwrap();
1176 assert_error!(
1177 m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 8),
1178 "AccessViolation"
1179 );
1180 }
1181 }
1182
1183 #[test]
1184 fn test_gapped_map() {
1185 for aligned_memory_mapping in [false, true] {
1186 let config = Config {
1187 aligned_memory_mapping,
1188 ..Config::default()
1189 };
1190 let mut mem1 = [0xff; 8];
1191 let mem2 = [0; 8];
1192 let mut m = unsafe {
1193 MemoryMapping::new(
1194 vec![
1195 MemoryRegion::new(&raw const mem2[..], ebpf::MM_REGION_SIZE),
1196 MemoryRegion::new_gapped(&raw mut mem1[..], ebpf::MM_REGION_SIZE * 2, 2),
1197 ],
1198 &config,
1199 SBPFVersion::V3,
1200 )
1201 .unwrap()
1202 };
1203 for frame in 0..4 {
1204 let address = ebpf::MM_STACK_START + frame * 4;
1205 assert!(m.find_region(address).is_some());
1206 assert!(m.map(AccessType::Load, address, 2).is_ok());
1207 assert_error!(m.map(AccessType::Load, address + 2, 2), "AccessViolation");
1208 assert_eq!(m.load::<u16>(address).unwrap(), 0xFFFF);
1209 assert_error!(m.load::<u16>(address + 2), "AccessViolation");
1210 assert!(m.store::<u16>(0xFFFF, address).is_ok());
1211 assert_error!(m.store::<u16>(0xFFFF, address + 2), "AccessViolation");
1212 }
1213 }
1214 }
1215
1216 #[test]
1217 fn test_unaligned_map_overlap() {
1218 let config = Config {
1219 aligned_memory_mapping: false,
1220 ..Config::default()
1221 };
1222 let mem1 = [1, 2, 3, 4];
1223 let mem2 = [5, 6];
1224 assert_error!(
1225 unsafe {
1226 MemoryMapping::new(
1227 vec![
1228 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1229 MemoryRegion::new(
1230 &raw const mem2,
1231 ebpf::MM_REGION_SIZE + mem1.len() as u64 - 1,
1232 ),
1233 ],
1234 &config,
1235 SBPFVersion::V3,
1236 )
1237 },
1238 "InvalidMemoryRegion(1)"
1239 );
1240 assert!(unsafe {
1241 MemoryMapping::new(
1242 vec![
1243 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1244 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1245 ],
1246 &config,
1247 SBPFVersion::V3,
1248 )
1249 }
1250 .is_ok());
1251 }
1252
1253 #[test]
1254 fn test_unaligned_map() {
1255 let config = Config {
1256 aligned_memory_mapping: false,
1257 ..Config::default()
1258 };
1259 let mut mem1 = [11];
1260 let mem2 = [22, 22];
1261 let mem3 = [33];
1262 let mem4 = [44, 44];
1263 let m = unsafe {
1264 MemoryMapping::new(
1265 vec![
1266 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1267 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1268 MemoryRegion::new(
1269 &raw const mem3,
1270 ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len()) as u64,
1271 ),
1272 MemoryRegion::new(
1273 &raw const mem4,
1274 ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len() + mem3.len()) as u64,
1275 ),
1276 ],
1277 &config,
1278 SBPFVersion::V3,
1279 )
1280 .unwrap()
1281 };
1282
1283 assert_eq!(
1284 m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1285 .unwrap()
1286 .ptr()
1287 .addr(),
1288 mem1.as_ptr().addr()
1289 );
1290
1291 assert_eq!(
1292 m.map(AccessType::Store, ebpf::MM_REGION_SIZE, 1)
1293 .unwrap()
1294 .ptr()
1295 .addr(),
1296 mem1.as_ptr().addr()
1297 );
1298
1299 assert_error!(
1300 m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 2),
1301 "AccessViolation"
1302 );
1303
1304 assert_eq!(
1305 m.map(
1306 AccessType::Load,
1307 ebpf::MM_REGION_SIZE + mem1.len() as u64,
1308 1,
1309 )
1310 .unwrap()
1311 .ptr()
1312 .addr(),
1313 mem2.as_ptr().addr()
1314 );
1315
1316 assert_eq!(
1317 m.map(
1318 AccessType::Load,
1319 ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len()) as u64,
1320 1,
1321 )
1322 .unwrap()
1323 .ptr()
1324 .addr(),
1325 mem3.as_ptr().addr()
1326 );
1327
1328 assert_eq!(
1329 m.map(
1330 AccessType::Load,
1331 ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len() + mem3.len()) as u64,
1332 1,
1333 )
1334 .unwrap()
1335 .ptr()
1336 .addr(),
1337 mem4.as_ptr().addr()
1338 );
1339
1340 assert_error!(
1341 m.map(
1342 AccessType::Load,
1343 ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len() + mem3.len() + mem4.len()) as u64,
1344 1,
1345 ),
1346 "AccessViolation"
1347 );
1348 }
1349
1350 #[test]
1351 fn test_unaligned_region() {
1352 let config = Config {
1353 aligned_memory_mapping: false,
1354 ..Config::default()
1355 };
1356
1357 let mut mem1 = [0xFF; 4];
1358 let mem2 = [0xDD; 4];
1359 let m = unsafe {
1360 MemoryMapping::new(
1361 vec![
1362 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1363 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + 4),
1364 ],
1365 &config,
1366 SBPFVersion::V3,
1367 )
1368 .unwrap()
1369 };
1370 assert!(m.find_region(ebpf::MM_REGION_SIZE - 1).is_none());
1371 assert_eq!(
1372 HostBuffer::Mutable(&raw mut mem1[..]),
1373 m.find_region(ebpf::MM_REGION_SIZE).unwrap().1.host,
1374 );
1375 assert_eq!(
1376 HostBuffer::Mutable(&raw mut mem1[..]),
1377 m.find_region(ebpf::MM_REGION_SIZE + 3).unwrap().1.host,
1378 );
1379 assert_eq!(
1380 HostBuffer::Immutable(&raw const mem2[..]),
1381 m.find_region(ebpf::MM_REGION_SIZE + 4).unwrap().1.host,
1382 );
1383 assert_eq!(
1384 HostBuffer::Immutable(&raw const mem2[..]),
1385 m.find_region(ebpf::MM_REGION_SIZE + 7).unwrap().1.host,
1386 );
1387 assert!(m.find_region(ebpf::MM_REGION_SIZE + 8).is_some());
1388 }
1389
1390 #[test]
1391 fn test_aligned_region() {
1392 let config = Config {
1393 aligned_memory_mapping: true,
1394 ..Config::default()
1395 };
1396
1397 let mut mem1 = [0xFF; 4];
1398 let mem2 = [0xDD; 4];
1399 let m = unsafe {
1400 MemoryMapping::new(
1401 vec![
1402 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1403 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE * 2),
1404 ],
1405 &config,
1406 SBPFVersion::V4,
1407 )
1408 .unwrap()
1409 };
1410 assert_eq!(m.find_region(ebpf::MM_REGION_SIZE - 1).unwrap().1.len(), 0);
1411 assert_eq!(
1412 HostBuffer::Mutable(&raw mut mem1[..]),
1413 m.find_region(ebpf::MM_REGION_SIZE).unwrap().1.host,
1414 );
1415 assert_eq!(
1416 HostBuffer::Mutable(&raw mut mem1[..]),
1417 m.find_region(ebpf::MM_REGION_SIZE + 3).unwrap().1.host,
1418 );
1419 assert!(m.find_region(ebpf::MM_REGION_SIZE + 4).is_some());
1420 assert_eq!(
1421 HostBuffer::Immutable(&raw const mem2[..]),
1422 m.find_region(ebpf::MM_REGION_SIZE * 2).unwrap().1.host,
1423 );
1424 assert_eq!(
1425 HostBuffer::Immutable(&raw const mem2[..]),
1426 m.find_region(ebpf::MM_REGION_SIZE * 2 + 3).unwrap().1.host,
1427 );
1428 assert!(m.find_region(ebpf::MM_REGION_SIZE * 3 + 4).is_none());
1429 }
1430
1431 #[test]
1432 fn test_unaligned_map_load() {
1433 let config = Config {
1434 aligned_memory_mapping: false,
1435 ..Config::default()
1436 };
1437 let mem1 = [0x11, 0x22];
1438 let mem2 = [0x33];
1439 let mut m = unsafe {
1440 MemoryMapping::new(
1441 vec![
1442 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1443 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1444 ],
1445 &config,
1446 SBPFVersion::V3,
1447 )
1448 .unwrap()
1449 };
1450
1451 assert_eq!(m.load::<u16>(ebpf::MM_REGION_SIZE).unwrap(), 0x2211);
1452 assert_error!(m.load::<u32>(ebpf::MM_REGION_SIZE), "AccessViolation");
1453 assert_error!(m.load::<u32>(ebpf::MM_REGION_SIZE + 4), "AccessViolation");
1454 }
1455
1456 #[test]
1457 fn test_unaligned_map_store() {
1458 let config = Config {
1459 aligned_memory_mapping: false,
1460 ..Config::default()
1461 };
1462 let mut mem1 = [0xff, 0xff];
1463 let mut mem2 = [0xff];
1464 let mut m = unsafe {
1465 MemoryMapping::new(
1466 vec![
1467 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1468 MemoryRegion::new(&raw mut mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1469 ],
1470 &config,
1471 SBPFVersion::V3,
1472 )
1473 .unwrap()
1474 };
1475
1476 m.store(0x1122u16, ebpf::MM_REGION_SIZE).unwrap();
1477 assert_eq!(m.load::<u16>(ebpf::MM_REGION_SIZE).unwrap(), 0x1122);
1478
1479 assert_error!(
1480 m.store(0x33445566u32, ebpf::MM_REGION_SIZE),
1481 "AccessViolation"
1482 );
1483 }
1484
1485 #[test]
1486 fn test_unaligned_map_store_out_of_bounds() {
1487 let config = Config {
1488 aligned_memory_mapping: false,
1489 ..Config::default()
1490 };
1491
1492 let mut mem1 = [0xFF];
1493 let mut m = unsafe {
1494 MemoryMapping::new(
1495 vec![MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE)],
1496 &config,
1497 SBPFVersion::V3,
1498 )
1499 .unwrap()
1500 };
1501 m.store(0x11u8, ebpf::MM_REGION_SIZE).unwrap();
1502 assert_error!(m.store(0x11u8, ebpf::MM_REGION_SIZE - 1), "AccessViolation");
1503 assert_error!(m.store(0x11u8, ebpf::MM_REGION_SIZE + 1), "AccessViolation");
1504 assert_error!(m.store(0x11u8, ebpf::MM_REGION_SIZE + 2), "AccessViolation");
1507
1508 let mut mem1 = [0xFF; 4];
1509 let mut mem2 = [0xDD; 4];
1510 let mut m = unsafe {
1511 MemoryMapping::new(
1512 vec![
1513 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1514 MemoryRegion::new(&raw mut mem2, ebpf::MM_REGION_SIZE + 4),
1515 ],
1516 &config,
1517 SBPFVersion::V3,
1518 )
1519 .unwrap()
1520 };
1521 assert_error!(
1522 m.store(0x1122334455667788u64, ebpf::MM_REGION_SIZE),
1523 "AccessViolation"
1524 );
1525 }
1526
1527 #[test]
1528 fn test_unaligned_map_load_out_of_bounds() {
1529 let config = Config {
1530 aligned_memory_mapping: false,
1531 ..Config::default()
1532 };
1533
1534 let mem1 = [0xff];
1535 let mut m = unsafe {
1536 MemoryMapping::new(
1537 vec![MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE)],
1538 &config,
1539 SBPFVersion::V3,
1540 )
1541 .unwrap()
1542 };
1543 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 0xff);
1544 assert_error!(m.load::<u8>(ebpf::MM_REGION_SIZE - 1), "AccessViolation");
1545 assert_error!(m.load::<u8>(ebpf::MM_REGION_SIZE + 1), "AccessViolation");
1546 assert_error!(m.load::<u8>(ebpf::MM_REGION_SIZE + 2), "AccessViolation");
1547
1548 let mem1 = [0xFF; 4];
1549 let mem2 = [0xDD; 4];
1550 let mut m = unsafe {
1551 MemoryMapping::new(
1552 vec![
1553 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1554 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + 4),
1555 ],
1556 &config,
1557 SBPFVersion::V3,
1558 )
1559 .unwrap()
1560 };
1561 assert_error!(m.load::<u64>(ebpf::MM_REGION_SIZE), "AccessViolation");
1562 }
1563
1564 #[test]
1565 #[should_panic(expected = "AccessViolation")]
1566 fn test_store_readonly() {
1567 let config = Config {
1568 aligned_memory_mapping: false,
1569 ..Config::default()
1570 };
1571 let mut mem1 = [0xff, 0xff];
1572 let mem2 = [0xff, 0xff];
1573 let mut m = unsafe {
1574 MemoryMapping::new(
1575 vec![
1576 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1577 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1578 ],
1579 &config,
1580 SBPFVersion::V3,
1581 )
1582 .unwrap()
1583 };
1584 m.store(0x11223344, ebpf::MM_REGION_SIZE).unwrap();
1585 }
1586
1587 #[test]
1588 fn test_unaligned_map_replace_region() {
1589 let config = Config {
1590 aligned_memory_mapping: false,
1591 ..Config::default()
1592 };
1593 let mem1 = [11];
1594 let mem2 = [22, 22];
1595 let mem3 = [33];
1596 let mut m = unsafe {
1597 MemoryMapping::new(
1598 vec![
1599 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1600 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1601 ],
1602 &config,
1603 SBPFVersion::V3,
1604 )
1605 .unwrap()
1606 };
1607
1608 assert_eq!(
1609 m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1610 .unwrap()
1611 .ptr()
1612 .addr(),
1613 mem1.as_ptr().addr()
1614 );
1615
1616 assert_eq!(
1617 m.map(
1618 AccessType::Load,
1619 ebpf::MM_REGION_SIZE + mem1.len() as u64,
1620 1,
1621 )
1622 .unwrap()
1623 .ptr()
1624 .addr(),
1625 mem2.as_ptr().addr()
1626 );
1627
1628 assert_error!(
1629 unsafe {
1630 m.replace_region(
1631 2,
1632 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1633 )
1634 },
1635 "InvalidMemoryRegion(2)"
1636 );
1637
1638 let region_index = m
1639 .get_regions()
1640 .iter()
1641 .position(|mem| mem.vm_addr == ebpf::MM_REGION_SIZE + mem1.len() as u64)
1642 .unwrap();
1643
1644 assert_error!(
1646 unsafe {
1647 m.replace_region(
1648 region_index,
1649 MemoryRegion::new(
1650 &raw const mem3,
1651 ebpf::MM_REGION_SIZE + mem1.len() as u64 + 1,
1652 ),
1653 )
1654 },
1655 "InvalidMemoryRegion({})",
1656 region_index
1657 );
1658
1659 unsafe {
1660 m.replace_region(
1661 region_index,
1662 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1663 )
1664 .unwrap()
1665 };
1666
1667 assert_eq!(
1668 m.map(
1669 AccessType::Load,
1670 ebpf::MM_REGION_SIZE + mem1.len() as u64,
1671 1,
1672 )
1673 .unwrap()
1674 .ptr()
1675 .addr(),
1676 mem3.as_ptr().addr()
1677 );
1678 }
1679
1680 #[test]
1681 fn test_aligned_map_replace_region() {
1682 let config = Config {
1683 aligned_memory_mapping: true,
1684 ..Config::default()
1685 };
1686 let mem1 = [11];
1687 let mem2 = [22, 22];
1688 let mem3 = [33, 33];
1689 let mut m = unsafe {
1690 MemoryMapping::new(
1691 vec![
1692 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1693 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE * 2),
1694 ],
1695 &config,
1696 SBPFVersion::V4,
1697 )
1698 .unwrap()
1699 };
1700
1701 assert_eq!(
1702 m.map(AccessType::Load, ebpf::MM_REGION_SIZE * 2, 1)
1703 .unwrap()
1704 .ptr()
1705 .addr(),
1706 mem2.as_ptr().addr()
1707 );
1708
1709 assert_error!(
1711 unsafe {
1712 m.replace_region(
1713 3,
1714 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 2),
1715 )
1716 },
1717 "InvalidMemoryRegion(3)"
1718 );
1719
1720 assert_error!(
1722 unsafe {
1723 m.replace_region(
1724 2,
1725 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 3),
1726 )
1727 },
1728 "InvalidMemoryRegion(2)"
1729 );
1730
1731 assert_error!(
1733 unsafe {
1734 m.replace_region(
1735 2,
1736 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 3 - 1),
1737 )
1738 },
1739 "InvalidMemoryRegion(2)"
1740 );
1741
1742 unsafe {
1743 m.replace_region(
1744 2,
1745 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 2),
1746 )
1747 .unwrap()
1748 };
1749
1750 assert_eq!(
1751 m.map(AccessType::Load, ebpf::MM_REGION_SIZE * 2, 1)
1752 .unwrap()
1753 .ptr()
1754 .addr(),
1755 mem3.as_ptr().addr()
1756 );
1757 }
1758
1759 #[test]
1760 fn test_access_violation_handler_map() {
1761 for aligned_memory_mapping in [true, false] {
1762 let config = Config {
1763 aligned_memory_mapping,
1764 ..Config::default()
1765 };
1766 let original = [11, 22];
1767 let copied = Rc::new(RefCell::new(Vec::new()));
1768 let mut regions = vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)];
1769 regions[0].access_violation_handler_payload = Some(0);
1770
1771 let c = Rc::clone(&copied);
1772 let mut m = unsafe {
1773 MemoryMapping::new_with_access_violation_handler(
1774 regions,
1775 &config,
1776 SBPFVersion::V3,
1777 Box::new(move |region, _, _, _, _| {
1778 let mut vec = c.borrow_mut();
1779 vec.extend_from_slice(&original);
1780 region.redirect(&raw mut vec[..]);
1781 }),
1782 )
1783 .unwrap()
1784 };
1785
1786 assert_eq!(
1787 m.map_with_access_violation_handler(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1788 .unwrap()
1789 .ptr()
1790 .addr(),
1791 original.as_ptr().addr()
1792 );
1793 assert_eq!(
1794 m.map_with_access_violation_handler(AccessType::Store, ebpf::MM_REGION_SIZE, 1)
1795 .unwrap()
1796 .ptr()
1797 .addr(),
1798 copied.borrow().as_ptr().addr()
1799 );
1800 }
1801 }
1802
1803 #[test]
1804 fn test_access_violation_handler_load_store() {
1805 for aligned_memory_mapping in [true, false] {
1806 let config = Config {
1807 aligned_memory_mapping,
1808 ..Config::default()
1809 };
1810 let original = [11, 22];
1811 let copied = Rc::new(RefCell::new(Vec::new()));
1812 let mut regions = vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)];
1813 regions[0].access_violation_handler_payload = Some(0);
1814
1815 let c = Rc::clone(&copied);
1816 let mut m = unsafe {
1817 MemoryMapping::new_with_access_violation_handler(
1818 regions,
1819 &config,
1820 SBPFVersion::V3,
1821 Box::new(move |region, _, _, _, _| {
1822 let mut vec = c.borrow_mut();
1823 vec.extend_from_slice(&original);
1824 region.redirect(&raw mut vec[..]);
1825 }),
1826 )
1827 .unwrap()
1828 };
1829
1830 assert_eq!(
1831 m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1832 .unwrap()
1833 .ptr()
1834 .addr(),
1835 original.as_ptr().addr()
1836 );
1837
1838 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 11);
1839 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE + 1).unwrap(), 22);
1840 assert!(copied.borrow().is_empty());
1841
1842 m.store(33u8, ebpf::MM_REGION_SIZE).unwrap();
1843 assert_eq!(original[0], 11);
1844 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 33);
1845 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE + 1).unwrap(), 22);
1846 }
1847 }
1848
1849 #[test]
1850 fn test_access_violation_handler_region_id() {
1851 for aligned_memory_mapping in [true, false] {
1852 let config = Config {
1853 aligned_memory_mapping,
1854 ..Config::default()
1855 };
1856 let original1 = [11, 22];
1857 let original2 = [33, 44];
1858 let copied = Rc::new(RefCell::new(Vec::new()));
1859
1860 let mut regions = vec![
1861 MemoryRegion::new(&raw const original1, ebpf::MM_REGION_SIZE),
1862 MemoryRegion::new(&raw const original2, ebpf::MM_REGION_SIZE * 2),
1863 ];
1864 regions[0].access_violation_handler_payload = Some(42);
1865
1866 let c = Rc::clone(&copied);
1867 let mut m = unsafe {
1868 MemoryMapping::new_with_access_violation_handler(
1869 regions,
1870 &config,
1871 SBPFVersion::V3,
1872 Box::new(move |region, _, _, _, _| {
1873 assert_eq!(region.access_violation_handler_payload, Some(42));
1876 let mut vec = c.borrow_mut();
1877 vec.extend_from_slice(&original1);
1878 region.redirect(&raw mut vec[..]);
1879 }),
1880 )
1881 .unwrap()
1882 };
1883
1884 m.store(55u8, ebpf::MM_REGION_SIZE).unwrap();
1885 assert_eq!(original1[0], 11);
1886 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 55);
1887 }
1888 }
1889
1890 #[test]
1891 #[should_panic(expected = "AccessViolation")]
1892 fn test_map_access_violation_handler_error() {
1893 let config = Config::default();
1894 let original = [11, 22];
1895
1896 let m = unsafe {
1897 MemoryMapping::new_with_access_violation_handler(
1898 vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)],
1899 &config,
1900 SBPFVersion::V4,
1901 Box::new(default_access_violation_handler),
1902 )
1903 .unwrap()
1904 };
1905
1906 m.map(AccessType::Store, ebpf::MM_REGION_SIZE, 1).unwrap();
1907 }
1908
1909 #[test]
1910 #[should_panic(expected = "AccessViolation")]
1911 fn test_store_access_violation_handler_error() {
1912 let config = Config::default();
1913 let original = [11, 22];
1914
1915 let mut m = unsafe {
1916 MemoryMapping::new_with_access_violation_handler(
1917 vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)],
1918 &config,
1919 SBPFVersion::V4,
1920 Box::new(default_access_violation_handler),
1921 )
1922 .unwrap()
1923 };
1924
1925 m.store(33u8, ebpf::MM_REGION_SIZE).unwrap();
1926 }
1927
1928 #[test]
1929 fn test_access_violation_region_identification() {
1930 let config = Config::default();
1931 let original = [11, 22];
1932 let region = 0x10_0000_0000;
1933 let mut m = unsafe {
1934 MemoryMapping::new(
1935 vec![MemoryRegion::new(&raw const original, region)],
1936 &config,
1937 SBPFVersion::V4,
1938 )
1939 .unwrap()
1940 };
1941 let store_err_inbound = m.store(33u8, region).unwrap_err();
1942 assert_eq!(
1943 store_err_inbound.to_string(),
1944 "Access violation writing 1 bytes at address 0x1000000000 (in allocated region)"
1945 );
1946 let store_err_oob = m.load::<u64>(region + 3).unwrap_err();
1947 assert_eq!(
1948 store_err_oob.to_string(),
1949 "Access violation reading 8 bytes at address 0x1000000003 (in unallocated region)"
1950 );
1951 }
1952
1953 #[test]
1954 fn v4_aligned_mapping() {
1955 let config = Config {
1956 aligned_memory_mapping: false,
1957 ..Config::default()
1958 };
1959
1960 let mem = [11, 12];
1961 let mapping = unsafe {
1962 MemoryMapping::new_with_access_violation_handler(
1963 vec![MemoryRegion::new(&raw const mem, ebpf::MM_REGION_SIZE)],
1964 &config,
1965 SBPFVersion::V4,
1966 Box::new(default_access_violation_handler),
1967 )
1968 .unwrap()
1969 };
1970
1971 assert!(matches!(mapping.ty, MemoryMappingType::Aligned(_)));
1972 }
1973}