Skip to main content

solana_sbpf/
memory_region.rs

1//! This module defines memory regions
2
3use 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
13/* Explanation of the Gapped Memory
14
15    The MemoryMapping supports a special mapping mode which is used for the stack MemoryRegion.
16    In this mode the backing address space of the host is sliced in power-of-two aligned frames.
17    The exponent of this alignment is specified in vm_gap_shift. Then the virtual address space
18    of the guest is spread out in a way which leaves gaps, the same size as the frames, in
19    between the frames. This effectively doubles the size of the guests virtual address space.
20    But the actual mapped memory stays the same, as the gaps are not mapped and accessing them
21    results in an AccessViolation.
22
23    Guest: frame 0 | gap 0 | frame 1 | gap 1 | frame 2 | gap 2 | ...
24              |                /                 /
25              |          *----*    *------------*
26              |         /         /
27    Host:  frame 0 | frame 1 | frame 2 | ...
28*/
29
30/// Callback executed before generate_access_violation()
31pub type AccessViolationHandler = Box<dyn Fn(&mut MemoryRegion, u64, AccessType, u64, u64)>;
32/// Fail always
33#[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
43/// Types that can be used as backing memory for memory mappings.
44///
45/// ## Safety
46///
47/// The implementers must ensure that the returned address and byte length are contained by the
48/// implementing type's objects.
49pub unsafe trait HostMemoryObject {
50    /// Should the mapping region constructed by this object be considered writable?
51    const WRITABLE: bool;
52
53    /// The provenance-exposed address in the host address space for this object.
54    ///
55    /// Normally this is just an address of the pointer.
56    fn host_address(self) -> usize;
57    /// Number of contiguous bytes this object occupies.
58    fn byte_length(&self) -> usize;
59}
60
61/// Types that can be directly mapped into VM memory space should implement this trait.
62///
63/// The blanket implementations of [`HostMemoryObject`] for `*const T` where `T: VmExposable` allow
64/// exposing the implementing types to the guests directly.
65pub trait VmExposable {}
66
67/// Types that can be directly and mutably mapped into VM memory space.
68///
69/// The blanket implementations of [`HostMemoryObject`] for `*mut T` where `T: VmExposableMut` allow
70/// mutably exposing the implementing types to the guests directly.
71///
72/// ## Safety
73///
74/// The type must not have any invariants that could be broken by the guest's modifications to the
75/// data within objects of this type.
76pub 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/// Memory region for bounds checking and address translation
142#[derive(Default, Eq, PartialEq, Clone)]
143#[repr(C, align(32))]
144pub struct MemoryRegion {
145    /// start host address
146    pub host_addr: u64,
147    /// start virtual address
148    pub vm_addr: u64,
149    /// Length in bytes
150    pub len: u64,
151    /// Size of regular gaps as bit shift (63 means this region is continuous)
152    pub vm_gap_shift: u8,
153    /// Is `AccessType::Store` allowed without triggering an access violation
154    pub writable: bool,
155    /// User defined payload for the [AccessViolationHandler]
156    pub access_violation_handler_payload: Option<u16>,
157}
158
159impl MemoryRegion {
160    /// Create a VM memory region with host `address` pointing to a `len` bytes of data.
161    ///
162    /// This region will be made available in the guest at `vm_addr`.
163    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    /// Creates a new `MemoryRegion` backed by the provided host memory.
188    ///
189    /// The backing memory must remain allocated for the duration of the returned `MemoryRegion`.
190    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    /// Creates a new gapped `MemoryRegion` backed by the provided host memory.
196    ///
197    /// The backing memory must remain allocated for the duration of the returned `MemoryRegion`.
198    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    /// Returns the vm address space covered by this MemoryRegion
205    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    /// Convert a virtual machine address into a host address
214    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        // This can happen if a region starts at an offset from the base region
220        // address, eg with rodata regions if config.optimize_rodata = true, see
221        // Elf::get_ro_region.
222        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            // fast path for non-gapped regions
229            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/// Type of memory access
283#[derive(Clone, Copy, PartialEq, Eq, Debug)]
284pub enum AccessType {
285    /// Read
286    Load,
287    /// Write
288    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
300/// Memory mapping based on eytzinger search.
301pub struct UnalignedMemoryMapping {
302    /// Common parts
303    regions: Box<[MemoryRegion]>,
304    /// Regions vm_addr fields in Eytzinger order
305    region_addresses: Box<[u64]>,
306    /// Converts the Eytzinger order back to the original order
307    region_index_lookup: Box<[usize]>,
308    /// Cache of the last `MappingCache::SIZE` vm_addr => region_index lookups
309    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    /// Create an uninitialized UnalignedMapping
337    ///
338    /// # Safety
339    ///
340    /// Refer to [`MemoryMapping::new_uninitialized`].
341    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    /// Creates a new MemoryMapping structure from the given regions
352    ///
353    /// # Safety
354    ///
355    /// Refer to [`MemoryMapping::new_uninitialized`].
356    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    /// Initialize the memory mapping
363    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    /// Returns the `MemoryRegion` which may contain the given address.
379    #[allow(clippy::arithmetic_side_effects)]
380    #[inline(always)]
381    pub fn find_region(&self, vm_addr: u64) -> Option<(usize, &MemoryRegion)> {
382        // Safety:
383        // &mut references to the mapping cache are only created internally from methods that do not
384        // invoke each other. UnalignedMemoryMapping is !Sync, so the cache reference below is
385        // guaranteed to be unique.
386        let cache = unsafe { &mut *self.cache.get() };
387        if let Some(index) = cache.find(vm_addr) {
388            // Safety:
389            // Cached index, we validated it before caching it. See the corresponding safety section
390            // in the miss branch.
391            Some((index, unsafe { self.regions.get_unchecked(index) }))
392        } else {
393            let mut index = 1;
394            while index <= self.region_addresses.len() {
395                // Safety:
396                // we start the search at index=1 and in the loop condition check
397                // for index <= len, so bound checks can be avoided
398                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            // Safety:
407            // we check for index==0 above, and by construction if we get here index
408            // must be contained in region
409            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    /// Replaces the `MemoryRegion` at the given index
417    ///
418    /// # Safety
419    ///
420    /// Refer to [`MemoryMapping::new_uninitialized`].
421    #[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/// Memory mapping that uses the upper half of an address to identify the
434/// underlying memory region.
435#[derive(Debug)]
436pub struct AlignedMemoryMapping {
437    regions: Vec<MemoryRegion>,
438    allow_memory_region_zero: bool,
439}
440
441impl AlignedMemoryMapping {
442    /// Creates a new initialized MemoryMapping structure from the given regions
443    ///
444    /// # Safety
445    ///
446    /// Refer to [`MemoryMapping::new_uninitialized`].
447    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    /// Create an uninitialized MemoryMapping
454    ///
455    /// # Safety
456    ///
457    /// Refer to [`MemoryMapping::new_uninitialized`].
458    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    /// Initialize the memory mapping by sorting its regions and filling gaps
466    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    /// Returns the `MemoryRegion` which may contain the given address.
512    #[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            // Safety: bounds check above
517            let region = unsafe { self.regions.get_unchecked(index) };
518            return Some((index, region));
519        }
520        None
521    }
522
523    /// Replaces the `MemoryRegion` at the given index
524    ///
525    /// # Safety
526    ///
527    /// Refer to [`MemoryMapping::new_uninitialized`].
528    #[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
551/// Common parts of [UnalignedMemoryMapping] and [AlignedMemoryMapping]
552pub struct MemoryMapping {
553    /// Access violation handler
554    access_violation_handler: AccessViolationHandler,
555    max_call_depth: i64,
556    stack_frame_size: i64,
557    disable_address_translation: bool,
558    /// Executable sbpf_version
559    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/// Maps virtual memory to host memory.
576#[derive(Debug)]
577pub enum MemoryMappingType {
578    /// Aligned memory mapping which uses the upper half of an address to
579    /// identify the underlying memory region.
580    Aligned(AlignedMemoryMapping),
581    /// Memory mapping that allows mapping unaligned memory regions.
582    Unaligned(UnalignedMemoryMapping),
583}
584
585impl MemoryMapping {
586    /// Creates a new memory mapping.
587    ///
588    /// Uses aligned or unaligned memory mapping depending on the value of
589    /// `config.aligned_memory_mapping=true`.
590    ///
591    /// # Safety
592    ///
593    /// In addition to the requirements of [`MemoryMapping::new_uninitialized`], the provided
594    /// `access_violation_handler` must return with its [`MemoryRegion`] unmodified, or initialized
595    /// to another equally correct [`MemoryRegion`] as described in the safety invariants for
596    /// `new_uninitialized`.
597    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    /// Creates an unitialized memory mapping
610    ///
611    /// # Safety
612    ///
613    /// The memory pointed to by the [`MemoryRegion`]s must point to a valid object live for the
614    /// duration of this `MemoryMapping`.
615    ///
616    /// For `MemoryRegion`s marked writable, the memory pointed to by the memory region must accept
617    /// arbitrary bytes being overwritten without it resulting in unsoundness (due to e.g. broken
618    /// internal type invariants.)
619    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    /// Creates a new memory mapping for tests and benches.
647    ///
648    /// `access_violation_handler` defaults to a function which always returns an error.
649    ///
650    /// # Safety
651    ///
652    /// Refer to [`MemoryMapping::new_uninitialized`].
653    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    /// Map virtual memory to host memory.
667    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    /// Map virtual memory to host memory and potentially call the [AccessViolationHandler].
682    ///
683    /// This requires the [MemoryMapping] to be mutable and
684    /// can cause previously translated addresses to become invalid.
685    #[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    /// Loads `size_of::<T>()` bytes from the given address.
719    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    /// Store `value` at the given address.
732    #[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    /// Returns the `MemoryRegion` which may contain the given address.
747    #[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    /// Returns the `MemoryRegion`s in this mapping.
757    #[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    /// Returns the [`MemoryRegion`]s as mutable.
766    ///
767    /// Modifying the regions might break the initialization constraints, so this function
768    /// uninitializes the mapping. The memory mappings must be initialized
769    /// again with [`Self::initialize`] before further use.
770    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    /// Replaces the `MemoryRegion` at the given index
782    ///
783    /// # Safety
784    ///
785    /// Refer to [`MemoryMapping::new_uninitialized`].
786    #[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    /// Initialize the MemoryMapping
810    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/// Fast, small linear cache used to speed up unaligned memory mapping.
861#[derive(Debug)]
862struct MappingCache {
863    // The cached entries.
864    entries: [(Range<u64>, usize); MappingCache::SIZE],
865    // Index of the last accessed memory region.
866    //
867    // New entries are written backwards, so that find() can always scan
868    // forward which is faster.
869    head: usize,
870}
871
872impl MappingCache {
873    // must be a power of two
874    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            // Safety:
888            // index is guaranteed to be between 0..Self::SIZE
889            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        // Safety:
902        // self.head is guaranteed to be between 0..Self::SIZE
903        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        // this gets us line coverage for the case where we're completely
1306        // outside the address space (the case above is just on the edge)
1307        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        // old.vm_addr != new.vm_addr
1441        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        // index > regions.len()
1502        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        // index != addr >> VIRTUAL_ADDRESS_BITS
1513        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        // index + len != addr >> VIRTUAL_ADDRESS_BITS
1524        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                        // check that the argument passed to MemoryRegion::new is then passed to the
1657                        // callback
1658                        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}