1use crate::{
4 aligned_memory::Pod,
5 ebpf,
6 error::{EbpfError, ProgramResult},
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 const WRITABLE: bool;
52
53 fn host_address(self) -> usize;
57 fn byte_length(&self) -> usize;
59}
60
61pub trait VmExposable {}
66
67pub unsafe trait VmExposableMut {}
77
78unsafe impl VmExposableMut for u8 {}
79impl<T: VmExposableMut> VmExposable for T {}
80
81unsafe impl<T: VmExposable> HostMemoryObject for *const T {
82 const WRITABLE: bool = false;
83 fn host_address(self) -> usize {
84 self.expose_provenance()
85 }
86 fn byte_length(&self) -> usize {
87 std::mem::size_of::<T>()
88 }
89}
90
91unsafe impl<T: VmExposableMut> HostMemoryObject for *mut T {
92 const WRITABLE: bool = true;
93 fn host_address(self) -> usize {
94 self.expose_provenance()
95 }
96 fn byte_length(&self) -> usize {
97 std::mem::size_of::<T>()
98 }
99}
100
101unsafe impl<T: VmExposable> HostMemoryObject for *const [T] {
102 const WRITABLE: bool = false;
103 fn host_address(self) -> usize {
104 self.expose_provenance()
105 }
106 fn byte_length(&self) -> usize {
107 self.len().checked_mul(core::mem::size_of::<T>()).unwrap()
108 }
109}
110
111unsafe impl<T: VmExposableMut> HostMemoryObject for *mut [T] {
112 const WRITABLE: bool = true;
113 fn host_address(self) -> usize {
114 self.expose_provenance()
115 }
116 fn byte_length(&self) -> usize {
117 self.len().checked_mul(core::mem::size_of::<T>()).unwrap()
118 }
119}
120
121unsafe impl<T: VmExposable, const N: usize> HostMemoryObject for *const [T; N] {
122 const WRITABLE: bool = false;
123 fn host_address(self) -> usize {
124 self.expose_provenance()
125 }
126 fn byte_length(&self) -> usize {
127 N.checked_mul(core::mem::size_of::<T>()).unwrap()
128 }
129}
130
131unsafe impl<T: VmExposableMut, const N: usize> HostMemoryObject for *mut [T; N] {
132 const WRITABLE: bool = true;
133 fn host_address(self) -> usize {
134 self.expose_provenance()
135 }
136 fn byte_length(&self) -> usize {
137 N.checked_mul(core::mem::size_of::<T>()).unwrap()
138 }
139}
140
141#[derive(Default, Eq, PartialEq, Clone)]
143#[repr(C, align(32))]
144pub struct MemoryRegion {
145 pub host_addr: u64,
147 pub vm_addr: u64,
149 pub len: u64,
151 pub vm_gap_shift: u8,
153 pub writable: bool,
155 pub access_violation_handler_payload: Option<u16>,
157}
158
159impl MemoryRegion {
160 fn new_internal(
164 address: usize,
165 len: usize,
166 vm_addr: u64,
167 vm_gap_size: u64,
168 writable: bool,
169 ) -> Self {
170 let mut vm_gap_shift = (std::mem::size_of::<u64>() as u8)
171 .saturating_mul(8)
172 .saturating_sub(1);
173 if vm_gap_size > 0 {
174 vm_gap_shift = vm_gap_shift.saturating_sub(vm_gap_size.leading_zeros() as u8);
175 debug_assert_eq!(Some(vm_gap_size), 1_u64.checked_shl(vm_gap_shift as u32));
176 };
177 MemoryRegion {
178 host_addr: address as u64,
179 vm_addr,
180 len: len as u64,
181 vm_gap_shift,
182 writable,
183 access_violation_handler_payload: None,
184 }
185 }
186
187 pub fn new<HO: HostMemoryObject>(host: HO, vm_addr: u64) -> Self {
191 let bytes = host.byte_length();
192 Self::new_internal(host.host_address(), bytes, vm_addr, 0, HO::WRITABLE)
193 }
194
195 pub fn new_gapped<HO: HostMemoryObject>(host: HO, vm_addr: u64, vm_gap_size: u64) -> Self {
199 let bytes = host.byte_length();
200 let host_address = host.host_address();
201 Self::new_internal(host_address, bytes, vm_addr, vm_gap_size, HO::WRITABLE)
202 }
203
204 pub fn vm_addr_range(&self) -> Range<u64> {
206 if self.vm_gap_shift == 63 {
207 self.vm_addr..self.vm_addr.saturating_add(self.len)
208 } else {
209 self.vm_addr..self.vm_addr.saturating_add(self.len.saturating_mul(2))
210 }
211 }
212
213 pub fn vm_to_host(&self, access_type: AccessType, vm_addr: u64, len: u64) -> Option<u64> {
215 if access_type == AccessType::Store && !self.writable {
216 return None;
217 }
218
219 if vm_addr < self.vm_addr {
223 return None;
224 }
225
226 let begin_offset = vm_addr.saturating_sub(self.vm_addr);
227 if self.vm_gap_shift == 63 {
228 if let Some(end_offset) = begin_offset.checked_add(len) {
230 if end_offset <= self.len {
231 return Some(self.host_addr.saturating_add(begin_offset));
232 }
233 }
234 return None;
235 }
236
237 let is_in_gap = (begin_offset
238 .checked_shr(self.vm_gap_shift as u32)
239 .unwrap_or(0)
240 & 1)
241 == 1;
242 let gap_mask = (-1i64).checked_shl(self.vm_gap_shift as u32).unwrap_or(0) as u64;
243 let gapped_offset =
244 (begin_offset & gap_mask).checked_shr(1).unwrap_or(0) | (begin_offset & !gap_mask);
245 if let Some(end_offset) = gapped_offset.checked_add(len) {
246 if end_offset <= self.len && !is_in_gap {
247 return Some(self.host_addr.saturating_add(gapped_offset));
248 }
249 }
250 None
251 }
252}
253
254impl fmt::Debug for MemoryRegion {
255 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
256 write!(
257 f,
258 "host_addr: {:#x?}-{:#x?}, vm_addr: {:#x?}-{:#x?}, len: {}, writable: {}, payload {:?}",
259 self.host_addr,
260 self.host_addr.saturating_add(self.len),
261 self.vm_addr,
262 self.vm_addr_range().end,
263 self.len,
264 self.writable,
265 self.access_violation_handler_payload,
266 )
267 }
268}
269
270impl std::cmp::PartialOrd for MemoryRegion {
271 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
272 Some(self.cmp(other))
273 }
274}
275
276impl std::cmp::Ord for MemoryRegion {
277 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
278 self.vm_addr.cmp(&other.vm_addr)
279 }
280}
281
282#[derive(Clone, Copy, PartialEq, Eq, Debug)]
284pub enum AccessType {
285 Load,
287 Store,
289}
290
291impl std::fmt::Display for AccessType {
292 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
293 f.write_str(match self {
294 Self::Load => "reading",
295 Self::Store => "writing",
296 })
297 }
298}
299
300pub struct UnalignedMemoryMapping {
302 regions: Box<[MemoryRegion]>,
304 region_addresses: Box<[u64]>,
306 region_index_lookup: Box<[usize]>,
308 cache: UnsafeCell<MappingCache>,
310}
311
312impl fmt::Debug for UnalignedMemoryMapping {
313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314 f.debug_struct("UnalignedMemoryMapping")
315 .field("regions", &self.regions)
316 .field("cache", &self.cache)
317 .finish()
318 }
319}
320
321impl UnalignedMemoryMapping {
322 fn construct_eytzinger_order(&mut self, mut in_index: usize, out_index: usize) -> usize {
323 if out_index >= self.regions.len() {
324 return in_index;
325 }
326 in_index =
327 self.construct_eytzinger_order(in_index, out_index.saturating_mul(2).saturating_add(1));
328 self.region_addresses[out_index] = self.regions[in_index].vm_addr;
329 self.region_index_lookup[out_index] = in_index;
330 self.construct_eytzinger_order(
331 in_index.saturating_add(1),
332 out_index.saturating_mul(2).saturating_add(2),
333 )
334 }
335
336 pub unsafe fn new_uninitialized(regions: Vec<MemoryRegion>) -> Self {
342 let number_of_regions = regions.len();
343 Self {
344 regions: regions.into_boxed_slice(),
345 region_addresses: vec![0; number_of_regions].into_boxed_slice(),
346 region_index_lookup: vec![0; number_of_regions].into_boxed_slice(),
347 cache: UnsafeCell::new(MappingCache::new()),
348 }
349 }
350
351 pub unsafe fn new(regions: Vec<MemoryRegion>) -> Result<Self, EbpfError> {
357 let mut mapping = Self::new_uninitialized(regions);
358 mapping.initialize()?;
359 Ok(mapping)
360 }
361
362 pub fn initialize(&mut self) -> Result<(), EbpfError> {
364 self.regions.sort();
365 let number_of_regions = self.regions.len();
366 for index in 1..number_of_regions {
367 let first = &self.regions[index.saturating_sub(1)];
368 let second = &self.regions[index];
369 if first.vm_addr_range().end > second.vm_addr {
370 return Err(EbpfError::InvalidMemoryRegion(index));
371 }
372 }
373
374 self.construct_eytzinger_order(0, 0);
375 Ok(())
376 }
377
378 #[allow(clippy::arithmetic_side_effects)]
380 #[inline(always)]
381 pub fn find_region(&self, vm_addr: u64) -> Option<(usize, &MemoryRegion)> {
382 let cache = unsafe { &mut *self.cache.get() };
387 if let Some(index) = cache.find(vm_addr) {
388 Some((index, unsafe { self.regions.get_unchecked(index) }))
392 } else {
393 let mut index = 1;
394 while index <= self.region_addresses.len() {
395 index = (index << 1)
399 + unsafe { *self.region_addresses.get_unchecked(index - 1) <= vm_addr }
400 as usize;
401 }
402 index >>= index.trailing_zeros() + 1;
403 if index == 0 {
404 return None;
405 }
406 index = unsafe { *self.region_index_lookup.get_unchecked(index - 1) };
410 let region = unsafe { self.regions.get_unchecked(index) };
411 cache.insert(region.vm_addr_range(), index);
412 Some((index, region))
413 }
414 }
415
416 #[inline(always)]
422 pub unsafe fn replace_region(
423 &mut self,
424 index: usize,
425 region: MemoryRegion,
426 ) -> Result<(), EbpfError> {
427 self.regions[index] = region;
428 self.cache.get_mut().flush();
429 Ok(())
430 }
431}
432
433#[derive(Debug)]
436pub struct AlignedMemoryMapping {
437 regions: Vec<MemoryRegion>,
438 allow_memory_region_zero: bool,
439}
440
441impl AlignedMemoryMapping {
442 pub unsafe fn new(regions: Vec<MemoryRegion>, config: &Config) -> Result<Self, EbpfError> {
448 let mut mapping = Self::new_uninitialized(regions, config);
449 mapping.initialize()?;
450 Ok(mapping)
451 }
452
453 pub unsafe fn new_uninitialized(regions: Vec<MemoryRegion>, config: &Config) -> Self {
459 Self {
460 regions,
461 allow_memory_region_zero: config.allow_memory_region_zero,
462 }
463 }
464
465 pub fn initialize(&mut self) -> Result<(), EbpfError> {
467 static EMPTY_SLICE: &[u8] = &[];
468 if self.allow_memory_region_zero {
469 self.regions.sort();
470 let mut expected_region_index = 0;
471 while expected_region_index < self.regions.len() {
472 let actual_region_index = self
473 .regions
474 .get(expected_region_index)
475 .unwrap()
476 .vm_addr
477 .checked_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32)
478 .unwrap_or(0) as usize;
479 if actual_region_index > expected_region_index {
480 self.regions.insert(
481 expected_region_index,
482 MemoryRegion::new(
483 &raw const *EMPTY_SLICE,
484 (expected_region_index as u64).saturating_mul(ebpf::MM_REGION_SIZE),
485 ),
486 );
487 } else if actual_region_index < expected_region_index {
488 return Err(EbpfError::InvalidMemoryRegion(actual_region_index));
489 }
490 expected_region_index = expected_region_index.saturating_add(1);
491 }
492 } else {
493 self.regions
494 .push(MemoryRegion::new(&raw const *EMPTY_SLICE, 0));
495 self.regions.sort();
496 for (index, region) in self.regions.iter().enumerate() {
497 if region
498 .vm_addr
499 .checked_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32)
500 .unwrap_or(0)
501 != index as u64
502 {
503 return Err(EbpfError::InvalidMemoryRegion(index));
504 }
505 }
506 }
507
508 Ok(())
509 }
510
511 #[inline(always)]
513 pub fn find_region(&self, vm_addr: u64) -> Option<(usize, &MemoryRegion)> {
514 let index = vm_addr.wrapping_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32) as usize;
515 if index < self.regions.len() && (index > 0 || self.allow_memory_region_zero) {
516 let region = unsafe { self.regions.get_unchecked(index) };
518 return Some((index, region));
519 }
520 None
521 }
522
523 #[inline(always)]
529 pub unsafe fn replace_region(
530 &mut self,
531 index: usize,
532 region: MemoryRegion,
533 ) -> Result<(), EbpfError> {
534 let begin_index = region
535 .vm_addr
536 .checked_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32)
537 .unwrap_or(0) as usize;
538 let end_index = region
539 .vm_addr
540 .saturating_add(region.len.saturating_sub(1))
541 .checked_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32)
542 .unwrap_or(0) as usize;
543 if begin_index != index || end_index != index {
544 return Err(EbpfError::InvalidMemoryRegion(index));
545 }
546 self.regions[index] = region;
547 Ok(())
548 }
549}
550
551pub struct MemoryMapping {
553 access_violation_handler: AccessViolationHandler,
555 max_call_depth: i64,
556 stack_frame_size: i64,
557 disable_address_translation: bool,
558 sbpf_version: SBPFVersion,
560 initialized: bool,
561 ty: MemoryMappingType,
562}
563
564impl fmt::Debug for MemoryMapping {
565 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
566 f.debug_struct("MemoryMapping")
567 .field("max_call_depth", &self.max_call_depth)
568 .field("stack_frame_size", &self.stack_frame_size)
569 .field("sbpf_version", &self.sbpf_version)
570 .field("ty", &self.ty)
571 .finish()
572 }
573}
574
575#[derive(Debug)]
577pub enum MemoryMappingType {
578 Aligned(AlignedMemoryMapping),
581 Unaligned(UnalignedMemoryMapping),
583}
584
585impl MemoryMapping {
586 pub unsafe fn new_with_access_violation_handler(
598 regions: Vec<MemoryRegion>,
599 config: &Config,
600 sbpf_version: SBPFVersion,
601 access_violation_handler: AccessViolationHandler,
602 ) -> Result<Self, EbpfError> {
603 let mut mapping =
604 Self::new_uninitialized(regions, config, sbpf_version, access_violation_handler);
605 mapping.initialize()?;
606 Ok(mapping)
607 }
608
609 pub unsafe fn new_uninitialized(
620 regions: Vec<MemoryRegion>,
621 config: &Config,
622 sbpf_version: SBPFVersion,
623 access_violation_handler: AccessViolationHandler,
624 ) -> Self {
625 let ty = if sbpf_version >= SBPFVersion::V4 || config.aligned_memory_mapping {
626 MemoryMappingType::Aligned(AlignedMemoryMapping::new_uninitialized(regions, config))
627 } else {
628 debug_assert!(
629 sbpf_version <= SBPFVersion::V3,
630 "SBPFv4 and later versions do not support unaligned memory"
631 );
632 MemoryMappingType::Unaligned(UnalignedMemoryMapping::new_uninitialized(regions))
633 };
634
635 Self {
636 access_violation_handler: Box::new(access_violation_handler),
637 max_call_depth: config.max_call_depth as i64,
638 stack_frame_size: config.stack_frame_size as i64,
639 disable_address_translation: !config.enable_address_translation,
640 sbpf_version,
641 initialized: false,
642 ty,
643 }
644 }
645
646 pub unsafe fn new(
654 regions: Vec<MemoryRegion>,
655 config: &Config,
656 sbpf_version: SBPFVersion,
657 ) -> Result<Self, EbpfError> {
658 Self::new_with_access_violation_handler(
659 regions,
660 config,
661 sbpf_version,
662 Box::new(default_access_violation_handler),
663 )
664 }
665
666 pub fn map(&self, access_type: AccessType, vm_addr: u64, len: u64) -> ProgramResult {
668 debug_assert!(self.initialized);
669 if self.disable_address_translation {
670 return ProgramResult::Ok(vm_addr);
671 }
672
673 if let Some((_index, region)) = self.find_region(vm_addr) {
674 if let Some(host_addr) = region.vm_to_host(access_type, vm_addr, len) {
675 return ProgramResult::Ok(host_addr);
676 }
677 }
678 self.generate_access_violation(access_type, vm_addr, len)
679 }
680
681 #[inline(always)]
686 pub fn map_with_access_violation_handler(
687 &mut self,
688 access_type: AccessType,
689 vm_addr: u64,
690 len: u64,
691 ) -> ProgramResult {
692 debug_assert!(self.initialized);
693 if self.disable_address_translation {
694 return ProgramResult::Ok(vm_addr);
695 }
696
697 if let Some((index, region)) = self.find_region(vm_addr) {
698 if let Some(host_addr) = region.vm_to_host(access_type, vm_addr, len) {
699 return ProgramResult::Ok(host_addr);
700 }
701 let mut region = (*region).clone();
702 let max_len = self
703 .get_regions()
704 .get(index.saturating_add(1))
705 .map_or(u64::MAX, |next_region| next_region.vm_addr)
706 .saturating_sub(region.vm_addr);
707 (self.access_violation_handler)(&mut region, max_len, access_type, vm_addr, len);
708 if let Some(host_addr) = region.vm_to_host(access_type, vm_addr, len) {
709 if let Err(err) = unsafe { self.replace_region(index, region) } {
710 return ProgramResult::Err(err);
711 }
712 return ProgramResult::Ok(host_addr);
713 }
714 }
715 self.generate_access_violation(access_type, vm_addr, len)
716 }
717
718 pub fn load<T: Pod + Into<u64>>(&mut self, vm_addr: u64) -> ProgramResult {
720 let len = mem::size_of::<T>() as u64;
721 debug_assert!(len <= mem::size_of::<u64>() as u64);
722 debug_assert!(self.initialized);
723 match self.map_with_access_violation_handler(AccessType::Load, vm_addr, len) {
724 ProgramResult::Ok(host_addr) => {
725 ProgramResult::Ok(unsafe { ptr::read_unaligned::<T>(host_addr as *const T) }.into())
726 }
727 err => err,
728 }
729 }
730
731 #[inline]
733 pub fn store<T: Pod>(&mut self, value: T, vm_addr: u64) -> ProgramResult {
734 let len = mem::size_of::<T>() as u64;
735 debug_assert!(len <= mem::size_of::<u64>() as u64);
736 debug_assert!(self.initialized);
737 match self.map_with_access_violation_handler(AccessType::Store, vm_addr, len) {
738 ProgramResult::Ok(host_addr) => {
739 unsafe { ptr::write_unaligned(host_addr as *mut T, value) };
740 ProgramResult::Ok(host_addr)
741 }
742 err => err,
743 }
744 }
745
746 #[inline(always)]
748 pub fn find_region(&self, vm_addr: u64) -> Option<(usize, &MemoryRegion)> {
749 debug_assert!(self.initialized);
750 match &self.ty {
751 MemoryMappingType::Aligned(inner) => inner.find_region(vm_addr),
752 MemoryMappingType::Unaligned(inner) => inner.find_region(vm_addr),
753 }
754 }
755
756 #[inline(always)]
758 pub fn get_regions(&self) -> &[MemoryRegion] {
759 match &self.ty {
760 MemoryMappingType::Aligned(inner) => &inner.regions,
761 MemoryMappingType::Unaligned(inner) => &inner.regions,
762 }
763 }
764
765 pub fn get_regions_mut(&mut self) -> &mut [MemoryRegion] {
771 self.initialized = false;
772
773 let regions = match &mut self.ty {
774 MemoryMappingType::Aligned(inner) => inner.regions.as_mut_slice(),
775 MemoryMappingType::Unaligned(inner) => &mut inner.regions,
776 };
777
778 regions
779 }
780
781 #[inline(always)]
787 pub unsafe fn replace_region(
788 &mut self,
789 index: usize,
790 region: MemoryRegion,
791 ) -> Result<(), EbpfError> {
792 debug_assert!(self.initialized);
793 let regions = self.get_regions();
794 let next_region_start = regions
795 .get(index.saturating_add(1))
796 .map_or(u64::MAX, |next_region| next_region.vm_addr);
797 if index >= regions.len()
798 || regions[index].vm_addr != region.vm_addr
799 || region.vm_addr_range().end > next_region_start
800 {
801 return Err(EbpfError::InvalidMemoryRegion(index));
802 }
803 match &mut self.ty {
804 MemoryMappingType::Aligned(inner) => inner.replace_region(index, region),
805 MemoryMappingType::Unaligned(inner) => inner.replace_region(index, region),
806 }
807 }
808
809 pub fn initialize(&mut self) -> Result<(), EbpfError> {
811 let result = match &mut self.ty {
812 MemoryMappingType::Aligned(inner) => inner.initialize(),
813 MemoryMappingType::Unaligned(inner) => inner.initialize(),
814 };
815 self.initialized = result.is_ok();
816 result
817 }
818
819 fn generate_access_violation(
820 &self,
821 access_type: AccessType,
822 vm_addr: u64,
823 len: u64,
824 ) -> ProgramResult {
825 let stack_frame = (vm_addr as i64)
826 .saturating_sub(ebpf::MM_STACK_START as i64)
827 .checked_div(self.stack_frame_size)
828 .unwrap_or(0);
829 if !self.sbpf_version.manual_stack_frame_bump()
830 && (-1..self.max_call_depth.saturating_add(1)).contains(&stack_frame)
831 {
832 ProgramResult::Err(EbpfError::StackAccessViolation(
833 access_type,
834 vm_addr,
835 len,
836 stack_frame,
837 ))
838 } else {
839 let region = self.find_region(vm_addr);
840 let region_name = match vm_addr & (!ebpf::MM_BYTECODE_START.saturating_sub(1)) {
841 _ if region.map(|(_, r)| r.vm_addr_range().contains(&vm_addr)) != Some(true) => {
842 "unallocated"
843 }
844 ebpf::MM_BYTECODE_START => "program",
845 ebpf::MM_STACK_START => "stack",
846 ebpf::MM_HEAP_START => "heap",
847 ebpf::MM_INPUT_START => "input",
848 _ => "allocated",
849 };
850 ProgramResult::Err(EbpfError::AccessViolation(
851 access_type,
852 vm_addr,
853 len,
854 region_name,
855 ))
856 }
857 }
858}
859
860#[derive(Debug)]
862struct MappingCache {
863 entries: [(Range<u64>, usize); MappingCache::SIZE],
865 head: usize,
870}
871
872impl MappingCache {
873 const SIZE: usize = 4;
875
876 fn new() -> MappingCache {
877 MappingCache {
878 entries: array::from_fn(|_| (0..0, 0)),
879 head: 0,
880 }
881 }
882
883 #[inline]
884 fn find(&self, vm_addr: u64) -> Option<usize> {
885 for i in 0..Self::SIZE {
886 let index = self.head.wrapping_add(i) % Self::SIZE;
887 let (vm_range, region_index) = unsafe { self.entries.get_unchecked(index) };
890 if vm_range.contains(&vm_addr) {
891 return Some(*region_index);
892 }
893 }
894
895 None
896 }
897
898 #[inline]
899 fn insert(&mut self, vm_range: Range<u64>, region_index: usize) {
900 self.head = self.head.wrapping_sub(1) % Self::SIZE;
901 unsafe { *self.entries.get_unchecked_mut(self.head) = (vm_range, region_index) };
904 }
905
906 #[inline]
907 fn flush(&mut self) {
908 self.entries = array::from_fn(|_| (0..0, 0));
909 self.head = 0;
910 }
911}
912
913#[cfg(test)]
914mod test {
915 use std::{cell::RefCell, rc::Rc};
916 use test_utils::assert_error;
917
918 use super::*;
919
920 #[test]
921 fn test_mapping_cache() {
922 let mut cache = MappingCache::new();
923 assert_eq!(cache.find(0), None);
924
925 let mut ranges = vec![10u64..20, 20..30, 30..40, 40..50];
926 for (region, range) in ranges.iter().cloned().enumerate() {
927 cache.insert(range, region);
928 }
929 for (region, range) in ranges.iter().enumerate() {
930 if region > 0 {
931 assert_eq!(cache.find(range.start - 1), Some(region - 1));
932 } else {
933 assert_eq!(cache.find(range.start - 1), None);
934 }
935 assert_eq!(cache.find(range.start), Some(region));
936 assert_eq!(cache.find(range.start + 1), Some(region));
937 assert_eq!(cache.find(range.end - 1), Some(region));
938 if region < 3 {
939 assert_eq!(cache.find(range.end), Some(region + 1));
940 } else {
941 assert_eq!(cache.find(range.end), None);
942 }
943 }
944
945 cache.insert(50..60, 4);
946 ranges.push(50..60);
947 for (region, range) in ranges.iter().enumerate() {
948 if region == 0 {
949 assert_eq!(cache.find(range.start), None);
950 continue;
951 }
952 if region > 1 {
953 assert_eq!(cache.find(range.start - 1), Some(region - 1));
954 } else {
955 assert_eq!(cache.find(range.start - 1), None);
956 }
957 assert_eq!(cache.find(range.start), Some(region));
958 assert_eq!(cache.find(range.start + 1), Some(region));
959 assert_eq!(cache.find(range.end - 1), Some(region));
960 if region < 4 {
961 assert_eq!(cache.find(range.end), Some(region + 1));
962 } else {
963 assert_eq!(cache.find(range.end), None);
964 }
965 }
966 }
967
968 #[test]
969 fn test_mapping_cache_flush() {
970 let mut cache = MappingCache::new();
971 assert_eq!(cache.find(0), None);
972 cache.insert(0..10, 0);
973 assert_eq!(cache.find(0), Some(0));
974 cache.flush();
975 assert_eq!(cache.find(0), None);
976 }
977
978 #[test]
979 fn test_map_empty() {
980 for aligned_memory_mapping in [false, true] {
981 let config = Config {
982 aligned_memory_mapping,
983 ..Config::default()
984 };
985 let m = unsafe { MemoryMapping::new(vec![], &config, SBPFVersion::V3) }.unwrap();
986 assert_error!(
987 m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 8),
988 "AccessViolation"
989 );
990 }
991 }
992
993 #[test]
994 fn test_gapped_map() {
995 for aligned_memory_mapping in [false, true] {
996 let config = Config {
997 aligned_memory_mapping,
998 ..Config::default()
999 };
1000 let mut mem1 = [0xff; 8];
1001 let mem2 = [0; 8];
1002 let mut m = unsafe {
1003 MemoryMapping::new(
1004 vec![
1005 MemoryRegion::new(&raw const mem2[..], ebpf::MM_REGION_SIZE),
1006 MemoryRegion::new_gapped(&raw mut mem1[..], ebpf::MM_REGION_SIZE * 2, 2),
1007 ],
1008 &config,
1009 SBPFVersion::V3,
1010 )
1011 .unwrap()
1012 };
1013 for frame in 0..4 {
1014 let address = ebpf::MM_STACK_START + frame * 4;
1015 assert!(m.find_region(address).is_some());
1016 assert!(m.map(AccessType::Load, address, 2).is_ok());
1017 assert_error!(m.map(AccessType::Load, address + 2, 2), "AccessViolation");
1018 assert_eq!(m.load::<u16>(address).unwrap(), 0xFFFF);
1019 assert_error!(m.load::<u16>(address + 2), "AccessViolation");
1020 assert!(m.store::<u16>(0xFFFF, address).is_ok());
1021 assert_error!(m.store::<u16>(0xFFFF, address + 2), "AccessViolation");
1022 }
1023 }
1024 }
1025
1026 #[test]
1027 fn test_unaligned_map_overlap() {
1028 let config = Config {
1029 aligned_memory_mapping: false,
1030 ..Config::default()
1031 };
1032 let mem1 = [1, 2, 3, 4];
1033 let mem2 = [5, 6];
1034 assert_error!(
1035 unsafe {
1036 MemoryMapping::new(
1037 vec![
1038 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1039 MemoryRegion::new(
1040 &raw const mem2,
1041 ebpf::MM_REGION_SIZE + mem1.len() as u64 - 1,
1042 ),
1043 ],
1044 &config,
1045 SBPFVersion::V3,
1046 )
1047 },
1048 "InvalidMemoryRegion(1)"
1049 );
1050 assert!(unsafe {
1051 MemoryMapping::new(
1052 vec![
1053 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1054 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1055 ],
1056 &config,
1057 SBPFVersion::V3,
1058 )
1059 }
1060 .is_ok());
1061 }
1062
1063 #[test]
1064 fn test_unaligned_map() {
1065 let config = Config {
1066 aligned_memory_mapping: false,
1067 ..Config::default()
1068 };
1069 let mut mem1 = [11];
1070 let mem2 = [22, 22];
1071 let mem3 = [33];
1072 let mem4 = [44, 44];
1073 let m = unsafe {
1074 MemoryMapping::new(
1075 vec![
1076 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1077 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1078 MemoryRegion::new(
1079 &raw const mem3,
1080 ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len()) as u64,
1081 ),
1082 MemoryRegion::new(
1083 &raw const mem4,
1084 ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len() + mem3.len()) as u64,
1085 ),
1086 ],
1087 &config,
1088 SBPFVersion::V3,
1089 )
1090 .unwrap()
1091 };
1092
1093 assert_eq!(
1094 m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 1).unwrap(),
1095 mem1.as_ptr() as u64
1096 );
1097
1098 assert_eq!(
1099 m.map(AccessType::Store, ebpf::MM_REGION_SIZE, 1).unwrap(),
1100 mem1.as_ptr() as u64
1101 );
1102
1103 assert_error!(
1104 m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 2),
1105 "AccessViolation"
1106 );
1107
1108 assert_eq!(
1109 m.map(
1110 AccessType::Load,
1111 ebpf::MM_REGION_SIZE + mem1.len() as u64,
1112 1,
1113 )
1114 .unwrap(),
1115 mem2.as_ptr() as u64
1116 );
1117
1118 assert_eq!(
1119 m.map(
1120 AccessType::Load,
1121 ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len()) as u64,
1122 1,
1123 )
1124 .unwrap(),
1125 mem3.as_ptr() as u64
1126 );
1127
1128 assert_eq!(
1129 m.map(
1130 AccessType::Load,
1131 ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len() + mem3.len()) as u64,
1132 1,
1133 )
1134 .unwrap(),
1135 mem4.as_ptr() as u64
1136 );
1137
1138 assert_error!(
1139 m.map(
1140 AccessType::Load,
1141 ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len() + mem3.len() + mem4.len()) as u64,
1142 1,
1143 ),
1144 "AccessViolation"
1145 );
1146 }
1147
1148 #[test]
1149 fn test_unaligned_region() {
1150 let config = Config {
1151 aligned_memory_mapping: false,
1152 ..Config::default()
1153 };
1154
1155 let mut mem1 = [0xFF; 4];
1156 let mem2 = [0xDD; 4];
1157 let m = unsafe {
1158 MemoryMapping::new(
1159 vec![
1160 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1161 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + 4),
1162 ],
1163 &config,
1164 SBPFVersion::V3,
1165 )
1166 .unwrap()
1167 };
1168 assert!(m.find_region(ebpf::MM_REGION_SIZE - 1).is_none());
1169 assert_eq!(
1170 m.find_region(ebpf::MM_REGION_SIZE).unwrap().1.host_addr,
1171 mem1.as_ptr() as u64
1172 );
1173 assert_eq!(
1174 m.find_region(ebpf::MM_REGION_SIZE + 3).unwrap().1.host_addr,
1175 mem1.as_ptr() as u64
1176 );
1177 assert_eq!(
1178 m.find_region(ebpf::MM_REGION_SIZE + 4).unwrap().1.host_addr,
1179 mem2.as_ptr() as u64
1180 );
1181 assert_eq!(
1182 m.find_region(ebpf::MM_REGION_SIZE + 7).unwrap().1.host_addr,
1183 mem2.as_ptr() as u64
1184 );
1185 assert!(m.find_region(ebpf::MM_REGION_SIZE + 8).is_some());
1186 }
1187
1188 #[test]
1189 fn test_aligned_region() {
1190 let config = Config {
1191 aligned_memory_mapping: true,
1192 ..Config::default()
1193 };
1194
1195 let mut mem1 = [0xFF; 4];
1196 let mem2 = [0xDD; 4];
1197 let m = unsafe {
1198 MemoryMapping::new(
1199 vec![
1200 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1201 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE * 2),
1202 ],
1203 &config,
1204 SBPFVersion::V4,
1205 )
1206 .unwrap()
1207 };
1208 assert_eq!(m.find_region(ebpf::MM_REGION_SIZE - 1).unwrap().1.len, 0);
1209 assert_eq!(
1210 m.find_region(ebpf::MM_REGION_SIZE).unwrap().1.host_addr,
1211 mem1.as_ptr() as u64
1212 );
1213 assert_eq!(
1214 m.find_region(ebpf::MM_REGION_SIZE + 3).unwrap().1.host_addr,
1215 mem1.as_ptr() as u64
1216 );
1217 assert!(m.find_region(ebpf::MM_REGION_SIZE + 4).is_some());
1218 assert_eq!(
1219 m.find_region(ebpf::MM_REGION_SIZE * 2).unwrap().1.host_addr,
1220 mem2.as_ptr() as u64
1221 );
1222 assert_eq!(
1223 m.find_region(ebpf::MM_REGION_SIZE * 2 + 3)
1224 .unwrap()
1225 .1
1226 .host_addr,
1227 mem2.as_ptr() as u64
1228 );
1229 assert!(m.find_region(ebpf::MM_REGION_SIZE * 3 + 4).is_none());
1230 }
1231
1232 #[test]
1233 fn test_unaligned_map_load() {
1234 let config = Config {
1235 aligned_memory_mapping: false,
1236 ..Config::default()
1237 };
1238 let mem1 = [0x11, 0x22];
1239 let mem2 = [0x33];
1240 let mut m = 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 .unwrap()
1250 };
1251
1252 assert_eq!(m.load::<u16>(ebpf::MM_REGION_SIZE).unwrap(), 0x2211);
1253 assert_error!(m.load::<u32>(ebpf::MM_REGION_SIZE), "AccessViolation");
1254 assert_error!(m.load::<u32>(ebpf::MM_REGION_SIZE + 4), "AccessViolation");
1255 }
1256
1257 #[test]
1258 fn test_unaligned_map_store() {
1259 let config = Config {
1260 aligned_memory_mapping: false,
1261 ..Config::default()
1262 };
1263 let mut mem1 = [0xff, 0xff];
1264 let mut mem2 = [0xff];
1265 let mut m = unsafe {
1266 MemoryMapping::new(
1267 vec![
1268 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1269 MemoryRegion::new(&raw mut mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1270 ],
1271 &config,
1272 SBPFVersion::V3,
1273 )
1274 .unwrap()
1275 };
1276
1277 m.store(0x1122u16, ebpf::MM_REGION_SIZE).unwrap();
1278 assert_eq!(m.load::<u16>(ebpf::MM_REGION_SIZE).unwrap(), 0x1122);
1279
1280 assert_error!(
1281 m.store(0x33445566u32, ebpf::MM_REGION_SIZE),
1282 "AccessViolation"
1283 );
1284 }
1285
1286 #[test]
1287 fn test_unaligned_map_store_out_of_bounds() {
1288 let config = Config {
1289 aligned_memory_mapping: false,
1290 ..Config::default()
1291 };
1292
1293 let mut mem1 = [0xFF];
1294 let mut m = unsafe {
1295 MemoryMapping::new(
1296 vec![MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE)],
1297 &config,
1298 SBPFVersion::V3,
1299 )
1300 .unwrap()
1301 };
1302 m.store(0x11u8, ebpf::MM_REGION_SIZE).unwrap();
1303 assert_error!(m.store(0x11u8, ebpf::MM_REGION_SIZE - 1), "AccessViolation");
1304 assert_error!(m.store(0x11u8, ebpf::MM_REGION_SIZE + 1), "AccessViolation");
1305 assert_error!(m.store(0x11u8, ebpf::MM_REGION_SIZE + 2), "AccessViolation");
1308
1309 let mut mem1 = [0xFF; 4];
1310 let mut mem2 = [0xDD; 4];
1311 let mut m = unsafe {
1312 MemoryMapping::new(
1313 vec![
1314 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1315 MemoryRegion::new(&raw mut mem2, ebpf::MM_REGION_SIZE + 4),
1316 ],
1317 &config,
1318 SBPFVersion::V3,
1319 )
1320 .unwrap()
1321 };
1322 assert_error!(
1323 m.store(0x1122334455667788u64, ebpf::MM_REGION_SIZE),
1324 "AccessViolation"
1325 );
1326 }
1327
1328 #[test]
1329 fn test_unaligned_map_load_out_of_bounds() {
1330 let config = Config {
1331 aligned_memory_mapping: false,
1332 ..Config::default()
1333 };
1334
1335 let mem1 = [0xff];
1336 let mut m = unsafe {
1337 MemoryMapping::new(
1338 vec![MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE)],
1339 &config,
1340 SBPFVersion::V3,
1341 )
1342 .unwrap()
1343 };
1344 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 0xff);
1345 assert_error!(m.load::<u8>(ebpf::MM_REGION_SIZE - 1), "AccessViolation");
1346 assert_error!(m.load::<u8>(ebpf::MM_REGION_SIZE + 1), "AccessViolation");
1347 assert_error!(m.load::<u8>(ebpf::MM_REGION_SIZE + 2), "AccessViolation");
1348
1349 let mem1 = [0xFF; 4];
1350 let mem2 = [0xDD; 4];
1351 let mut m = unsafe {
1352 MemoryMapping::new(
1353 vec![
1354 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1355 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + 4),
1356 ],
1357 &config,
1358 SBPFVersion::V3,
1359 )
1360 .unwrap()
1361 };
1362 assert_error!(m.load::<u64>(ebpf::MM_REGION_SIZE), "AccessViolation");
1363 }
1364
1365 #[test]
1366 #[should_panic(expected = "AccessViolation")]
1367 fn test_store_readonly() {
1368 let config = Config {
1369 aligned_memory_mapping: false,
1370 ..Config::default()
1371 };
1372 let mut mem1 = [0xff, 0xff];
1373 let mem2 = [0xff, 0xff];
1374 let mut m = unsafe {
1375 MemoryMapping::new(
1376 vec![
1377 MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1378 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1379 ],
1380 &config,
1381 SBPFVersion::V3,
1382 )
1383 .unwrap()
1384 };
1385 m.store(0x11223344, ebpf::MM_REGION_SIZE).unwrap();
1386 }
1387
1388 #[test]
1389 fn test_unaligned_map_replace_region() {
1390 let config = Config {
1391 aligned_memory_mapping: false,
1392 ..Config::default()
1393 };
1394 let mem1 = [11];
1395 let mem2 = [22, 22];
1396 let mem3 = [33];
1397 let mut m = unsafe {
1398 MemoryMapping::new(
1399 vec![
1400 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1401 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1402 ],
1403 &config,
1404 SBPFVersion::V3,
1405 )
1406 .unwrap()
1407 };
1408
1409 assert_eq!(
1410 m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 1).unwrap(),
1411 mem1.as_ptr() as u64
1412 );
1413
1414 assert_eq!(
1415 m.map(
1416 AccessType::Load,
1417 ebpf::MM_REGION_SIZE + mem1.len() as u64,
1418 1,
1419 )
1420 .unwrap(),
1421 mem2.as_ptr() as u64
1422 );
1423
1424 assert_error!(
1425 unsafe {
1426 m.replace_region(
1427 2,
1428 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1429 )
1430 },
1431 "InvalidMemoryRegion(2)"
1432 );
1433
1434 let region_index = m
1435 .get_regions()
1436 .iter()
1437 .position(|mem| mem.vm_addr == ebpf::MM_REGION_SIZE + mem1.len() as u64)
1438 .unwrap();
1439
1440 assert_error!(
1442 unsafe {
1443 m.replace_region(
1444 region_index,
1445 MemoryRegion::new(
1446 &raw const mem3,
1447 ebpf::MM_REGION_SIZE + mem1.len() as u64 + 1,
1448 ),
1449 )
1450 },
1451 "InvalidMemoryRegion({})",
1452 region_index
1453 );
1454
1455 unsafe {
1456 m.replace_region(
1457 region_index,
1458 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1459 )
1460 .unwrap()
1461 };
1462
1463 assert_eq!(
1464 m.map(
1465 AccessType::Load,
1466 ebpf::MM_REGION_SIZE + mem1.len() as u64,
1467 1,
1468 )
1469 .unwrap(),
1470 mem3.as_ptr() as u64
1471 );
1472 }
1473
1474 #[test]
1475 fn test_aligned_map_replace_region() {
1476 let config = Config {
1477 aligned_memory_mapping: true,
1478 ..Config::default()
1479 };
1480 let mem1 = [11];
1481 let mem2 = [22, 22];
1482 let mem3 = [33, 33];
1483 let mut m = unsafe {
1484 MemoryMapping::new(
1485 vec![
1486 MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1487 MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE * 2),
1488 ],
1489 &config,
1490 SBPFVersion::V4,
1491 )
1492 .unwrap()
1493 };
1494
1495 assert_eq!(
1496 m.map(AccessType::Load, ebpf::MM_REGION_SIZE * 2, 1)
1497 .unwrap(),
1498 mem2.as_ptr() as u64
1499 );
1500
1501 assert_error!(
1503 unsafe {
1504 m.replace_region(
1505 3,
1506 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 2),
1507 )
1508 },
1509 "InvalidMemoryRegion(3)"
1510 );
1511
1512 assert_error!(
1514 unsafe {
1515 m.replace_region(
1516 2,
1517 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 3),
1518 )
1519 },
1520 "InvalidMemoryRegion(2)"
1521 );
1522
1523 assert_error!(
1525 unsafe {
1526 m.replace_region(
1527 2,
1528 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 3 - 1),
1529 )
1530 },
1531 "InvalidMemoryRegion(2)"
1532 );
1533
1534 unsafe {
1535 m.replace_region(
1536 2,
1537 MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 2),
1538 )
1539 .unwrap()
1540 };
1541
1542 assert_eq!(
1543 m.map(AccessType::Load, ebpf::MM_REGION_SIZE * 2, 1)
1544 .unwrap(),
1545 mem3.as_ptr() as u64
1546 );
1547 }
1548
1549 #[test]
1550 fn test_access_violation_handler_map() {
1551 for aligned_memory_mapping in [true, false] {
1552 let config = Config {
1553 aligned_memory_mapping,
1554 ..Config::default()
1555 };
1556 let original = [11, 22];
1557 let copied = Rc::new(RefCell::new(Vec::new()));
1558 let mut regions = vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)];
1559 regions[0].access_violation_handler_payload = Some(0);
1560
1561 let c = Rc::clone(&copied);
1562 let mut m = unsafe {
1563 MemoryMapping::new_with_access_violation_handler(
1564 regions,
1565 &config,
1566 SBPFVersion::V3,
1567 Box::new(move |region, _, _, _, _| {
1568 c.borrow_mut().extend_from_slice(&original);
1569 region.writable = true;
1570 region.host_addr = c.borrow().as_slice().as_ptr() as u64;
1571 }),
1572 )
1573 .unwrap()
1574 };
1575
1576 assert_eq!(
1577 m.map_with_access_violation_handler(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1578 .unwrap(),
1579 original.as_ptr() as u64
1580 );
1581 assert_eq!(
1582 m.map_with_access_violation_handler(AccessType::Store, ebpf::MM_REGION_SIZE, 1)
1583 .unwrap(),
1584 copied.borrow().as_ptr() as u64
1585 );
1586 }
1587 }
1588
1589 #[test]
1590 fn test_access_violation_handler_load_store() {
1591 for aligned_memory_mapping in [true, false] {
1592 let config = Config {
1593 aligned_memory_mapping,
1594 ..Config::default()
1595 };
1596 let original = [11, 22];
1597 let copied = Rc::new(RefCell::new(Vec::new()));
1598 let mut regions = vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)];
1599 regions[0].access_violation_handler_payload = Some(0);
1600
1601 let c = Rc::clone(&copied);
1602 let mut m = unsafe {
1603 MemoryMapping::new_with_access_violation_handler(
1604 regions,
1605 &config,
1606 SBPFVersion::V3,
1607 Box::new(move |region, _, _, _, _| {
1608 c.borrow_mut().extend_from_slice(&original);
1609 region.writable = true;
1610 region.host_addr = c.borrow().as_slice().as_ptr() as u64;
1611 }),
1612 )
1613 .unwrap()
1614 };
1615
1616 assert_eq!(
1617 m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 1).unwrap(),
1618 original.as_ptr() as u64
1619 );
1620
1621 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 11);
1622 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE + 1).unwrap(), 22);
1623 assert!(copied.borrow().is_empty());
1624
1625 m.store(33u8, ebpf::MM_REGION_SIZE).unwrap();
1626 assert_eq!(original[0], 11);
1627 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 33);
1628 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE + 1).unwrap(), 22);
1629 }
1630 }
1631
1632 #[test]
1633 fn test_access_violation_handler_region_id() {
1634 for aligned_memory_mapping in [true, false] {
1635 let config = Config {
1636 aligned_memory_mapping,
1637 ..Config::default()
1638 };
1639 let original1 = [11, 22];
1640 let original2 = [33, 44];
1641 let copied = Rc::new(RefCell::new(Vec::new()));
1642
1643 let mut regions = vec![
1644 MemoryRegion::new(&raw const original1, ebpf::MM_REGION_SIZE),
1645 MemoryRegion::new(&raw const original2, ebpf::MM_REGION_SIZE * 2),
1646 ];
1647 regions[0].access_violation_handler_payload = Some(42);
1648
1649 let c = Rc::clone(&copied);
1650 let mut m = unsafe {
1651 MemoryMapping::new_with_access_violation_handler(
1652 regions,
1653 &config,
1654 SBPFVersion::V3,
1655 Box::new(move |region, _, _, _, _| {
1656 assert_eq!(region.access_violation_handler_payload, Some(42));
1659 c.borrow_mut().extend_from_slice(&original1);
1660 region.writable = true;
1661 region.host_addr = c.borrow().as_slice().as_ptr() as u64;
1662 }),
1663 )
1664 .unwrap()
1665 };
1666
1667 m.store(55u8, ebpf::MM_REGION_SIZE).unwrap();
1668 assert_eq!(original1[0], 11);
1669 assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 55);
1670 }
1671 }
1672
1673 #[test]
1674 #[should_panic(expected = "AccessViolation")]
1675 fn test_map_access_violation_handler_error() {
1676 let config = Config::default();
1677 let original = [11, 22];
1678
1679 let m = unsafe {
1680 MemoryMapping::new_with_access_violation_handler(
1681 vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)],
1682 &config,
1683 SBPFVersion::V4,
1684 Box::new(default_access_violation_handler),
1685 )
1686 .unwrap()
1687 };
1688
1689 m.map(AccessType::Store, ebpf::MM_REGION_SIZE, 1).unwrap();
1690 }
1691
1692 #[test]
1693 #[should_panic(expected = "AccessViolation")]
1694 fn test_store_access_violation_handler_error() {
1695 let config = Config::default();
1696 let original = [11, 22];
1697
1698 let mut m = unsafe {
1699 MemoryMapping::new_with_access_violation_handler(
1700 vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)],
1701 &config,
1702 SBPFVersion::V4,
1703 Box::new(default_access_violation_handler),
1704 )
1705 .unwrap()
1706 };
1707
1708 m.store(33u8, ebpf::MM_REGION_SIZE).unwrap();
1709 }
1710
1711 #[test]
1712 fn test_access_violation_region_identification() {
1713 let config = Config::default();
1714 let original = [11, 22];
1715 let region = 0x10_0000_0000;
1716 let mut m = unsafe {
1717 MemoryMapping::new(
1718 vec![MemoryRegion::new(&raw const original, region)],
1719 &config,
1720 SBPFVersion::V4,
1721 )
1722 .unwrap()
1723 };
1724 let store_err_inbound = m.store(33u8, region).unwrap_err();
1725 assert_eq!(
1726 store_err_inbound.to_string(),
1727 "Access violation writing 1 bytes at address 0x1000000000 (in allocated region)"
1728 );
1729 let store_err_oob = m.load::<u64>(region + 3).unwrap_err();
1730 assert_eq!(
1731 store_err_oob.to_string(),
1732 "Access violation reading 8 bytes at address 0x1000000003 (in unallocated region)"
1733 );
1734 }
1735
1736 #[test]
1737 fn v4_aligned_mapping() {
1738 let config = Config {
1739 aligned_memory_mapping: false,
1740 ..Config::default()
1741 };
1742
1743 let mem = [11, 12];
1744 let mapping = unsafe {
1745 MemoryMapping::new_with_access_violation_handler(
1746 vec![MemoryRegion::new(&raw const mem, ebpf::MM_REGION_SIZE)],
1747 &config,
1748 SBPFVersion::V4,
1749 Box::new(default_access_violation_handler),
1750 )
1751 .unwrap()
1752 };
1753
1754 assert!(matches!(mapping.ty, MemoryMappingType::Aligned(_)));
1755 }
1756}