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, StableResult},
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    /// The provenance-exposed address in the host address space for this object.
51    ///
52    /// Normally this is just an address of the pointer.
53    fn host(self) -> HostBuffer;
54}
55
56/// Types that can be directly mapped into VM memory space should implement this trait.
57///
58/// The blanket implementations of [`HostMemoryObject`] for `*const T` where `T: VmExposable` allow
59/// exposing the implementing types to the guests directly.
60pub trait VmExposable {}
61
62/// Types that can be directly and mutably mapped into VM memory space.
63///
64/// The blanket implementations of [`HostMemoryObject`] for `*mut T` where `T: VmExposableMut` allow
65/// mutably exposing the implementing types to the guests directly.
66///
67/// ## Safety
68///
69/// The type must not have any invariants that could be broken by the guest's modifications to the
70/// data within objects of this type.
71pub unsafe trait VmExposableMut {}
72
73unsafe impl VmExposableMut for u8 {}
74impl<T: VmExposableMut> VmExposable for T {}
75
76unsafe impl<T: VmExposable> HostMemoryObject for *const T {
77    fn host(self) -> HostBuffer {
78        HostBuffer::Immutable(ptr::slice_from_raw_parts(
79            self.cast(),
80            std::mem::size_of::<T>(),
81        ))
82    }
83}
84
85unsafe impl<T: VmExposableMut> HostMemoryObject for *mut T {
86    fn host(self) -> HostBuffer {
87        HostBuffer::Mutable(ptr::slice_from_raw_parts_mut(
88            self.cast(),
89            std::mem::size_of::<T>(),
90        ))
91    }
92}
93
94unsafe impl<T: VmExposable> HostMemoryObject for *const [T] {
95    fn host(self) -> HostBuffer {
96        HostBuffer::Immutable(ptr::slice_from_raw_parts(
97            self.cast(),
98            self.len().checked_mul(core::mem::size_of::<T>()).unwrap(),
99        ))
100    }
101}
102
103unsafe impl<T: VmExposableMut> HostMemoryObject for *mut [T] {
104    fn host(self) -> HostBuffer {
105        HostBuffer::Mutable(ptr::slice_from_raw_parts_mut(
106            self.cast(),
107            self.len().checked_mul(core::mem::size_of::<T>()).unwrap(),
108        ))
109    }
110}
111
112unsafe impl<T: VmExposable, const N: usize> HostMemoryObject for *const [T; N] {
113    fn host(self) -> HostBuffer {
114        HostBuffer::Immutable(ptr::slice_from_raw_parts(
115            self.cast(),
116            N.checked_mul(core::mem::size_of::<T>()).unwrap(),
117        ))
118    }
119}
120
121unsafe impl<T: VmExposableMut, const N: usize> HostMemoryObject for *mut [T; N] {
122    fn host(self) -> HostBuffer {
123        HostBuffer::Mutable(ptr::slice_from_raw_parts_mut(
124            self.cast(),
125            N.checked_mul(core::mem::size_of::<T>()).unwrap(),
126        ))
127    }
128}
129
130/// Either mutable or immutable slice, returned by [`MemoryRegion::host_buffer`].
131#[derive(PartialEq, Eq, Copy, Clone, Debug)]
132pub enum HostBuffer {
133    /// The `MemoryRegion` is read-only.
134    Immutable(*const [u8]),
135    /// The `MemoryRegion` is writable (`AccessType::Store` is permitted.)
136    Mutable(*mut [u8]),
137}
138
139impl HostBuffer {
140    /// The length of this host buffer.
141    pub fn len(&self) -> usize {
142        match self {
143            HostBuffer::Immutable(p) => p.len(),
144            HostBuffer::Mutable(p) => p.len(),
145        }
146    }
147
148    /// `true` if this host buffer has a length of 0.
149    pub fn is_empty(&self) -> bool {
150        match self {
151            HostBuffer::Immutable(p) => p.is_empty(),
152            HostBuffer::Mutable(p) => p.is_empty(),
153        }
154    }
155
156    /// `true` if this is a `HostBuffer::Mutable`.
157    pub fn is_mutable(&self) -> bool {
158        matches!(self, HostBuffer::Mutable(_))
159    }
160
161    /// Make this host buffer mutable.
162    ///
163    /// # Safety
164    ///
165    /// This host buffer *must* have been initially constructed with a mutable pointer.
166    pub unsafe fn mutable(self) -> Self {
167        match self {
168            HostBuffer::Immutable(p) => HostBuffer::Mutable(p.cast_mut()),
169            HostBuffer::Mutable(_) => self,
170        }
171    }
172
173    /// Make this host buffer immutable.
174    pub fn immutable(self) -> Self {
175        match self {
176            HostBuffer::Immutable(_) => self,
177            HostBuffer::Mutable(p) => Self::Immutable(p.cast_const()),
178        }
179    }
180
181    /// Subslice this host buffer with the provided range.
182    #[inline]
183    pub fn get(self, range: std::ops::Range<usize>) -> Option<Self> {
184        if range.end > self.len() {
185            return None;
186        }
187        let new_len = range.len();
188        unsafe {
189            // SAFETY:
190            //
191            // Contract from `ptr::add`: If the computed offset is non-zero, then self
192            // must be derived from a pointer to some allocation, and the entire memory
193            // range between self and the result must be in bounds of that allocation.
194            // In particular, this range must not "wrap around" the edge of the address
195            // space.
196            //
197            // Evidence: We take care to ensure to only get here if the `begin_offset`
198            // (and a stronger condition: `begin_offset + len`) would be within `len()`
199            // of the base pointer.
200            //
201            // Contract from `ptr::add`: The offset in bytes, count * size_of::<T>(),
202            // computed on mathematical integers (without "wrapping around"), must fit
203            // in an isize.
204            //
205            // Evidence: Allocation size in Rust, including those of the region backing
206            // memory here specifically, may not exceed `isize::MAX` bytes.
207            Some(match self {
208                HostBuffer::Immutable(p) => HostBuffer::Immutable(ptr::slice_from_raw_parts(
209                    p.byte_add(range.start).cast(),
210                    new_len,
211                )),
212                HostBuffer::Mutable(p) => HostBuffer::Mutable(ptr::slice_from_raw_parts_mut(
213                    p.byte_add(range.start).cast(),
214                    new_len,
215                )),
216            })
217        }
218    }
219
220    /// Pointer to a slice of the host memory.
221    #[inline(always)]
222    pub fn ptr(self) -> *const [u8] {
223        match self {
224            HostBuffer::Immutable(p) => p,
225            HostBuffer::Mutable(p) => p,
226        }
227    }
228
229    /// Mutble pointer to a slice of the host memory.
230    #[inline(always)]
231    pub fn ptr_mut(self) -> *mut [u8] {
232        match self {
233            HostBuffer::Immutable(p) => {
234                debug_assert!(false, "ptr_mut, but buffer is immutable");
235                p.cast_mut()
236            }
237            HostBuffer::Mutable(p) => p,
238        }
239    }
240}
241
242unsafe impl HostMemoryObject for HostBuffer {
243    fn host(self) -> HostBuffer {
244        self
245    }
246}
247
248/// Memory region for bounds checking and address translation
249#[derive(Eq, PartialEq, Clone)]
250pub struct MemoryRegion {
251    host: HostBuffer,
252    /// start virtual address
253    vm_addr: u64,
254    /// Size of regular gaps as bit shift (63 means this region is continuous)
255    vm_gap_shift: u8,
256    /// User defined payload for the [AccessViolationHandler]
257    pub access_violation_handler_payload: Option<u16>,
258}
259
260impl MemoryRegion {
261    /// Create a VM memory region with host `address` pointing to a `len` bytes of data.
262    ///
263    /// This region will be made available in the guest at `vm_addr`.
264    fn new_internal(host: HostBuffer, vm_addr: u64, vm_gap_size: u64) -> Self {
265        let mut vm_gap_shift = (std::mem::size_of::<u64>() as u8)
266            .saturating_mul(8)
267            .saturating_sub(1);
268        if vm_gap_size > 0 {
269            vm_gap_shift = vm_gap_shift.saturating_sub(vm_gap_size.leading_zeros() as u8);
270            debug_assert_eq!(Some(vm_gap_size), 1_u64.checked_shl(vm_gap_shift as u32));
271        };
272        MemoryRegion {
273            host,
274            vm_addr,
275            vm_gap_shift,
276            access_violation_handler_payload: None,
277        }
278    }
279
280    /// Creates a new, empty `MemoryRegion`.
281    ///
282    /// This does not require to provide any backing host memory.
283    pub fn new_empty(vm_addr: u64) -> Self {
284        const EMPTY: &[u8] = &[];
285        Self::new_internal((&raw const *EMPTY).host(), vm_addr, 0)
286    }
287
288    /// Creates a new `MemoryRegion` backed by the provided host memory.
289    ///
290    /// The backing memory must remain allocated for the duration of the returned `MemoryRegion`.
291    pub fn new<HO: HostMemoryObject>(host: HO, vm_addr: u64) -> Self {
292        Self::new_internal(host.host(), vm_addr, 0)
293    }
294
295    /// Creates a new gapped `MemoryRegion` backed by the provided host memory.
296    ///
297    /// The backing memory must remain allocated for the duration of the returned `MemoryRegion`.
298    pub fn new_gapped<HO: HostMemoryObject>(host: HO, vm_addr: u64, vm_gap_size: u64) -> Self {
299        Self::new_internal(host.host(), vm_addr, vm_gap_size)
300    }
301
302    /// Redirect this memory region to a different location in host memory.
303    ///
304    /// Depending on whether `HO` is mutable, the writability of the region is adjusted as well.
305    ///
306    /// # Safety
307    ///
308    /// If this `MemoryRegion` is a part of a [`MemoryMapping`] then, after redirection, this region
309    /// must adhere to all the same contracts as the `MemoryRegion`s used for
310    /// [`MemoryMapping::replace_region`].
311    pub unsafe fn redirect<HO: HostMemoryObject>(&mut self, host: HO) {
312        self.host = host.host();
313    }
314
315    /// Ensure that this memory region is immutable.
316    pub fn make_immutable(&mut self) {
317        unsafe {
318            // SAFETY:
319            // Contract from `MemoryRegion::redirect`: memory region must be live for
320            // the duration of the mapping.
321            //
322            // Evidence: Since we aren't changing where the host buffer is pointing at,
323            // the condition must already have been satisfied at the time this function was called
324            // and thus remains satisfied.
325            //
326            // Contract from `MemoryRegion::redirect`: For `MemoryRegions` marked writable...
327            //
328            // Evidence: Memory region is no longer writable.
329            self.redirect(self.host_buffer().immutable());
330        }
331    }
332
333    /// Returns the vm address space covered by this MemoryRegion
334    pub fn vm_addr_range(&self) -> Range<u64> {
335        let bytes = self.len() as u64;
336        if self.vm_gap_shift == 63 {
337            self.vm_addr..self.vm_addr.saturating_add(bytes)
338        } else {
339            self.vm_addr..self.vm_addr.saturating_add(bytes.saturating_mul(2))
340        }
341    }
342
343    /// Return the raw slice to the host memory that this memory region points at.
344    ///
345    /// This can be used to construct a new memory region.
346    pub fn host_buffer(&self) -> HostBuffer {
347        self.host
348    }
349
350    /// Length of this memory region in bytes.
351    pub fn len(&self) -> usize {
352        self.host.len()
353    }
354
355    /// Is the length of this memory region 0 bytes?
356    pub fn is_empty(&self) -> bool {
357        self.host.is_empty()
358    }
359
360    /// Return the `gap_size` with which the memory region has been constructed.
361    pub fn gap_size(&self) -> u64 {
362        if self.vm_gap_shift == 63 {
363            0
364        } else {
365            1 << self.vm_gap_shift
366        }
367    }
368
369    /// Convert a virtual machine address into a host slice.
370    ///
371    /// The returned slice will have exactly `len` bytes. If the provided `vm_addr` does not
372    /// correlate to a valid subslice of this region, a `None` will be returned.
373    #[inline]
374    pub(crate) fn vm_to_host_buffer(&self, vm_addr: u64, len: u64) -> Option<HostBuffer> {
375        // This can happen if a region starts at an offset from the base region
376        // address, eg with rodata regions if config.optimize_rodata = true, see
377        // Elf::get_ro_region.
378        if vm_addr < self.vm_addr {
379            return None;
380        }
381
382        let begin_offset = vm_addr.saturating_sub(self.vm_addr);
383        if self.vm_gap_shift == 63 {
384            // fast path for non-gapped regions
385            if let Some(end_offset) = begin_offset.checked_add(len) {
386                return self.host.get(begin_offset as usize..end_offset as usize);
387            }
388            return None;
389        }
390
391        let is_in_gap = (begin_offset
392            .checked_shr(self.vm_gap_shift as u32)
393            .unwrap_or(0)
394            & 1)
395            == 1;
396        let gap_mask = (-1i64).checked_shl(self.vm_gap_shift as u32).unwrap_or(0) as u64;
397        let gapped_offset =
398            (begin_offset & gap_mask).checked_shr(1).unwrap_or(0) | (begin_offset & !gap_mask);
399        if let Some(end_offset) = gapped_offset.checked_add(len) {
400            if !is_in_gap {
401                return self.host.get(gapped_offset as usize..end_offset as usize);
402            }
403        }
404        None
405    }
406}
407
408impl fmt::Debug for MemoryRegion {
409    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
410        let vm_addr = self.vm_addr_range();
411        let (host_addr, len, writable) = match self.host {
412            HostBuffer::Immutable(p) => (p.addr() as u64, p.len() as u64, false),
413            HostBuffer::Mutable(p) => (p.addr() as u64, p.len() as u64, true),
414        };
415        write!(
416            f,
417            "host_addr: {:#x?}-{:#x?}, vm_addr: {:#x?}-{:#x?}, len: {}, writable: {}, payload {:?}",
418            host_addr,
419            host_addr.saturating_add(len),
420            vm_addr.start,
421            vm_addr.end,
422            len,
423            writable,
424            self.access_violation_handler_payload,
425        )
426    }
427}
428
429impl std::cmp::PartialOrd for MemoryRegion {
430    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
431        Some(self.cmp(other))
432    }
433}
434
435impl std::cmp::Ord for MemoryRegion {
436    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
437        self.vm_addr.cmp(&other.vm_addr)
438    }
439}
440
441/// Type of memory access
442#[derive(Clone, Copy, PartialEq, Eq, Debug)]
443pub enum AccessType {
444    /// Read
445    Load,
446    /// Write
447    Store,
448}
449
450impl std::fmt::Display for AccessType {
451    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
452        f.write_str(match self {
453            Self::Load => "reading",
454            Self::Store => "writing",
455        })
456    }
457}
458
459/// Memory mapping based on eytzinger search.
460pub struct UnalignedMemoryMapping {
461    /// Common parts
462    regions: Box<[MemoryRegion]>,
463    /// Regions vm_addr fields in Eytzinger order
464    region_addresses: Box<[u64]>,
465    /// Converts the Eytzinger order back to the original order
466    region_index_lookup: Box<[usize]>,
467    /// Cache of the last `MappingCache::SIZE` vm_addr => region_index lookups
468    cache: UnsafeCell<MappingCache>,
469}
470
471impl fmt::Debug for UnalignedMemoryMapping {
472    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473        f.debug_struct("UnalignedMemoryMapping")
474            .field("regions", &self.regions)
475            .field("cache", &self.cache)
476            .finish()
477    }
478}
479
480impl UnalignedMemoryMapping {
481    fn construct_eytzinger_order(&mut self, mut in_index: usize, out_index: usize) -> usize {
482        if out_index >= self.regions.len() {
483            return in_index;
484        }
485        in_index =
486            self.construct_eytzinger_order(in_index, out_index.saturating_mul(2).saturating_add(1));
487        self.region_addresses[out_index] = self.regions[in_index].vm_addr;
488        self.region_index_lookup[out_index] = in_index;
489        self.construct_eytzinger_order(
490            in_index.saturating_add(1),
491            out_index.saturating_mul(2).saturating_add(2),
492        )
493    }
494
495    /// Create an uninitialized UnalignedMapping
496    ///
497    /// # Safety
498    ///
499    /// Refer to [`MemoryMapping::new_uninitialized`].
500    pub unsafe fn new_uninitialized(regions: Vec<MemoryRegion>) -> Self {
501        let number_of_regions = regions.len();
502        Self {
503            regions: regions.into_boxed_slice(),
504            region_addresses: vec![0; number_of_regions].into_boxed_slice(),
505            region_index_lookup: vec![0; number_of_regions].into_boxed_slice(),
506            cache: UnsafeCell::new(MappingCache::new()),
507        }
508    }
509
510    /// Creates a new MemoryMapping structure from the given regions
511    ///
512    /// # Safety
513    ///
514    /// Refer to [`MemoryMapping::new_uninitialized`].
515    pub unsafe fn new(regions: Vec<MemoryRegion>) -> Result<Self, EbpfError> {
516        let mut mapping = Self::new_uninitialized(regions);
517        mapping.initialize()?;
518        Ok(mapping)
519    }
520
521    /// Initialize the memory mapping
522    pub fn initialize(&mut self) -> Result<(), EbpfError> {
523        self.regions.sort();
524        let number_of_regions = self.regions.len();
525        for index in 1..number_of_regions {
526            let first = &self.regions[index.saturating_sub(1)];
527            let second = &self.regions[index];
528            if first.vm_addr_range().end > second.vm_addr {
529                return Err(EbpfError::InvalidMemoryRegion(index));
530            }
531        }
532
533        self.construct_eytzinger_order(0, 0);
534        Ok(())
535    }
536
537    /// Returns the `MemoryRegion` which may contain the given address.
538    #[allow(clippy::arithmetic_side_effects)]
539    #[inline(always)]
540    pub fn find_region(&self, vm_addr: u64) -> Option<(usize, &MemoryRegion)> {
541        // Safety:
542        // &mut references to the mapping cache are only created internally from methods that do not
543        // invoke each other. UnalignedMemoryMapping is !Sync, so the cache reference below is
544        // guaranteed to be unique.
545        let cache = unsafe { &mut *self.cache.get() };
546        if let Some(index) = cache.find(vm_addr) {
547            // Safety:
548            // Cached index, we validated it before caching it. See the corresponding safety section
549            // in the miss branch.
550            Some((index, unsafe { self.regions.get_unchecked(index) }))
551        } else {
552            let mut index = 1;
553            while index <= self.region_addresses.len() {
554                // Safety:
555                // we start the search at index=1 and in the loop condition check
556                // for index <= len, so bound checks can be avoided
557                index = (index << 1)
558                    + unsafe { *self.region_addresses.get_unchecked(index - 1) <= vm_addr }
559                        as usize;
560            }
561            index >>= index.trailing_zeros() + 1;
562            if index == 0 {
563                return None;
564            }
565            // Safety:
566            // we check for index==0 above, and by construction if we get here index
567            // must be contained in region
568            index = unsafe { *self.region_index_lookup.get_unchecked(index - 1) };
569            let region = unsafe { self.regions.get_unchecked(index) };
570            cache.insert(region.vm_addr_range(), index);
571            Some((index, region))
572        }
573    }
574
575    /// Replaces the `MemoryRegion` at the given index
576    ///
577    /// # Safety
578    ///
579    /// Refer to [`MemoryMapping::new_uninitialized`].
580    #[inline(always)]
581    pub unsafe fn replace_region(
582        &mut self,
583        index: usize,
584        region: MemoryRegion,
585    ) -> Result<(), EbpfError> {
586        self.regions[index] = region;
587        self.cache.get_mut().flush();
588        Ok(())
589    }
590}
591
592/// Memory mapping that uses the upper half of an address to identify the
593/// underlying memory region.
594#[derive(Debug)]
595pub struct AlignedMemoryMapping {
596    regions: Vec<MemoryRegion>,
597}
598
599impl AlignedMemoryMapping {
600    /// Creates a new initialized MemoryMapping structure from the given regions
601    ///
602    /// # Safety
603    ///
604    /// Refer to [`MemoryMapping::new_uninitialized`].
605    pub unsafe fn new(regions: Vec<MemoryRegion>) -> Result<Self, EbpfError> {
606        let mut mapping = Self::new_uninitialized(regions);
607        mapping.initialize()?;
608        Ok(mapping)
609    }
610
611    /// Create an uninitialized MemoryMapping
612    ///
613    /// # Safety
614    ///
615    /// Refer to [`MemoryMapping::new_uninitialized`].
616    pub unsafe fn new_uninitialized(regions: Vec<MemoryRegion>) -> Self {
617        Self { regions }
618    }
619
620    /// Initialize the memory mapping by sorting its regions and filling gaps
621    pub fn initialize(&mut self) -> Result<(), EbpfError> {
622        static EMPTY_SLICE: &[u8] = &[];
623        self.regions.sort();
624        let mut expected_region_index = 0;
625        while expected_region_index < self.regions.len() {
626            let actual_region_index = self
627                .regions
628                .get(expected_region_index)
629                .unwrap()
630                .vm_addr
631                .checked_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32)
632                .unwrap_or(0) as usize;
633            if actual_region_index > expected_region_index {
634                self.regions.insert(
635                    expected_region_index,
636                    MemoryRegion::new(
637                        &raw const *EMPTY_SLICE,
638                        (expected_region_index as u64).saturating_mul(ebpf::MM_REGION_SIZE),
639                    ),
640                );
641            } else if actual_region_index < expected_region_index {
642                return Err(EbpfError::InvalidMemoryRegion(actual_region_index));
643            }
644            expected_region_index = expected_region_index.saturating_add(1);
645        }
646
647        Ok(())
648    }
649
650    /// Returns the `MemoryRegion` which may contain the given address.
651    #[inline(always)]
652    pub fn find_region(&self, vm_addr: u64) -> Option<(usize, &MemoryRegion)> {
653        let index = vm_addr.wrapping_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32) as usize;
654        if index < self.regions.len() {
655            // Safety: bounds check above
656            let region = unsafe { self.regions.get_unchecked(index) };
657            return Some((index, region));
658        }
659        None
660    }
661
662    /// Replaces the `MemoryRegion` at the given index
663    ///
664    /// # Safety
665    ///
666    /// Refer to [`MemoryMapping::new_uninitialized`].
667    #[inline(always)]
668    pub unsafe fn replace_region(
669        &mut self,
670        index: usize,
671        region: MemoryRegion,
672    ) -> Result<(), EbpfError> {
673        let begin_index = region
674            .vm_addr
675            .checked_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32)
676            .unwrap_or(0) as usize;
677        let end_index = region
678            .vm_addr
679            .saturating_add((region.len() as u64).saturating_sub(1))
680            .checked_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32)
681            .unwrap_or(0) as usize;
682        if begin_index != index || end_index != index {
683            return Err(EbpfError::InvalidMemoryRegion(index));
684        }
685        self.regions[index] = region;
686        Ok(())
687    }
688}
689
690/// Common parts of [UnalignedMemoryMapping] and [AlignedMemoryMapping]
691pub struct MemoryMapping {
692    /// Access violation handler
693    access_violation_handler: AccessViolationHandler,
694    max_call_depth: i64,
695    stack_frame_size: i64,
696    disable_address_translation: bool,
697    /// Executable sbpf_version
698    sbpf_version: SBPFVersion,
699    initialized: bool,
700    ty: MemoryMappingType,
701}
702
703impl fmt::Debug for MemoryMapping {
704    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
705        f.debug_struct("MemoryMapping")
706            .field("max_call_depth", &self.max_call_depth)
707            .field("stack_frame_size", &self.stack_frame_size)
708            .field("sbpf_version", &self.sbpf_version)
709            .field("ty", &self.ty)
710            .finish()
711    }
712}
713
714/// Maps virtual memory to host memory.
715#[derive(Debug)]
716pub enum MemoryMappingType {
717    /// Aligned memory mapping which uses the upper half of an address to
718    /// identify the underlying memory region.
719    Aligned(AlignedMemoryMapping),
720    /// Memory mapping that allows mapping unaligned memory regions.
721    Unaligned(UnalignedMemoryMapping),
722}
723
724impl MemoryMapping {
725    /// Creates a new memory mapping.
726    ///
727    /// Uses aligned or unaligned memory mapping depending on the value of
728    /// `config.aligned_memory_mapping=true`.
729    ///
730    /// # Safety
731    ///
732    /// In addition to the requirements of [`MemoryMapping::new_uninitialized`], the provided
733    /// `access_violation_handler` must return with its [`MemoryRegion`] unmodified, or initialized
734    /// to another equally correct [`MemoryRegion`] as described in the safety invariants for
735    /// `new_uninitialized`.
736    pub unsafe fn new_with_access_violation_handler(
737        regions: Vec<MemoryRegion>,
738        config: &Config,
739        sbpf_version: SBPFVersion,
740        access_violation_handler: AccessViolationHandler,
741    ) -> Result<Self, EbpfError> {
742        let mut mapping =
743            Self::new_uninitialized(regions, config, sbpf_version, access_violation_handler);
744        mapping.initialize()?;
745        Ok(mapping)
746    }
747
748    /// Creates an unitialized memory mapping
749    ///
750    /// # Safety
751    ///
752    /// The memory pointed to by the [`MemoryRegion`]s must point to a valid object live for the
753    /// duration of this `MemoryMapping`.
754    ///
755    /// For `MemoryRegion`s marked writable, the memory pointed to by the memory region must accept
756    /// arbitrary bytes being overwritten without it resulting in unsoundness (due to e.g. broken
757    /// internal type invariants.)
758    pub unsafe fn new_uninitialized(
759        regions: Vec<MemoryRegion>,
760        config: &Config,
761        sbpf_version: SBPFVersion,
762        access_violation_handler: AccessViolationHandler,
763    ) -> Self {
764        let ty = if sbpf_version >= SBPFVersion::V4 || config.aligned_memory_mapping {
765            MemoryMappingType::Aligned(AlignedMemoryMapping::new_uninitialized(regions))
766        } else {
767            debug_assert!(
768                sbpf_version <= SBPFVersion::V3,
769                "SBPFv4 and later versions do not support unaligned memory"
770            );
771            MemoryMappingType::Unaligned(UnalignedMemoryMapping::new_uninitialized(regions))
772        };
773
774        Self {
775            access_violation_handler: Box::new(access_violation_handler),
776            max_call_depth: config.max_call_depth as i64,
777            stack_frame_size: config.stack_frame_size as i64,
778            disable_address_translation: !config.enable_address_translation,
779            sbpf_version,
780            initialized: false,
781            ty,
782        }
783    }
784
785    /// Creates a new memory mapping for tests and benches.
786    ///
787    /// `access_violation_handler` defaults to a function which always returns an error.
788    ///
789    /// # Safety
790    ///
791    /// Refer to [`MemoryMapping::new_uninitialized`].
792    pub unsafe fn new(
793        regions: Vec<MemoryRegion>,
794        config: &Config,
795        sbpf_version: SBPFVersion,
796    ) -> Result<Self, EbpfError> {
797        Self::new_with_access_violation_handler(
798            regions,
799            config,
800            sbpf_version,
801            Box::new(default_access_violation_handler),
802        )
803    }
804
805    /// Map virtual memory to host memory.
806    pub fn map(
807        &self,
808        access_type: AccessType,
809        vm_addr: u64,
810        len: u64,
811    ) -> StableResult<HostBuffer, EbpfError> {
812        debug_assert!(self.initialized);
813        if self.disable_address_translation {
814            // NOTE TRICKY: this pointer most likely did *not* get its provenance exposed in the
815            // Rust-land! This option in general is extremely unsafe and have us constructing
816            // pointers to no man's land. We acknowledge this and don't consider it to be a bug,
817            // given that the option isn't meant to be used for any serious applications of this
818            // crate.
819            let ptr = ptr::with_exposed_provenance_mut(vm_addr as usize);
820            let buffer = HostBuffer::Mutable(ptr::slice_from_raw_parts_mut(ptr, len as usize));
821            return StableResult::Ok(buffer);
822        }
823        if let Some((_index, region)) = self.find_region(vm_addr) {
824            if region.host_buffer().is_mutable() || access_type != AccessType::Store {
825                if let Some(host_buffer) = region.vm_to_host_buffer(vm_addr, len) {
826                    return StableResult::Ok(host_buffer);
827                }
828            }
829        }
830        StableResult::Err(self.generate_access_violation(access_type, vm_addr, len))
831    }
832
833    /// Map virtual memory to host memory and potentially call the [AccessViolationHandler].
834    ///
835    /// This requires the [MemoryMapping] to be mutable and
836    /// can cause previously translated addresses to become invalid.
837    #[inline(always)]
838    pub fn map_with_access_violation_handler(
839        &mut self,
840        access_type: AccessType,
841        vm_addr: u64,
842        len: u64,
843    ) -> StableResult<HostBuffer, EbpfError> {
844        debug_assert!(self.initialized);
845        if self.disable_address_translation {
846            // NOTE TRICKY: this pointer most likely did *not* get its provenance exposed in the
847            // Rust-land! This option in general is extremely unsafe and have us constructing
848            // pointers to no man's land. We acknowledge this and don't consider it to be a bug,
849            // given that the option isn't meant to be used for any serious applications of this
850            // crate.
851            let ptr = ptr::with_exposed_provenance_mut(vm_addr as usize);
852            let buffer = HostBuffer::Mutable(ptr::slice_from_raw_parts_mut(ptr, len as usize));
853            return StableResult::Ok(buffer);
854        }
855
856        let index = match self.find_region(vm_addr) {
857            Some((index, region)) => {
858                if region.host_buffer().is_mutable() || access_type != AccessType::Store {
859                    if let Some(host_buffer) = region.vm_to_host_buffer(vm_addr, len) {
860                        return StableResult::Ok(host_buffer);
861                    }
862                }
863                index
864            }
865            None => usize::MAX,
866        };
867        // The slow path is out of line, so that the code in there doesn't make the callers of the
868        // fast path save and restore registers they otherwise do not need to. Note, that a single
869        // call site for the cold path matters. Split it up and LLVM merges their results with the
870        // fast path's through the stack.
871        self.map_with_access_violation_handler_slow(index, access_type, vm_addr, len)
872    }
873
874    // `#[cold]`, `#[inline(never)]` have been experimentally determined to improve mapping function
875    // throughput.
876    // In the future using a `rust-cold` calling convention would help this further.
877    #[cold]
878    #[inline(never)]
879    fn map_with_access_violation_handler_slow(
880        &mut self,
881        region_index: usize,
882        access_type: AccessType,
883        vm_addr: u64,
884        len: u64,
885    ) -> StableResult<HostBuffer, EbpfError> {
886        let regions = self.get_regions();
887        let Some(region) = regions.get(region_index) else {
888            debug_assert!(self.find_region(vm_addr).is_none());
889            return StableResult::Err(self.generate_access_violation_for(
890                None,
891                access_type,
892                vm_addr,
893                len,
894            ));
895        };
896        let mut region = region.clone();
897        let max_len = regions
898            .get(region_index.saturating_add(1))
899            .map_or(u64::MAX, |next_region| next_region.vm_addr)
900            .saturating_sub(region.vm_addr);
901        (self.access_violation_handler)(&mut region, max_len, access_type, vm_addr, len);
902        if region.host_buffer().is_mutable() || access_type != AccessType::Store {
903            if let Some(host_buffer) = region.vm_to_host_buffer(vm_addr, len) {
904                if let Err(err) = unsafe { self.replace_region(region_index, region) } {
905                    return StableResult::Err(err);
906                }
907                return StableResult::Ok(host_buffer);
908            }
909        }
910        // The handler did not change the region in the mapping, so the original one still applies.
911        let region = self.get_regions().get(region_index);
912        StableResult::Err(self.generate_access_violation_for(region, access_type, vm_addr, len))
913    }
914
915    /// Loads `size_of::<T>()` bytes at the given guest address.
916    #[inline]
917    pub fn load<T: Pod + Into<u64>>(&mut self, vm_addr: u64) -> ProgramResult {
918        let len = mem::size_of::<T>() as u64;
919        debug_assert!(len <= mem::size_of::<u64>() as u64);
920        debug_assert!(self.initialized);
921        let ptr = match self.map_with_access_violation_handler(AccessType::Load, vm_addr, len) {
922            StableResult::Err(e) => return ProgramResult::Err(e),
923            StableResult::Ok(buf) => buf.ptr(),
924        };
925        ProgramResult::Ok(unsafe {
926            // SAFETY:
927            //
928            // Contract from `ptr::read_unaligned`: `src` must be valid for reads.
929            // Evidence: So long as `disable_address_translation` is not `true`, `map_*` only
930            // returns valid, allocated subslices of memory.
931            // Contract from `ptr::read_unaligned`: `src` must point to a properly initialized value
932            // of type T.
933            // Evidence: `T: Pod`.
934            ptr::read_unaligned::<T>(ptr.cast()).into()
935        })
936    }
937
938    /// Store `value` at the given guest address.
939    #[inline]
940    pub fn store<T: Pod>(&mut self, value: T, vm_addr: u64) -> ProgramResult {
941        let len = mem::size_of::<T>() as u64;
942        debug_assert!(len <= mem::size_of::<u64>() as u64);
943        debug_assert!(self.initialized);
944        let ptr = match self.map_with_access_violation_handler(AccessType::Store, vm_addr, len) {
945            StableResult::Err(e) => return ProgramResult::Err(e),
946            StableResult::Ok(buf) => buf.ptr_mut(),
947        };
948        StableResult::Ok(unsafe {
949            // SAFETY:
950            //
951            // Contract from `ptr::read_unaligned`: `src` must be valid for reads.
952            // Evidence: So long as `disable_address_translation` is not `true`, `map_*` only
953            // returns valid, allocated subslices of memory.
954            // Contract from `ptr::read_unaligned`: `src` must point to a properly initialized value
955            // of type T.
956            // Evidence: `T: Pod`.
957            ptr::write_unaligned::<T>(ptr.cast(), value);
958            0
959        })
960    }
961
962    /// Returns the `MemoryRegion` which may contain the given address.
963    #[inline(always)]
964    pub fn find_region(&self, vm_addr: u64) -> Option<(usize, &MemoryRegion)> {
965        debug_assert!(self.initialized);
966        match &self.ty {
967            MemoryMappingType::Aligned(inner) => inner.find_region(vm_addr),
968            MemoryMappingType::Unaligned(inner) => inner.find_region(vm_addr),
969        }
970    }
971
972    /// Returns the `MemoryRegion`s in this mapping.
973    #[inline(always)]
974    pub fn get_regions(&self) -> &[MemoryRegion] {
975        match &self.ty {
976            MemoryMappingType::Aligned(inner) => &inner.regions,
977            MemoryMappingType::Unaligned(inner) => &inner.regions,
978        }
979    }
980
981    /// Returns the [`MemoryRegion`]s as mutable.
982    ///
983    /// Modifying the regions might break the initialization constraints, so this function
984    /// uninitializes the mapping. The memory mappings must be initialized
985    /// again with [`Self::initialize`] before further use.
986    pub fn get_regions_mut(&mut self) -> &mut [MemoryRegion] {
987        self.initialized = false;
988
989        let regions = match &mut self.ty {
990            MemoryMappingType::Aligned(inner) => inner.regions.as_mut_slice(),
991            MemoryMappingType::Unaligned(inner) => &mut inner.regions,
992        };
993
994        regions
995    }
996
997    /// Replaces the `MemoryRegion` at the given index
998    ///
999    /// # Safety
1000    ///
1001    /// Refer to [`MemoryMapping::new_uninitialized`].
1002    #[inline(always)]
1003    pub unsafe fn replace_region(
1004        &mut self,
1005        index: usize,
1006        region: MemoryRegion,
1007    ) -> Result<(), EbpfError> {
1008        debug_assert!(self.initialized);
1009        let regions = self.get_regions();
1010        let next_region_start = regions
1011            .get(index.saturating_add(1))
1012            .map_or(u64::MAX, |next_region| next_region.vm_addr);
1013        if index >= regions.len()
1014            || regions[index].vm_addr != region.vm_addr
1015            || region.vm_addr_range().end > next_region_start
1016        {
1017            return Err(EbpfError::InvalidMemoryRegion(index));
1018        }
1019        match &mut self.ty {
1020            MemoryMappingType::Aligned(inner) => inner.replace_region(index, region),
1021            MemoryMappingType::Unaligned(inner) => inner.replace_region(index, region),
1022        }
1023    }
1024
1025    /// Initialize the MemoryMapping
1026    pub fn initialize(&mut self) -> Result<(), EbpfError> {
1027        let result = match &mut self.ty {
1028            MemoryMappingType::Aligned(inner) => inner.initialize(),
1029            MemoryMappingType::Unaligned(inner) => inner.initialize(),
1030        };
1031        self.initialized = result.is_ok();
1032        result
1033    }
1034
1035    fn generate_access_violation(
1036        &self,
1037        access_type: AccessType,
1038        vm_addr: u64,
1039        len: u64,
1040    ) -> EbpfError {
1041        self.generate_access_violation_for(
1042            self.find_region(vm_addr).map(|(_, region)| region),
1043            access_type,
1044            vm_addr,
1045            len,
1046        )
1047    }
1048
1049    /// Like [`Self::generate_access_violation`], with `region` being what
1050    /// `find_region(vm_addr)` returns.
1051    fn generate_access_violation_for(
1052        &self,
1053        region: Option<&MemoryRegion>,
1054        access_type: AccessType,
1055        vm_addr: u64,
1056        len: u64,
1057    ) -> EbpfError {
1058        let stack_frame = (vm_addr as i64)
1059            .saturating_sub(ebpf::MM_STACK_START as i64)
1060            .checked_div(self.stack_frame_size)
1061            .unwrap_or(0);
1062        if !self.sbpf_version.manual_stack_frame_bump()
1063            && (-1..self.max_call_depth.saturating_add(1)).contains(&stack_frame)
1064        {
1065            EbpfError::StackAccessViolation(access_type, vm_addr, len, stack_frame)
1066        } else {
1067            let region_name = match vm_addr & (!ebpf::MM_BYTECODE_START.saturating_sub(1)) {
1068                _ if region.map(|r| r.vm_addr_range().contains(&vm_addr)) != Some(true) => {
1069                    "unallocated"
1070                }
1071                ebpf::MM_BYTECODE_START => "program",
1072                ebpf::MM_STACK_START => "stack",
1073                ebpf::MM_HEAP_START => "heap",
1074                ebpf::MM_INPUT_START => "input",
1075                _ => "allocated",
1076            };
1077            EbpfError::AccessViolation(access_type, vm_addr, len, region_name)
1078        }
1079    }
1080}
1081
1082/// Fast, small linear cache used to speed up unaligned memory mapping.
1083#[derive(Debug)]
1084struct MappingCache {
1085    // The cached entries.
1086    entries: [(Range<u64>, usize); MappingCache::SIZE],
1087    // Index of the last accessed memory region.
1088    //
1089    // New entries are written backwards, so that find() can always scan
1090    // forward which is faster.
1091    head: usize,
1092}
1093
1094impl MappingCache {
1095    // must be a power of two
1096    const SIZE: usize = 4;
1097
1098    fn new() -> MappingCache {
1099        MappingCache {
1100            entries: array::from_fn(|_| (0..0, 0)),
1101            head: 0,
1102        }
1103    }
1104
1105    #[inline]
1106    fn find(&self, vm_addr: u64) -> Option<usize> {
1107        for i in 0..Self::SIZE {
1108            let index = self.head.wrapping_add(i) % Self::SIZE;
1109            // Safety:
1110            // index is guaranteed to be between 0..Self::SIZE
1111            let (vm_range, region_index) = unsafe { self.entries.get_unchecked(index) };
1112            if vm_range.contains(&vm_addr) {
1113                return Some(*region_index);
1114            }
1115        }
1116
1117        None
1118    }
1119
1120    #[inline]
1121    fn insert(&mut self, vm_range: Range<u64>, region_index: usize) {
1122        self.head = self.head.wrapping_sub(1) % Self::SIZE;
1123        // Safety:
1124        // self.head is guaranteed to be between 0..Self::SIZE
1125        unsafe { *self.entries.get_unchecked_mut(self.head) = (vm_range, region_index) };
1126    }
1127
1128    #[inline]
1129    fn flush(&mut self) {
1130        self.entries = array::from_fn(|_| (0..0, 0));
1131        self.head = 0;
1132    }
1133}
1134
1135#[cfg(test)]
1136mod test {
1137    use std::{cell::RefCell, rc::Rc};
1138    use test_utils::assert_error;
1139
1140    use super::*;
1141
1142    #[test]
1143    fn test_mapping_cache() {
1144        let mut cache = MappingCache::new();
1145        assert_eq!(cache.find(0), None);
1146
1147        let mut ranges = vec![10u64..20, 20..30, 30..40, 40..50];
1148        for (region, range) in ranges.iter().cloned().enumerate() {
1149            cache.insert(range, region);
1150        }
1151        for (region, range) in ranges.iter().enumerate() {
1152            if region > 0 {
1153                assert_eq!(cache.find(range.start - 1), Some(region - 1));
1154            } else {
1155                assert_eq!(cache.find(range.start - 1), None);
1156            }
1157            assert_eq!(cache.find(range.start), Some(region));
1158            assert_eq!(cache.find(range.start + 1), Some(region));
1159            assert_eq!(cache.find(range.end - 1), Some(region));
1160            if region < 3 {
1161                assert_eq!(cache.find(range.end), Some(region + 1));
1162            } else {
1163                assert_eq!(cache.find(range.end), None);
1164            }
1165        }
1166
1167        cache.insert(50..60, 4);
1168        ranges.push(50..60);
1169        for (region, range) in ranges.iter().enumerate() {
1170            if region == 0 {
1171                assert_eq!(cache.find(range.start), None);
1172                continue;
1173            }
1174            if region > 1 {
1175                assert_eq!(cache.find(range.start - 1), Some(region - 1));
1176            } else {
1177                assert_eq!(cache.find(range.start - 1), None);
1178            }
1179            assert_eq!(cache.find(range.start), Some(region));
1180            assert_eq!(cache.find(range.start + 1), Some(region));
1181            assert_eq!(cache.find(range.end - 1), Some(region));
1182            if region < 4 {
1183                assert_eq!(cache.find(range.end), Some(region + 1));
1184            } else {
1185                assert_eq!(cache.find(range.end), None);
1186            }
1187        }
1188    }
1189
1190    #[test]
1191    fn test_mapping_cache_flush() {
1192        let mut cache = MappingCache::new();
1193        assert_eq!(cache.find(0), None);
1194        cache.insert(0..10, 0);
1195        assert_eq!(cache.find(0), Some(0));
1196        cache.flush();
1197        assert_eq!(cache.find(0), None);
1198    }
1199
1200    #[test]
1201    fn test_map_empty() {
1202        for aligned_memory_mapping in [false, true] {
1203            let config = Config {
1204                aligned_memory_mapping,
1205                ..Config::default()
1206            };
1207            let m = unsafe { MemoryMapping::new(vec![], &config, SBPFVersion::V3) }.unwrap();
1208            assert_error!(
1209                m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 8),
1210                "AccessViolation"
1211            );
1212        }
1213    }
1214
1215    #[test]
1216    fn test_gapped_map() {
1217        for aligned_memory_mapping in [false, true] {
1218            let config = Config {
1219                aligned_memory_mapping,
1220                ..Config::default()
1221            };
1222            let mut mem1 = [0xff; 8];
1223            let mem2 = [0; 8];
1224            let mut m = unsafe {
1225                MemoryMapping::new(
1226                    vec![
1227                        MemoryRegion::new(&raw const mem2[..], ebpf::MM_REGION_SIZE),
1228                        MemoryRegion::new_gapped(&raw mut mem1[..], ebpf::MM_REGION_SIZE * 2, 2),
1229                    ],
1230                    &config,
1231                    SBPFVersion::V3,
1232                )
1233                .unwrap()
1234            };
1235            for frame in 0..4 {
1236                let address = ebpf::MM_STACK_START + frame * 4;
1237                assert!(m.find_region(address).is_some());
1238                assert!(m.map(AccessType::Load, address, 2).is_ok());
1239                assert_error!(m.map(AccessType::Load, address + 2, 2), "AccessViolation");
1240                assert_eq!(m.load::<u16>(address).unwrap(), 0xFFFF);
1241                assert_error!(m.load::<u16>(address + 2), "AccessViolation");
1242                assert!(m.store::<u16>(0xFFFF, address).is_ok());
1243                assert_error!(m.store::<u16>(0xFFFF, address + 2), "AccessViolation");
1244            }
1245        }
1246    }
1247
1248    #[test]
1249    fn test_unaligned_map_overlap() {
1250        let config = Config {
1251            aligned_memory_mapping: false,
1252            ..Config::default()
1253        };
1254        let mem1 = [1, 2, 3, 4];
1255        let mem2 = [5, 6];
1256        assert_error!(
1257            unsafe {
1258                MemoryMapping::new(
1259                    vec![
1260                        MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1261                        MemoryRegion::new(
1262                            &raw const mem2,
1263                            ebpf::MM_REGION_SIZE + mem1.len() as u64 - 1,
1264                        ),
1265                    ],
1266                    &config,
1267                    SBPFVersion::V3,
1268                )
1269            },
1270            "InvalidMemoryRegion(1)"
1271        );
1272        assert!(unsafe {
1273            MemoryMapping::new(
1274                vec![
1275                    MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1276                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1277                ],
1278                &config,
1279                SBPFVersion::V3,
1280            )
1281        }
1282        .is_ok());
1283    }
1284
1285    #[test]
1286    fn test_unaligned_map() {
1287        let config = Config {
1288            aligned_memory_mapping: false,
1289            ..Config::default()
1290        };
1291        let mut mem1 = [11];
1292        let mem2 = [22, 22];
1293        let mem3 = [33];
1294        let mem4 = [44, 44];
1295        let m = unsafe {
1296            MemoryMapping::new(
1297                vec![
1298                    MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1299                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1300                    MemoryRegion::new(
1301                        &raw const mem3,
1302                        ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len()) as u64,
1303                    ),
1304                    MemoryRegion::new(
1305                        &raw const mem4,
1306                        ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len() + mem3.len()) as u64,
1307                    ),
1308                ],
1309                &config,
1310                SBPFVersion::V3,
1311            )
1312            .unwrap()
1313        };
1314
1315        assert_eq!(
1316            m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1317                .unwrap()
1318                .ptr()
1319                .addr(),
1320            mem1.as_ptr().addr()
1321        );
1322
1323        assert_eq!(
1324            m.map(AccessType::Store, ebpf::MM_REGION_SIZE, 1)
1325                .unwrap()
1326                .ptr()
1327                .addr(),
1328            mem1.as_ptr().addr()
1329        );
1330
1331        assert_error!(
1332            m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 2),
1333            "AccessViolation"
1334        );
1335
1336        assert_eq!(
1337            m.map(
1338                AccessType::Load,
1339                ebpf::MM_REGION_SIZE + mem1.len() as u64,
1340                1,
1341            )
1342            .unwrap()
1343            .ptr()
1344            .addr(),
1345            mem2.as_ptr().addr()
1346        );
1347
1348        assert_eq!(
1349            m.map(
1350                AccessType::Load,
1351                ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len()) as u64,
1352                1,
1353            )
1354            .unwrap()
1355            .ptr()
1356            .addr(),
1357            mem3.as_ptr().addr()
1358        );
1359
1360        assert_eq!(
1361            m.map(
1362                AccessType::Load,
1363                ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len() + mem3.len()) as u64,
1364                1,
1365            )
1366            .unwrap()
1367            .ptr()
1368            .addr(),
1369            mem4.as_ptr().addr()
1370        );
1371
1372        assert_error!(
1373            m.map(
1374                AccessType::Load,
1375                ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len() + mem3.len() + mem4.len()) as u64,
1376                1,
1377            ),
1378            "AccessViolation"
1379        );
1380    }
1381
1382    #[test]
1383    fn test_unaligned_region() {
1384        let config = Config {
1385            aligned_memory_mapping: false,
1386            ..Config::default()
1387        };
1388
1389        let mut mem1 = [0xFF; 4];
1390        let mem2 = [0xDD; 4];
1391        let m = unsafe {
1392            MemoryMapping::new(
1393                vec![
1394                    MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1395                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + 4),
1396                ],
1397                &config,
1398                SBPFVersion::V3,
1399            )
1400            .unwrap()
1401        };
1402        assert!(m.find_region(ebpf::MM_REGION_SIZE - 1).is_none());
1403        assert_eq!(
1404            HostBuffer::Mutable(&raw mut mem1[..]),
1405            m.find_region(ebpf::MM_REGION_SIZE).unwrap().1.host,
1406        );
1407        assert_eq!(
1408            HostBuffer::Mutable(&raw mut mem1[..]),
1409            m.find_region(ebpf::MM_REGION_SIZE + 3).unwrap().1.host,
1410        );
1411        assert_eq!(
1412            HostBuffer::Immutable(&raw const mem2[..]),
1413            m.find_region(ebpf::MM_REGION_SIZE + 4).unwrap().1.host,
1414        );
1415        assert_eq!(
1416            HostBuffer::Immutable(&raw const mem2[..]),
1417            m.find_region(ebpf::MM_REGION_SIZE + 7).unwrap().1.host,
1418        );
1419        assert!(m.find_region(ebpf::MM_REGION_SIZE + 8).is_some());
1420    }
1421
1422    #[test]
1423    fn test_aligned_region() {
1424        let config = Config {
1425            aligned_memory_mapping: true,
1426            ..Config::default()
1427        };
1428
1429        let mut mem1 = [0xFF; 4];
1430        let mem2 = [0xDD; 4];
1431        let m = unsafe {
1432            MemoryMapping::new(
1433                vec![
1434                    MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1435                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE * 2),
1436                ],
1437                &config,
1438                SBPFVersion::V4,
1439            )
1440            .unwrap()
1441        };
1442        assert_eq!(m.find_region(ebpf::MM_REGION_SIZE - 1).unwrap().1.len(), 0);
1443        assert_eq!(
1444            HostBuffer::Mutable(&raw mut mem1[..]),
1445            m.find_region(ebpf::MM_REGION_SIZE).unwrap().1.host,
1446        );
1447        assert_eq!(
1448            HostBuffer::Mutable(&raw mut mem1[..]),
1449            m.find_region(ebpf::MM_REGION_SIZE + 3).unwrap().1.host,
1450        );
1451        assert!(m.find_region(ebpf::MM_REGION_SIZE + 4).is_some());
1452        assert_eq!(
1453            HostBuffer::Immutable(&raw const mem2[..]),
1454            m.find_region(ebpf::MM_REGION_SIZE * 2).unwrap().1.host,
1455        );
1456        assert_eq!(
1457            HostBuffer::Immutable(&raw const mem2[..]),
1458            m.find_region(ebpf::MM_REGION_SIZE * 2 + 3).unwrap().1.host,
1459        );
1460        assert!(m.find_region(ebpf::MM_REGION_SIZE * 3 + 4).is_none());
1461    }
1462
1463    #[test]
1464    fn test_unaligned_map_load() {
1465        let config = Config {
1466            aligned_memory_mapping: false,
1467            ..Config::default()
1468        };
1469        let mem1 = [0x11, 0x22];
1470        let mem2 = [0x33];
1471        let mut m = unsafe {
1472            MemoryMapping::new(
1473                vec![
1474                    MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1475                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1476                ],
1477                &config,
1478                SBPFVersion::V3,
1479            )
1480            .unwrap()
1481        };
1482
1483        assert_eq!(m.load::<u16>(ebpf::MM_REGION_SIZE).unwrap(), 0x2211);
1484        assert_error!(m.load::<u32>(ebpf::MM_REGION_SIZE), "AccessViolation");
1485        assert_error!(m.load::<u32>(ebpf::MM_REGION_SIZE + 4), "AccessViolation");
1486    }
1487
1488    #[test]
1489    fn test_unaligned_map_store() {
1490        let config = Config {
1491            aligned_memory_mapping: false,
1492            ..Config::default()
1493        };
1494        let mut mem1 = [0xff, 0xff];
1495        let mut mem2 = [0xff];
1496        let mut m = unsafe {
1497            MemoryMapping::new(
1498                vec![
1499                    MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1500                    MemoryRegion::new(&raw mut mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1501                ],
1502                &config,
1503                SBPFVersion::V3,
1504            )
1505            .unwrap()
1506        };
1507
1508        m.store(0x1122u16, ebpf::MM_REGION_SIZE).unwrap();
1509        assert_eq!(m.load::<u16>(ebpf::MM_REGION_SIZE).unwrap(), 0x1122);
1510
1511        assert_error!(
1512            m.store(0x33445566u32, ebpf::MM_REGION_SIZE),
1513            "AccessViolation"
1514        );
1515    }
1516
1517    #[test]
1518    fn test_unaligned_map_store_out_of_bounds() {
1519        let config = Config {
1520            aligned_memory_mapping: false,
1521            ..Config::default()
1522        };
1523
1524        let mut mem1 = [0xFF];
1525        let mut m = unsafe {
1526            MemoryMapping::new(
1527                vec![MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE)],
1528                &config,
1529                SBPFVersion::V3,
1530            )
1531            .unwrap()
1532        };
1533        m.store(0x11u8, ebpf::MM_REGION_SIZE).unwrap();
1534        assert_error!(m.store(0x11u8, ebpf::MM_REGION_SIZE - 1), "AccessViolation");
1535        assert_error!(m.store(0x11u8, ebpf::MM_REGION_SIZE + 1), "AccessViolation");
1536        // this gets us line coverage for the case where we're completely
1537        // outside the address space (the case above is just on the edge)
1538        assert_error!(m.store(0x11u8, ebpf::MM_REGION_SIZE + 2), "AccessViolation");
1539
1540        let mut mem1 = [0xFF; 4];
1541        let mut mem2 = [0xDD; 4];
1542        let mut m = unsafe {
1543            MemoryMapping::new(
1544                vec![
1545                    MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1546                    MemoryRegion::new(&raw mut mem2, ebpf::MM_REGION_SIZE + 4),
1547                ],
1548                &config,
1549                SBPFVersion::V3,
1550            )
1551            .unwrap()
1552        };
1553        assert_error!(
1554            m.store(0x1122334455667788u64, ebpf::MM_REGION_SIZE),
1555            "AccessViolation"
1556        );
1557    }
1558
1559    #[test]
1560    fn test_unaligned_map_load_out_of_bounds() {
1561        let config = Config {
1562            aligned_memory_mapping: false,
1563            ..Config::default()
1564        };
1565
1566        let mem1 = [0xff];
1567        let mut m = unsafe {
1568            MemoryMapping::new(
1569                vec![MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE)],
1570                &config,
1571                SBPFVersion::V3,
1572            )
1573            .unwrap()
1574        };
1575        assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 0xff);
1576        assert_error!(m.load::<u8>(ebpf::MM_REGION_SIZE - 1), "AccessViolation");
1577        assert_error!(m.load::<u8>(ebpf::MM_REGION_SIZE + 1), "AccessViolation");
1578        assert_error!(m.load::<u8>(ebpf::MM_REGION_SIZE + 2), "AccessViolation");
1579
1580        let mem1 = [0xFF; 4];
1581        let mem2 = [0xDD; 4];
1582        let mut m = unsafe {
1583            MemoryMapping::new(
1584                vec![
1585                    MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1586                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + 4),
1587                ],
1588                &config,
1589                SBPFVersion::V3,
1590            )
1591            .unwrap()
1592        };
1593        assert_error!(m.load::<u64>(ebpf::MM_REGION_SIZE), "AccessViolation");
1594    }
1595
1596    #[test]
1597    #[should_panic(expected = "AccessViolation")]
1598    fn test_store_readonly() {
1599        let config = Config {
1600            aligned_memory_mapping: false,
1601            ..Config::default()
1602        };
1603        let mut mem1 = [0xff, 0xff];
1604        let mem2 = [0xff, 0xff];
1605        let mut m = unsafe {
1606            MemoryMapping::new(
1607                vec![
1608                    MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1609                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1610                ],
1611                &config,
1612                SBPFVersion::V3,
1613            )
1614            .unwrap()
1615        };
1616        m.store(0x11223344, ebpf::MM_REGION_SIZE).unwrap();
1617    }
1618
1619    #[test]
1620    fn test_unaligned_map_replace_region() {
1621        let config = Config {
1622            aligned_memory_mapping: false,
1623            ..Config::default()
1624        };
1625        let mem1 = [11];
1626        let mem2 = [22, 22];
1627        let mem3 = [33];
1628        let mut m = unsafe {
1629            MemoryMapping::new(
1630                vec![
1631                    MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1632                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1633                ],
1634                &config,
1635                SBPFVersion::V3,
1636            )
1637            .unwrap()
1638        };
1639
1640        assert_eq!(
1641            m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1642                .unwrap()
1643                .ptr()
1644                .addr(),
1645            mem1.as_ptr().addr()
1646        );
1647
1648        assert_eq!(
1649            m.map(
1650                AccessType::Load,
1651                ebpf::MM_REGION_SIZE + mem1.len() as u64,
1652                1,
1653            )
1654            .unwrap()
1655            .ptr()
1656            .addr(),
1657            mem2.as_ptr().addr()
1658        );
1659
1660        assert_error!(
1661            unsafe {
1662                m.replace_region(
1663                    2,
1664                    MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1665                )
1666            },
1667            "InvalidMemoryRegion(2)"
1668        );
1669
1670        let region_index = m
1671            .get_regions()
1672            .iter()
1673            .position(|mem| mem.vm_addr == ebpf::MM_REGION_SIZE + mem1.len() as u64)
1674            .unwrap();
1675
1676        // old.vm_addr != new.vm_addr
1677        assert_error!(
1678            unsafe {
1679                m.replace_region(
1680                    region_index,
1681                    MemoryRegion::new(
1682                        &raw const mem3,
1683                        ebpf::MM_REGION_SIZE + mem1.len() as u64 + 1,
1684                    ),
1685                )
1686            },
1687            "InvalidMemoryRegion({})",
1688            region_index
1689        );
1690
1691        unsafe {
1692            m.replace_region(
1693                region_index,
1694                MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1695            )
1696            .unwrap()
1697        };
1698
1699        assert_eq!(
1700            m.map(
1701                AccessType::Load,
1702                ebpf::MM_REGION_SIZE + mem1.len() as u64,
1703                1,
1704            )
1705            .unwrap()
1706            .ptr()
1707            .addr(),
1708            mem3.as_ptr().addr()
1709        );
1710    }
1711
1712    #[test]
1713    fn test_aligned_map_replace_region() {
1714        let config = Config {
1715            aligned_memory_mapping: true,
1716            ..Config::default()
1717        };
1718        let mem1 = [11];
1719        let mem2 = [22, 22];
1720        let mem3 = [33, 33];
1721        let mut m = unsafe {
1722            MemoryMapping::new(
1723                vec![
1724                    MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1725                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE * 2),
1726                ],
1727                &config,
1728                SBPFVersion::V4,
1729            )
1730            .unwrap()
1731        };
1732
1733        assert_eq!(
1734            m.map(AccessType::Load, ebpf::MM_REGION_SIZE * 2, 1)
1735                .unwrap()
1736                .ptr()
1737                .addr(),
1738            mem2.as_ptr().addr()
1739        );
1740
1741        // index > regions.len()
1742        assert_error!(
1743            unsafe {
1744                m.replace_region(
1745                    3,
1746                    MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 2),
1747                )
1748            },
1749            "InvalidMemoryRegion(3)"
1750        );
1751
1752        // index != addr >> VIRTUAL_ADDRESS_BITS
1753        assert_error!(
1754            unsafe {
1755                m.replace_region(
1756                    2,
1757                    MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 3),
1758                )
1759            },
1760            "InvalidMemoryRegion(2)"
1761        );
1762
1763        // index + len != addr >> VIRTUAL_ADDRESS_BITS
1764        assert_error!(
1765            unsafe {
1766                m.replace_region(
1767                    2,
1768                    MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 3 - 1),
1769                )
1770            },
1771            "InvalidMemoryRegion(2)"
1772        );
1773
1774        unsafe {
1775            m.replace_region(
1776                2,
1777                MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 2),
1778            )
1779            .unwrap()
1780        };
1781
1782        assert_eq!(
1783            m.map(AccessType::Load, ebpf::MM_REGION_SIZE * 2, 1)
1784                .unwrap()
1785                .ptr()
1786                .addr(),
1787            mem3.as_ptr().addr()
1788        );
1789    }
1790
1791    #[test]
1792    fn test_access_violation_handler_map() {
1793        for aligned_memory_mapping in [true, false] {
1794            let config = Config {
1795                aligned_memory_mapping,
1796                ..Config::default()
1797            };
1798            let original = [11, 22];
1799            let copied = Rc::new(RefCell::new(Vec::new()));
1800            let mut regions = vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)];
1801            regions[0].access_violation_handler_payload = Some(0);
1802
1803            let c = Rc::clone(&copied);
1804            let mut m = unsafe {
1805                MemoryMapping::new_with_access_violation_handler(
1806                    regions,
1807                    &config,
1808                    SBPFVersion::V3,
1809                    Box::new(move |region, _, _, _, _| {
1810                        let mut vec = c.borrow_mut();
1811                        vec.extend_from_slice(&original);
1812                        region.redirect(&raw mut vec[..]);
1813                    }),
1814                )
1815                .unwrap()
1816            };
1817
1818            assert_eq!(
1819                m.map_with_access_violation_handler(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1820                    .unwrap()
1821                    .ptr()
1822                    .addr(),
1823                original.as_ptr().addr()
1824            );
1825            assert_eq!(
1826                m.map_with_access_violation_handler(AccessType::Store, ebpf::MM_REGION_SIZE, 1)
1827                    .unwrap()
1828                    .ptr()
1829                    .addr(),
1830                copied.borrow().as_ptr().addr()
1831            );
1832        }
1833    }
1834
1835    #[test]
1836    fn test_access_violation_handler_load_store() {
1837        for aligned_memory_mapping in [true, false] {
1838            let config = Config {
1839                aligned_memory_mapping,
1840                ..Config::default()
1841            };
1842            let original = [11, 22];
1843            let copied = Rc::new(RefCell::new(Vec::new()));
1844            let mut regions = vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)];
1845            regions[0].access_violation_handler_payload = Some(0);
1846
1847            let c = Rc::clone(&copied);
1848            let mut m = unsafe {
1849                MemoryMapping::new_with_access_violation_handler(
1850                    regions,
1851                    &config,
1852                    SBPFVersion::V3,
1853                    Box::new(move |region, _, _, _, _| {
1854                        let mut vec = c.borrow_mut();
1855                        vec.extend_from_slice(&original);
1856                        region.redirect(&raw mut vec[..]);
1857                    }),
1858                )
1859                .unwrap()
1860            };
1861
1862            assert_eq!(
1863                m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1864                    .unwrap()
1865                    .ptr()
1866                    .addr(),
1867                original.as_ptr().addr()
1868            );
1869
1870            assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 11);
1871            assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE + 1).unwrap(), 22);
1872            assert!(copied.borrow().is_empty());
1873
1874            m.store(33u8, ebpf::MM_REGION_SIZE).unwrap();
1875            assert_eq!(original[0], 11);
1876            assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 33);
1877            assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE + 1).unwrap(), 22);
1878        }
1879    }
1880
1881    #[test]
1882    fn test_access_violation_handler_region_id() {
1883        for aligned_memory_mapping in [true, false] {
1884            let config = Config {
1885                aligned_memory_mapping,
1886                ..Config::default()
1887            };
1888            let original1 = [11, 22];
1889            let original2 = [33, 44];
1890            let copied = Rc::new(RefCell::new(Vec::new()));
1891
1892            let mut regions = vec![
1893                MemoryRegion::new(&raw const original1, ebpf::MM_REGION_SIZE),
1894                MemoryRegion::new(&raw const original2, ebpf::MM_REGION_SIZE * 2),
1895            ];
1896            regions[0].access_violation_handler_payload = Some(42);
1897
1898            let c = Rc::clone(&copied);
1899            let mut m = unsafe {
1900                MemoryMapping::new_with_access_violation_handler(
1901                    regions,
1902                    &config,
1903                    SBPFVersion::V3,
1904                    Box::new(move |region, _, _, _, _| {
1905                        // check that the argument passed to MemoryRegion::new is then passed to the
1906                        // callback
1907                        assert_eq!(region.access_violation_handler_payload, Some(42));
1908                        let mut vec = c.borrow_mut();
1909                        vec.extend_from_slice(&original1);
1910                        region.redirect(&raw mut vec[..]);
1911                    }),
1912                )
1913                .unwrap()
1914            };
1915
1916            m.store(55u8, ebpf::MM_REGION_SIZE).unwrap();
1917            assert_eq!(original1[0], 11);
1918            assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 55);
1919        }
1920    }
1921
1922    #[test]
1923    #[should_panic(expected = "AccessViolation")]
1924    fn test_map_access_violation_handler_error() {
1925        let config = Config::default();
1926        let original = [11, 22];
1927
1928        let m = unsafe {
1929            MemoryMapping::new_with_access_violation_handler(
1930                vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)],
1931                &config,
1932                SBPFVersion::V4,
1933                Box::new(default_access_violation_handler),
1934            )
1935            .unwrap()
1936        };
1937
1938        m.map(AccessType::Store, ebpf::MM_REGION_SIZE, 1).unwrap();
1939    }
1940
1941    #[test]
1942    #[should_panic(expected = "AccessViolation")]
1943    fn test_store_access_violation_handler_error() {
1944        let config = Config::default();
1945        let original = [11, 22];
1946
1947        let mut m = unsafe {
1948            MemoryMapping::new_with_access_violation_handler(
1949                vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)],
1950                &config,
1951                SBPFVersion::V4,
1952                Box::new(default_access_violation_handler),
1953            )
1954            .unwrap()
1955        };
1956
1957        m.store(33u8, ebpf::MM_REGION_SIZE).unwrap();
1958    }
1959
1960    #[test]
1961    fn test_access_violation_region_identification() {
1962        let config = Config::default();
1963        let original = [11, 22];
1964        let region = 0x10_0000_0000;
1965        let mut m = unsafe {
1966            MemoryMapping::new(
1967                vec![MemoryRegion::new(&raw const original, region)],
1968                &config,
1969                SBPFVersion::V4,
1970            )
1971            .unwrap()
1972        };
1973        let store_err_inbound = m.store(33u8, region).unwrap_err();
1974        assert_eq!(
1975            store_err_inbound.to_string(),
1976            "Access violation writing 1 bytes at address 0x1000000000 (in allocated region)"
1977        );
1978        let store_err_oob = m.load::<u64>(region + 3).unwrap_err();
1979        assert_eq!(
1980            store_err_oob.to_string(),
1981            "Access violation reading 8 bytes at address 0x1000000003 (in unallocated region)"
1982        );
1983    }
1984
1985    #[test]
1986    fn v4_aligned_mapping() {
1987        let config = Config {
1988            aligned_memory_mapping: false,
1989            ..Config::default()
1990        };
1991
1992        let mem = [11, 12];
1993        let mapping = unsafe {
1994            MemoryMapping::new_with_access_violation_handler(
1995                vec![MemoryRegion::new(&raw const mem, ebpf::MM_REGION_SIZE)],
1996                &config,
1997                SBPFVersion::V4,
1998                Box::new(default_access_violation_handler),
1999            )
2000            .unwrap()
2001        };
2002
2003        assert!(matches!(mapping.ty, MemoryMappingType::Aligned(_)));
2004    }
2005}