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