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}
598
599impl AlignedMemoryMapping {
600 pub unsafe fn new(regions: Vec<MemoryRegion>) -> Result<Self, EbpfError> {
606 let mut mapping = Self::new_uninitialized(regions);
607 mapping.initialize()?;
608 Ok(mapping)
609 }
610
611 pub unsafe fn new_uninitialized(regions: Vec<MemoryRegion>) -> Self {
617 Self { regions }
618 }
619
620 pub fn initialize(&mut self) -> Result<(), EbpfError> {
622 static EMPTY_SLICE: &[u8] = &[];
623 self.regions.sort();
624 let mut expected_region_index = 0;
625 while expected_region_index < self.regions.len() {
626 let actual_region_index = self
627 .regions
628 .get(expected_region_index)
629 .unwrap()
630 .vm_addr
631 .checked_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32)
632 .unwrap_or(0) as usize;
633 if actual_region_index > expected_region_index {
634 self.regions.insert(
635 expected_region_index,
636 MemoryRegion::new(
637 &raw const *EMPTY_SLICE,
638 (expected_region_index as u64).saturating_mul(ebpf::MM_REGION_SIZE),
639 ),
640 );
641 } else if actual_region_index < expected_region_index {
642 return Err(EbpfError::InvalidMemoryRegion(actual_region_index));
643 }
644 expected_region_index = expected_region_index.saturating_add(1);
645 }
646
647 Ok(())
648 }
649
650 #[inline(always)]
652 pub fn find_region(&self, vm_addr: u64) -> Option<(usize, &MemoryRegion)> {
653 let index = vm_addr.wrapping_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32) as usize;
654 if index < self.regions.len() {
655 let region = unsafe { self.regions.get_unchecked(index) };
657 return Some((index, region));
658 }
659 None
660 }
661
662 #[inline(always)]
668 pub unsafe fn replace_region(
669 &mut self,
670 index: usize,
671 region: MemoryRegion,
672 ) -> Result<(), EbpfError> {
673 let begin_index = region
674 .vm_addr
675 .checked_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32)
676 .unwrap_or(0) as usize;
677 let end_index = region
678 .vm_addr
679 .saturating_add((region.len() as u64).saturating_sub(1))
680 .checked_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32)
681 .unwrap_or(0) as usize;
682 if begin_index != index || end_index != index {
683 return Err(EbpfError::InvalidMemoryRegion(index));
684 }
685 self.regions[index] = region;
686 Ok(())
687 }
688}
689
690pub struct MemoryMapping {
692 access_violation_handler: AccessViolationHandler,
694 max_call_depth: i64,
695 stack_frame_size: i64,
696 disable_address_translation: bool,
697 sbpf_version: SBPFVersion,
699 initialized: bool,
700 ty: MemoryMappingType,
701}
702
703impl fmt::Debug for MemoryMapping {
704 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
705 f.debug_struct("MemoryMapping")
706 .field("max_call_depth", &self.max_call_depth)
707 .field("stack_frame_size", &self.stack_frame_size)
708 .field("sbpf_version", &self.sbpf_version)
709 .field("ty", &self.ty)
710 .finish()
711 }
712}
713
714#[derive(Debug)]
716pub enum MemoryMappingType {
717 Aligned(AlignedMemoryMapping),
720 Unaligned(UnalignedMemoryMapping),
722}
723
724impl MemoryMapping {
725 pub unsafe fn new_with_access_violation_handler(
737 regions: Vec<MemoryRegion>,
738 config: &Config,
739 sbpf_version: SBPFVersion,
740 access_violation_handler: AccessViolationHandler,
741 ) -> Result<Self, EbpfError> {
742 let mut mapping =
743 Self::new_uninitialized(regions, config, sbpf_version, access_violation_handler);
744 mapping.initialize()?;
745 Ok(mapping)
746 }
747
748 pub unsafe fn new_uninitialized(
759 regions: Vec<MemoryRegion>,
760 config: &Config,
761 sbpf_version: SBPFVersion,
762 access_violation_handler: AccessViolationHandler,
763 ) -> Self {
764 let ty = if sbpf_version >= SBPFVersion::V4 || config.aligned_memory_mapping {
765 MemoryMappingType::Aligned(AlignedMemoryMapping::new_uninitialized(regions))
766 } else {
767 debug_assert!(
768 sbpf_version <= SBPFVersion::V3,
769 "SBPFv4 and later versions do not support unaligned memory"
770 );
771 MemoryMappingType::Unaligned(UnalignedMemoryMapping::new_uninitialized(regions))
772 };
773
774 Self {
775 access_violation_handler: Box::new(access_violation_handler),
776 max_call_depth: config.max_call_depth as i64,
777 stack_frame_size: config.stack_frame_size as i64,
778 disable_address_translation: !config.enable_address_translation,
779 sbpf_version,
780 initialized: false,
781 ty,
782 }
783 }
784
785 pub unsafe fn new(
793 regions: Vec<MemoryRegion>,
794 config: &Config,
795 sbpf_version: SBPFVersion,
796 ) -> Result<Self, EbpfError> {
797 Self::new_with_access_violation_handler(
798 regions,
799 config,
800 sbpf_version,
801 Box::new(default_access_violation_handler),
802 )
803 }
804
805 pub fn map(
807 &self,
808 access_type: AccessType,
809 vm_addr: u64,
810 len: u64,
811 ) -> StableResult<HostBuffer, EbpfError> {
812 debug_assert!(self.initialized);
813 if self.disable_address_translation {
814 let ptr = ptr::with_exposed_provenance_mut(vm_addr as usize);
820 let buffer = HostBuffer::Mutable(ptr::slice_from_raw_parts_mut(ptr, len as usize));
821 return StableResult::Ok(buffer);
822 }
823 if let Some((_index, region)) = self.find_region(vm_addr) {
824 if region.host_buffer().is_mutable() || access_type != AccessType::Store {
825 if let Some(host_buffer) = region.vm_to_host_buffer(vm_addr, len) {
826 return StableResult::Ok(host_buffer);
827 }
828 }
829 }
830 StableResult::Err(self.generate_access_violation(access_type, vm_addr, len))
831 }
832
833 #[inline(always)]
838 pub fn map_with_access_violation_handler(
839 &mut self,
840 access_type: AccessType,
841 vm_addr: u64,
842 len: u64,
843 ) -> StableResult<HostBuffer, EbpfError> {
844 debug_assert!(self.initialized);
845 if self.disable_address_translation {
846 let ptr = ptr::with_exposed_provenance_mut(vm_addr as usize);
852 let buffer = HostBuffer::Mutable(ptr::slice_from_raw_parts_mut(ptr, len as usize));
853 return StableResult::Ok(buffer);
854 }
855
856 if let Some((index, region)) = self.find_region(vm_addr) {
857 if region.host_buffer().is_mutable() || access_type != AccessType::Store {
858 if let Some(host_buffer) = region.vm_to_host_buffer(vm_addr, len) {
859 return StableResult::Ok(host_buffer);
860 }
861 }
862 let mut region = (*region).clone();
863 let max_len = self
864 .get_regions()
865 .get(index.saturating_add(1))
866 .map_or(u64::MAX, |next_region| next_region.vm_addr)
867 .saturating_sub(region.vm_addr);
868 (self.access_violation_handler)(&mut region, max_len, access_type, vm_addr, len);
869 if region.host_buffer().is_mutable() || access_type != AccessType::Store {
870 if let Some(host_buffer) = region.vm_to_host_buffer(vm_addr, len) {
871 if let Err(err) = unsafe { self.replace_region(index, region) } {
872 return StableResult::Err(err);
873 }
874 return StableResult::Ok(host_buffer);
875 }
876 }
877 }
878 StableResult::Err(self.generate_access_violation(access_type, vm_addr, len))
879 }
880
881 pub fn load<T: Pod + Into<u64>>(&mut self, vm_addr: u64) -> ProgramResult {
883 let len = mem::size_of::<T>() as u64;
884 debug_assert!(len <= mem::size_of::<u64>() as u64);
885 debug_assert!(self.initialized);
886 let ptr = match self.map_with_access_violation_handler(AccessType::Load, vm_addr, len) {
887 StableResult::Err(e) => return ProgramResult::Err(e),
888 StableResult::Ok(buf) => buf.ptr(),
889 };
890 ProgramResult::Ok(unsafe {
891 ptr::read_unaligned::<T>(ptr.cast()).into()
900 })
901 }
902
903 pub fn store<T: Pod>(&mut self, value: T, vm_addr: u64) -> ProgramResult {
905 let len = mem::size_of::<T>() as u64;
906 debug_assert!(len <= mem::size_of::<u64>() as u64);
907 debug_assert!(self.initialized);
908 let ptr = match self.map_with_access_violation_handler(AccessType::Store, vm_addr, len) {
909 StableResult::Err(e) => return ProgramResult::Err(e),
910 StableResult::Ok(buf) => buf.ptr_mut(),
911 };
912 StableResult::Ok(unsafe {
913 ptr::write_unaligned::<T>(ptr.cast(), value);
922 0
923 })
924 }
925
926 #[inline(always)]
928 pub fn find_region(&self, vm_addr: u64) -> Option<(usize, &MemoryRegion)> {
929 debug_assert!(self.initialized);
930 match &self.ty {
931 MemoryMappingType::Aligned(inner) => inner.find_region(vm_addr),
932 MemoryMappingType::Unaligned(inner) => inner.find_region(vm_addr),
933 }
934 }
935
936 #[inline(always)]
938 pub fn get_regions(&self) -> &[MemoryRegion] {
939 match &self.ty {
940 MemoryMappingType::Aligned(inner) => &inner.regions,
941 MemoryMappingType::Unaligned(inner) => &inner.regions,
942 }
943 }
944
945 pub fn get_regions_mut(&mut self) -> &mut [MemoryRegion] {
951 self.initialized = false;
952
953 let regions = match &mut self.ty {
954 MemoryMappingType::Aligned(inner) => inner.regions.as_mut_slice(),
955 MemoryMappingType::Unaligned(inner) => &mut inner.regions,
956 };
957
958 regions
959 }
960
961 #[inline(always)]
967 pub unsafe fn replace_region(
968 &mut self,
969 index: usize,
970 region: MemoryRegion,
971 ) -> Result<(), EbpfError> {
972 debug_assert!(self.initialized);
973 let regions = self.get_regions();
974 let next_region_start = regions
975 .get(index.saturating_add(1))
976 .map_or(u64::MAX, |next_region| next_region.vm_addr);
977 if index >= regions.len()
978 || regions[index].vm_addr != region.vm_addr
979 || region.vm_addr_range().end > next_region_start
980 {
981 return Err(EbpfError::InvalidMemoryRegion(index));
982 }
983 match &mut self.ty {
984 MemoryMappingType::Aligned(inner) => inner.replace_region(index, region),
985 MemoryMappingType::Unaligned(inner) => inner.replace_region(index, region),
986 }
987 }
988
989 pub fn initialize(&mut self) -> Result<(), EbpfError> {
991 let result = match &mut self.ty {
992 MemoryMappingType::Aligned(inner) => inner.initialize(),
993 MemoryMappingType::Unaligned(inner) => inner.initialize(),
994 };
995 self.initialized = result.is_ok();
996 result
997 }
998
999 fn generate_access_violation(
1000 &self,
1001 access_type: AccessType,
1002 vm_addr: u64,
1003 len: u64,
1004 ) -> EbpfError {
1005 let stack_frame = (vm_addr as i64)
1006 .saturating_sub(ebpf::MM_STACK_START as i64)
1007 .checked_div(self.stack_frame_size)
1008 .unwrap_or(0);
1009 if !self.sbpf_version.manual_stack_frame_bump()
1010 && (-1..self.max_call_depth.saturating_add(1)).contains(&stack_frame)
1011 {
1012 EbpfError::StackAccessViolation(access_type, vm_addr, len, stack_frame)
1013 } else {
1014 let region = self.find_region(vm_addr);
1015 let region_name = match vm_addr & (!ebpf::MM_BYTECODE_START.saturating_sub(1)) {
1016 _ if region.map(|(_, r)| r.vm_addr_range().contains(&vm_addr)) != Some(true) => {
1017 "unallocated"
1018 }
1019 ebpf::MM_BYTECODE_START => "program",
1020 ebpf::MM_STACK_START => "stack",
1021 ebpf::MM_HEAP_START => "heap",
1022 ebpf::MM_INPUT_START => "input",
1023 _ => "allocated",
1024 };
1025 EbpfError::AccessViolation(access_type, vm_addr, len, region_name)
1026 }
1027 }
1028}
1029
1030#[derive(Debug)]
1032struct MappingCache {
1033 entries: [(Range<u64>, usize); MappingCache::SIZE],
1035 head: usize,
1040}
1041
1042impl MappingCache {
1043 const SIZE: usize = 4;
1045
1046 fn new() -> MappingCache {
1047 MappingCache {
1048 entries: array::from_fn(|_| (0..0, 0)),
1049 head: 0,
1050 }
1051 }
1052
1053 #[inline]
1054 fn find(&self, vm_addr: u64) -> Option<usize> {
1055 for i in 0..Self::SIZE {
1056 let index = self.head.wrapping_add(i) % Self::SIZE;
1057 let (vm_range, region_index) = unsafe { self.entries.get_unchecked(index) };
1060 if vm_range.contains(&vm_addr) {
1061 return Some(*region_index);
1062 }
1063 }
1064
1065 None
1066 }
1067
1068 #[inline]
1069 fn insert(&mut self, vm_range: Range<u64>, region_index: usize) {
1070 self.head = self.head.wrapping_sub(1) % Self::SIZE;
1071 unsafe { *self.entries.get_unchecked_mut(self.head) = (vm_range, region_index) };
1074 }
1075
1076 #[inline]
1077 fn flush(&mut self) {
1078 self.entries = array::from_fn(|_| (0..0, 0));
1079 self.head = 0;
1080 }
1081}
1082
1083#[cfg(test)]
1084mod test {
1085 use std::{cell::RefCell, rc::Rc};
1086 use test_utils::assert_error;
1087
1088 use super::*;
1089
1090 #[test]
1091 fn test_mapping_cache() {
1092 let mut cache = MappingCache::new();
1093 assert_eq!(cache.find(0), None);
1094
1095 let mut ranges = vec![10u64..20, 20..30, 30..40, 40..50];
1096 for (region, range) in ranges.iter().cloned().enumerate() {
1097 cache.insert(range, region);
1098 }
1099 for (region, range) in ranges.iter().enumerate() {
1100 if region > 0 {
1101 assert_eq!(cache.find(range.start - 1), Some(region - 1));
1102 } else {
1103 assert_eq!(cache.find(range.start - 1), None);
1104 }
1105 assert_eq!(cache.find(range.start), Some(region));
1106 assert_eq!(cache.find(range.start + 1), Some(region));
1107 assert_eq!(cache.find(range.end - 1), Some(region));
1108 if region < 3 {
1109 assert_eq!(cache.find(range.end), Some(region + 1));
1110 } else {
1111 assert_eq!(cache.find(range.end), None);
1112 }
1113 }
1114
1115 cache.insert(50..60, 4);
1116 ranges.push(50..60);
1117 for (region, range) in ranges.iter().enumerate() {
1118 if region == 0 {
1119 assert_eq!(cache.find(range.start), None);
1120 continue;
1121 }
1122 if region > 1 {
1123 assert_eq!(cache.find(range.start - 1), Some(region - 1));
1124 } else {
1125 assert_eq!(cache.find(range.start - 1), None);
1126 }
1127 assert_eq!(cache.find(range.start), Some(region));
1128 assert_eq!(cache.find(range.start + 1), Some(region));
1129 assert_eq!(cache.find(range.end - 1), Some(region));
1130 if region < 4 {
1131 assert_eq!(cache.find(range.end), Some(region + 1));
1132 } else {
1133 assert_eq!(cache.find(range.end), None);
1134 }
1135 }
1136 }
1137
1138 #[test]
1139 fn test_mapping_cache_flush() {
1140 let mut cache = MappingCache::new();
1141 assert_eq!(cache.find(0), None);
1142 cache.insert(0..10, 0);
1143 assert_eq!(cache.find(0), Some(0));
1144 cache.flush();
1145 assert_eq!(cache.find(0), None);
1146 }
1147
1148 #[test]
1149 fn test_map_empty() {
1150 for aligned_memory_mapping in [false, true] {
1151 let config = Config {
1152 aligned_memory_mapping,
1153 ..Config::default()
1154 };
1155 let m = unsafe { MemoryMapping::new(vec![], &config, SBPFVersion::V3) }.unwrap();
1156 assert_error!(
1157 m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 8),
1158 "AccessViolation"
1159 );
1160 }
1161 }
1162
1163 #[test]
1164 fn test_gapped_map() {
1165 for aligned_memory_mapping in [false, true] {
1166 let config = Config {
1167 aligned_memory_mapping,
1168 ..Config::default()
1169 };
1170 let mut mem1 = [0xff; 8];
1171 let mem2 = [0; 8];
1172 let mut m = unsafe {
1173 MemoryMapping::new(
1174 vec![
1175 MemoryRegion::new(&raw const mem2[..], ebpf::MM_REGION_SIZE),
1176 MemoryRegion::new_gapped(&raw mut mem1[..], ebpf::MM_REGION_SIZE * 2, 2),
1177 ],
1178 &config,
1179 SBPFVersion::V3,
1180 )
1181 .unwrap()
1182 };
1183 for frame in 0..4 {
1184 let address = ebpf::MM_STACK_START + frame * 4;
1185 assert!(m.find_region(address).is_some());
1186 assert!(m.map(AccessType::Load, address, 2).is_ok());
1187 assert_error!(m.map(AccessType::Load, address + 2, 2), "AccessViolation");
1188 assert_eq!(m.load::<u16>(address).unwrap(), 0xFFFF);
1189 assert_error!(m.load::<u16>(address + 2), "AccessViolation");
1190 assert!(m.store::<u16>(0xFFFF, address).is_ok());
1191 assert_error!(m.store::<u16>(0xFFFF, address + 2), "AccessViolation");
1192 }
1193 }
1194 }
1195
1196 #[test]
1197 fn test_unaligned_map_overlap() {
1198 let config = Config {
1199 aligned_memory_mapping: false,
1200 ..Config::default()
1201 };
1202 let mem1 = [1, 2, 3, 4];
1203 let mem2 = [5, 6];
1204 assert_error!(
1205 unsafe {
1206 MemoryMapping::new(
1207 vec![
1208 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1209 MemoryRegion::new(
1210 &raw const mem2,
1211 ebpf::MM_REGION_SIZE + mem1.len() as u64 - 1,
1212 ),
1213 ],
1214 &config,
1215 SBPFVersion::V3,
1216 )
1217 },
1218 "InvalidMemoryRegion(1)"
1219 );
1220 assert!(unsafe {
1221 MemoryMapping::new(
1222 vec![
1223 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1224 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1225 ],
1226 &config,
1227 SBPFVersion::V3,
1228 )
1229 }
1230 .is_ok());
1231 }
1232
1233 #[test]
1234 fn test_unaligned_map() {
1235 let config = Config {
1236 aligned_memory_mapping: false,
1237 ..Config::default()
1238 };
1239 let mut mem1 = [11];
1240 let mem2 = [22, 22];
1241 let mem3 = [33];
1242 let mem4 = [44, 44];
1243 let m = unsafe {
1244 MemoryMapping::new(
1245 vec![
1246 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1247 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1248 MemoryRegion::new(
1249 &raw const mem3,
1250 ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len()) as u64,
1251 ),
1252 MemoryRegion::new(
1253 &raw const mem4,
1254 ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len() + mem3.len()) as u64,
1255 ),
1256 ],
1257 &config,
1258 SBPFVersion::V3,
1259 )
1260 .unwrap()
1261 };
1262
1263 assert_eq!(
1264 m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1265 .unwrap()
1266 .ptr()
1267 .addr(),
1268 mem1.as_ptr().addr()
1269 );
1270
1271 assert_eq!(
1272 m.map(AccessType::Store, ebpf::MM_REGION_SIZE, 1)
1273 .unwrap()
1274 .ptr()
1275 .addr(),
1276 mem1.as_ptr().addr()
1277 );
1278
1279 assert_error!(
1280 m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 2),
1281 "AccessViolation"
1282 );
1283
1284 assert_eq!(
1285 m.map(
1286 AccessType::Load,
1287 ebpf::MM_REGION_SIZE + mem1.len() as u64,
1288 1,
1289 )
1290 .unwrap()
1291 .ptr()
1292 .addr(),
1293 mem2.as_ptr().addr()
1294 );
1295
1296 assert_eq!(
1297 m.map(
1298 AccessType::Load,
1299 ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len()) as u64,
1300 1,
1301 )
1302 .unwrap()
1303 .ptr()
1304 .addr(),
1305 mem3.as_ptr().addr()
1306 );
1307
1308 assert_eq!(
1309 m.map(
1310 AccessType::Load,
1311 ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len() + mem3.len()) as u64,
1312 1,
1313 )
1314 .unwrap()
1315 .ptr()
1316 .addr(),
1317 mem4.as_ptr().addr()
1318 );
1319
1320 assert_error!(
1321 m.map(
1322 AccessType::Load,
1323 ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len() + mem3.len() + mem4.len()) as u64,
1324 1,
1325 ),
1326 "AccessViolation"
1327 );
1328 }
1329
1330 #[test]
1331 fn test_unaligned_region() {
1332 let config = Config {
1333 aligned_memory_mapping: false,
1334 ..Config::default()
1335 };
1336
1337 let mut mem1 = [0xFF; 4];
1338 let mem2 = [0xDD; 4];
1339 let m = unsafe {
1340 MemoryMapping::new(
1341 vec![
1342 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1343 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + 4),
1344 ],
1345 &config,
1346 SBPFVersion::V3,
1347 )
1348 .unwrap()
1349 };
1350 assert!(m.find_region(ebpf::MM_REGION_SIZE - 1).is_none());
1351 assert_eq!(
1352 HostBuffer::Mutable(&raw mut mem1[..]),
1353 m.find_region(ebpf::MM_REGION_SIZE).unwrap().1.host,
1354 );
1355 assert_eq!(
1356 HostBuffer::Mutable(&raw mut mem1[..]),
1357 m.find_region(ebpf::MM_REGION_SIZE + 3).unwrap().1.host,
1358 );
1359 assert_eq!(
1360 HostBuffer::Immutable(&raw const mem2[..]),
1361 m.find_region(ebpf::MM_REGION_SIZE + 4).unwrap().1.host,
1362 );
1363 assert_eq!(
1364 HostBuffer::Immutable(&raw const mem2[..]),
1365 m.find_region(ebpf::MM_REGION_SIZE + 7).unwrap().1.host,
1366 );
1367 assert!(m.find_region(ebpf::MM_REGION_SIZE + 8).is_some());
1368 }
1369
1370 #[test]
1371 fn test_aligned_region() {
1372 let config = Config {
1373 aligned_memory_mapping: true,
1374 ..Config::default()
1375 };
1376
1377 let mut mem1 = [0xFF; 4];
1378 let mem2 = [0xDD; 4];
1379 let m = unsafe {
1380 MemoryMapping::new(
1381 vec![
1382 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1383 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE * 2),
1384 ],
1385 &config,
1386 SBPFVersion::V4,
1387 )
1388 .unwrap()
1389 };
1390 assert_eq!(m.find_region(ebpf::MM_REGION_SIZE - 1).unwrap().1.len(), 0);
1391 assert_eq!(
1392 HostBuffer::Mutable(&raw mut mem1[..]),
1393 m.find_region(ebpf::MM_REGION_SIZE).unwrap().1.host,
1394 );
1395 assert_eq!(
1396 HostBuffer::Mutable(&raw mut mem1[..]),
1397 m.find_region(ebpf::MM_REGION_SIZE + 3).unwrap().1.host,
1398 );
1399 assert!(m.find_region(ebpf::MM_REGION_SIZE + 4).is_some());
1400 assert_eq!(
1401 HostBuffer::Immutable(&raw const mem2[..]),
1402 m.find_region(ebpf::MM_REGION_SIZE * 2).unwrap().1.host,
1403 );
1404 assert_eq!(
1405 HostBuffer::Immutable(&raw const mem2[..]),
1406 m.find_region(ebpf::MM_REGION_SIZE * 2 + 3).unwrap().1.host,
1407 );
1408 assert!(m.find_region(ebpf::MM_REGION_SIZE * 3 + 4).is_none());
1409 }
1410
1411 #[test]
1412 fn test_unaligned_map_load() {
1413 let config = Config {
1414 aligned_memory_mapping: false,
1415 ..Config::default()
1416 };
1417 let mem1 = [0x11, 0x22];
1418 let mem2 = [0x33];
1419 let mut m = unsafe {
1420 MemoryMapping::new(
1421 vec![
1422 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1423 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1424 ],
1425 &config,
1426 SBPFVersion::V3,
1427 )
1428 .unwrap()
1429 };
1430
1431 assert_eq!(m.load::<u16>(ebpf::MM_REGION_SIZE).unwrap(), 0x2211);
1432 assert_error!(m.load::<u32>(ebpf::MM_REGION_SIZE), "AccessViolation");
1433 assert_error!(m.load::<u32>(ebpf::MM_REGION_SIZE + 4), "AccessViolation");
1434 }
1435
1436 #[test]
1437 fn test_unaligned_map_store() {
1438 let config = Config {
1439 aligned_memory_mapping: false,
1440 ..Config::default()
1441 };
1442 let mut mem1 = [0xff, 0xff];
1443 let mut mem2 = [0xff];
1444 let mut m = unsafe {
1445 MemoryMapping::new(
1446 vec![
1447 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1448 MemoryRegion::new(&raw mut mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1449 ],
1450 &config,
1451 SBPFVersion::V3,
1452 )
1453 .unwrap()
1454 };
1455
1456 m.store(0x1122u16, ebpf::MM_REGION_SIZE).unwrap();
1457 assert_eq!(m.load::<u16>(ebpf::MM_REGION_SIZE).unwrap(), 0x1122);
1458
1459 assert_error!(
1460 m.store(0x33445566u32, ebpf::MM_REGION_SIZE),
1461 "AccessViolation"
1462 );
1463 }
1464
1465 #[test]
1466 fn test_unaligned_map_store_out_of_bounds() {
1467 let config = Config {
1468 aligned_memory_mapping: false,
1469 ..Config::default()
1470 };
1471
1472 let mut mem1 = [0xFF];
1473 let mut m = unsafe {
1474 MemoryMapping::new(
1475 vec![MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE)],
1476 &config,
1477 SBPFVersion::V3,
1478 )
1479 .unwrap()
1480 };
1481 m.store(0x11u8, ebpf::MM_REGION_SIZE).unwrap();
1482 assert_error!(m.store(0x11u8, ebpf::MM_REGION_SIZE - 1), "AccessViolation");
1483 assert_error!(m.store(0x11u8, ebpf::MM_REGION_SIZE + 1), "AccessViolation");
1484 assert_error!(m.store(0x11u8, ebpf::MM_REGION_SIZE + 2), "AccessViolation");
1487
1488 let mut mem1 = [0xFF; 4];
1489 let mut mem2 = [0xDD; 4];
1490 let mut m = unsafe {
1491 MemoryMapping::new(
1492 vec![
1493 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1494 MemoryRegion::new(&raw mut mem2, ebpf::MM_REGION_SIZE + 4),
1495 ],
1496 &config,
1497 SBPFVersion::V3,
1498 )
1499 .unwrap()
1500 };
1501 assert_error!(
1502 m.store(0x1122334455667788u64, ebpf::MM_REGION_SIZE),
1503 "AccessViolation"
1504 );
1505 }
1506
1507 #[test]
1508 fn test_unaligned_map_load_out_of_bounds() {
1509 let config = Config {
1510 aligned_memory_mapping: false,
1511 ..Config::default()
1512 };
1513
1514 let mem1 = [0xff];
1515 let mut m = unsafe {
1516 MemoryMapping::new(
1517 vec![MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE)],
1518 &config,
1519 SBPFVersion::V3,
1520 )
1521 .unwrap()
1522 };
1523 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 0xff);
1524 assert_error!(m.load::<u8>(ebpf::MM_REGION_SIZE - 1), "AccessViolation");
1525 assert_error!(m.load::<u8>(ebpf::MM_REGION_SIZE + 1), "AccessViolation");
1526 assert_error!(m.load::<u8>(ebpf::MM_REGION_SIZE + 2), "AccessViolation");
1527
1528 let mem1 = [0xFF; 4];
1529 let mem2 = [0xDD; 4];
1530 let mut m = unsafe {
1531 MemoryMapping::new(
1532 vec![
1533 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1534 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + 4),
1535 ],
1536 &config,
1537 SBPFVersion::V3,
1538 )
1539 .unwrap()
1540 };
1541 assert_error!(m.load::<u64>(ebpf::MM_REGION_SIZE), "AccessViolation");
1542 }
1543
1544 #[test]
1545 #[should_panic(expected = "AccessViolation")]
1546 fn test_store_readonly() {
1547 let config = Config {
1548 aligned_memory_mapping: false,
1549 ..Config::default()
1550 };
1551 let mut mem1 = [0xff, 0xff];
1552 let mem2 = [0xff, 0xff];
1553 let mut m = unsafe {
1554 MemoryMapping::new(
1555 vec![
1556 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1557 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1558 ],
1559 &config,
1560 SBPFVersion::V3,
1561 )
1562 .unwrap()
1563 };
1564 m.store(0x11223344, ebpf::MM_REGION_SIZE).unwrap();
1565 }
1566
1567 #[test]
1568 fn test_unaligned_map_replace_region() {
1569 let config = Config {
1570 aligned_memory_mapping: false,
1571 ..Config::default()
1572 };
1573 let mem1 = [11];
1574 let mem2 = [22, 22];
1575 let mem3 = [33];
1576 let mut m = unsafe {
1577 MemoryMapping::new(
1578 vec![
1579 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1580 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1581 ],
1582 &config,
1583 SBPFVersion::V3,
1584 )
1585 .unwrap()
1586 };
1587
1588 assert_eq!(
1589 m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1590 .unwrap()
1591 .ptr()
1592 .addr(),
1593 mem1.as_ptr().addr()
1594 );
1595
1596 assert_eq!(
1597 m.map(
1598 AccessType::Load,
1599 ebpf::MM_REGION_SIZE + mem1.len() as u64,
1600 1,
1601 )
1602 .unwrap()
1603 .ptr()
1604 .addr(),
1605 mem2.as_ptr().addr()
1606 );
1607
1608 assert_error!(
1609 unsafe {
1610 m.replace_region(
1611 2,
1612 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1613 )
1614 },
1615 "InvalidMemoryRegion(2)"
1616 );
1617
1618 let region_index = m
1619 .get_regions()
1620 .iter()
1621 .position(|mem| mem.vm_addr == ebpf::MM_REGION_SIZE + mem1.len() as u64)
1622 .unwrap();
1623
1624 assert_error!(
1626 unsafe {
1627 m.replace_region(
1628 region_index,
1629 MemoryRegion::new(
1630 &raw const mem3,
1631 ebpf::MM_REGION_SIZE + mem1.len() as u64 + 1,
1632 ),
1633 )
1634 },
1635 "InvalidMemoryRegion({})",
1636 region_index
1637 );
1638
1639 unsafe {
1640 m.replace_region(
1641 region_index,
1642 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1643 )
1644 .unwrap()
1645 };
1646
1647 assert_eq!(
1648 m.map(
1649 AccessType::Load,
1650 ebpf::MM_REGION_SIZE + mem1.len() as u64,
1651 1,
1652 )
1653 .unwrap()
1654 .ptr()
1655 .addr(),
1656 mem3.as_ptr().addr()
1657 );
1658 }
1659
1660 #[test]
1661 fn test_aligned_map_replace_region() {
1662 let config = Config {
1663 aligned_memory_mapping: true,
1664 ..Config::default()
1665 };
1666 let mem1 = [11];
1667 let mem2 = [22, 22];
1668 let mem3 = [33, 33];
1669 let mut m = unsafe {
1670 MemoryMapping::new(
1671 vec![
1672 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1673 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE * 2),
1674 ],
1675 &config,
1676 SBPFVersion::V4,
1677 )
1678 .unwrap()
1679 };
1680
1681 assert_eq!(
1682 m.map(AccessType::Load, ebpf::MM_REGION_SIZE * 2, 1)
1683 .unwrap()
1684 .ptr()
1685 .addr(),
1686 mem2.as_ptr().addr()
1687 );
1688
1689 assert_error!(
1691 unsafe {
1692 m.replace_region(
1693 3,
1694 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 2),
1695 )
1696 },
1697 "InvalidMemoryRegion(3)"
1698 );
1699
1700 assert_error!(
1702 unsafe {
1703 m.replace_region(
1704 2,
1705 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 3),
1706 )
1707 },
1708 "InvalidMemoryRegion(2)"
1709 );
1710
1711 assert_error!(
1713 unsafe {
1714 m.replace_region(
1715 2,
1716 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 3 - 1),
1717 )
1718 },
1719 "InvalidMemoryRegion(2)"
1720 );
1721
1722 unsafe {
1723 m.replace_region(
1724 2,
1725 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 2),
1726 )
1727 .unwrap()
1728 };
1729
1730 assert_eq!(
1731 m.map(AccessType::Load, ebpf::MM_REGION_SIZE * 2, 1)
1732 .unwrap()
1733 .ptr()
1734 .addr(),
1735 mem3.as_ptr().addr()
1736 );
1737 }
1738
1739 #[test]
1740 fn test_access_violation_handler_map() {
1741 for aligned_memory_mapping in [true, false] {
1742 let config = Config {
1743 aligned_memory_mapping,
1744 ..Config::default()
1745 };
1746 let original = [11, 22];
1747 let copied = Rc::new(RefCell::new(Vec::new()));
1748 let mut regions = vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)];
1749 regions[0].access_violation_handler_payload = Some(0);
1750
1751 let c = Rc::clone(&copied);
1752 let mut m = unsafe {
1753 MemoryMapping::new_with_access_violation_handler(
1754 regions,
1755 &config,
1756 SBPFVersion::V3,
1757 Box::new(move |region, _, _, _, _| {
1758 let mut vec = c.borrow_mut();
1759 vec.extend_from_slice(&original);
1760 region.redirect(&raw mut vec[..]);
1761 }),
1762 )
1763 .unwrap()
1764 };
1765
1766 assert_eq!(
1767 m.map_with_access_violation_handler(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1768 .unwrap()
1769 .ptr()
1770 .addr(),
1771 original.as_ptr().addr()
1772 );
1773 assert_eq!(
1774 m.map_with_access_violation_handler(AccessType::Store, ebpf::MM_REGION_SIZE, 1)
1775 .unwrap()
1776 .ptr()
1777 .addr(),
1778 copied.borrow().as_ptr().addr()
1779 );
1780 }
1781 }
1782
1783 #[test]
1784 fn test_access_violation_handler_load_store() {
1785 for aligned_memory_mapping in [true, false] {
1786 let config = Config {
1787 aligned_memory_mapping,
1788 ..Config::default()
1789 };
1790 let original = [11, 22];
1791 let copied = Rc::new(RefCell::new(Vec::new()));
1792 let mut regions = vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)];
1793 regions[0].access_violation_handler_payload = Some(0);
1794
1795 let c = Rc::clone(&copied);
1796 let mut m = unsafe {
1797 MemoryMapping::new_with_access_violation_handler(
1798 regions,
1799 &config,
1800 SBPFVersion::V3,
1801 Box::new(move |region, _, _, _, _| {
1802 let mut vec = c.borrow_mut();
1803 vec.extend_from_slice(&original);
1804 region.redirect(&raw mut vec[..]);
1805 }),
1806 )
1807 .unwrap()
1808 };
1809
1810 assert_eq!(
1811 m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1812 .unwrap()
1813 .ptr()
1814 .addr(),
1815 original.as_ptr().addr()
1816 );
1817
1818 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 11);
1819 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE + 1).unwrap(), 22);
1820 assert!(copied.borrow().is_empty());
1821
1822 m.store(33u8, ebpf::MM_REGION_SIZE).unwrap();
1823 assert_eq!(original[0], 11);
1824 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 33);
1825 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE + 1).unwrap(), 22);
1826 }
1827 }
1828
1829 #[test]
1830 fn test_access_violation_handler_region_id() {
1831 for aligned_memory_mapping in [true, false] {
1832 let config = Config {
1833 aligned_memory_mapping,
1834 ..Config::default()
1835 };
1836 let original1 = [11, 22];
1837 let original2 = [33, 44];
1838 let copied = Rc::new(RefCell::new(Vec::new()));
1839
1840 let mut regions = vec![
1841 MemoryRegion::new(&raw const original1, ebpf::MM_REGION_SIZE),
1842 MemoryRegion::new(&raw const original2, ebpf::MM_REGION_SIZE * 2),
1843 ];
1844 regions[0].access_violation_handler_payload = Some(42);
1845
1846 let c = Rc::clone(&copied);
1847 let mut m = unsafe {
1848 MemoryMapping::new_with_access_violation_handler(
1849 regions,
1850 &config,
1851 SBPFVersion::V3,
1852 Box::new(move |region, _, _, _, _| {
1853 assert_eq!(region.access_violation_handler_payload, Some(42));
1856 let mut vec = c.borrow_mut();
1857 vec.extend_from_slice(&original1);
1858 region.redirect(&raw mut vec[..]);
1859 }),
1860 )
1861 .unwrap()
1862 };
1863
1864 m.store(55u8, ebpf::MM_REGION_SIZE).unwrap();
1865 assert_eq!(original1[0], 11);
1866 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 55);
1867 }
1868 }
1869
1870 #[test]
1871 #[should_panic(expected = "AccessViolation")]
1872 fn test_map_access_violation_handler_error() {
1873 let config = Config::default();
1874 let original = [11, 22];
1875
1876 let m = unsafe {
1877 MemoryMapping::new_with_access_violation_handler(
1878 vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)],
1879 &config,
1880 SBPFVersion::V4,
1881 Box::new(default_access_violation_handler),
1882 )
1883 .unwrap()
1884 };
1885
1886 m.map(AccessType::Store, ebpf::MM_REGION_SIZE, 1).unwrap();
1887 }
1888
1889 #[test]
1890 #[should_panic(expected = "AccessViolation")]
1891 fn test_store_access_violation_handler_error() {
1892 let config = Config::default();
1893 let original = [11, 22];
1894
1895 let mut m = unsafe {
1896 MemoryMapping::new_with_access_violation_handler(
1897 vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)],
1898 &config,
1899 SBPFVersion::V4,
1900 Box::new(default_access_violation_handler),
1901 )
1902 .unwrap()
1903 };
1904
1905 m.store(33u8, ebpf::MM_REGION_SIZE).unwrap();
1906 }
1907
1908 #[test]
1909 fn test_access_violation_region_identification() {
1910 let config = Config::default();
1911 let original = [11, 22];
1912 let region = 0x10_0000_0000;
1913 let mut m = unsafe {
1914 MemoryMapping::new(
1915 vec![MemoryRegion::new(&raw const original, region)],
1916 &config,
1917 SBPFVersion::V4,
1918 )
1919 .unwrap()
1920 };
1921 let store_err_inbound = m.store(33u8, region).unwrap_err();
1922 assert_eq!(
1923 store_err_inbound.to_string(),
1924 "Access violation writing 1 bytes at address 0x1000000000 (in allocated region)"
1925 );
1926 let store_err_oob = m.load::<u64>(region + 3).unwrap_err();
1927 assert_eq!(
1928 store_err_oob.to_string(),
1929 "Access violation reading 8 bytes at address 0x1000000003 (in unallocated region)"
1930 );
1931 }
1932
1933 #[test]
1934 fn v4_aligned_mapping() {
1935 let config = Config {
1936 aligned_memory_mapping: false,
1937 ..Config::default()
1938 };
1939
1940 let mem = [11, 12];
1941 let mapping = unsafe {
1942 MemoryMapping::new_with_access_violation_handler(
1943 vec![MemoryRegion::new(&raw const mem, ebpf::MM_REGION_SIZE)],
1944 &config,
1945 SBPFVersion::V4,
1946 Box::new(default_access_violation_handler),
1947 )
1948 .unwrap()
1949 };
1950
1951 assert!(matches!(mapping.ty, MemoryMappingType::Aligned(_)));
1952 }
1953}