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        if let Some((index, region)) = self.find_region(vm_addr) {
857            if region.host_buffer().is_mutable() || access_type != AccessType::Store {
858                if let Some(host_buffer) = region.vm_to_host_buffer(vm_addr, len) {
859                    return StableResult::Ok(host_buffer);
860                }
861            }
862            let mut region = (*region).clone();
863            let max_len = self
864                .get_regions()
865                .get(index.saturating_add(1))
866                .map_or(u64::MAX, |next_region| next_region.vm_addr)
867                .saturating_sub(region.vm_addr);
868            (self.access_violation_handler)(&mut region, max_len, access_type, vm_addr, len);
869            if region.host_buffer().is_mutable() || access_type != AccessType::Store {
870                if let Some(host_buffer) = region.vm_to_host_buffer(vm_addr, len) {
871                    if let Err(err) = unsafe { self.replace_region(index, region) } {
872                        return StableResult::Err(err);
873                    }
874                    return StableResult::Ok(host_buffer);
875                }
876            }
877        }
878        StableResult::Err(self.generate_access_violation(access_type, vm_addr, len))
879    }
880
881    /// Loads `size_of::<T>()` bytes at the given guest address.
882    pub fn load<T: Pod + Into<u64>>(&mut self, vm_addr: u64) -> ProgramResult {
883        let len = mem::size_of::<T>() as u64;
884        debug_assert!(len <= mem::size_of::<u64>() as u64);
885        debug_assert!(self.initialized);
886        let ptr = match self.map_with_access_violation_handler(AccessType::Load, vm_addr, len) {
887            StableResult::Err(e) => return ProgramResult::Err(e),
888            StableResult::Ok(buf) => buf.ptr(),
889        };
890        ProgramResult::Ok(unsafe {
891            // SAFETY:
892            //
893            // Contract from `ptr::read_unaligned`: `src` must be valid for reads.
894            // Evidence: So long as `disable_address_translation` is not `true`, `map_*` only
895            // returns valid, allocated subslices of memory.
896            // Contract from `ptr::read_unaligned`: `src` must point to a properly initialized value
897            // of type T.
898            // Evidence: `T: Pod`.
899            ptr::read_unaligned::<T>(ptr.cast()).into()
900        })
901    }
902
903    /// Store `value` at the given guest address.
904    pub fn store<T: Pod>(&mut self, value: T, vm_addr: u64) -> ProgramResult {
905        let len = mem::size_of::<T>() as u64;
906        debug_assert!(len <= mem::size_of::<u64>() as u64);
907        debug_assert!(self.initialized);
908        let ptr = match self.map_with_access_violation_handler(AccessType::Store, vm_addr, len) {
909            StableResult::Err(e) => return ProgramResult::Err(e),
910            StableResult::Ok(buf) => buf.ptr_mut(),
911        };
912        StableResult::Ok(unsafe {
913            // SAFETY:
914            //
915            // Contract from `ptr::read_unaligned`: `src` must be valid for reads.
916            // Evidence: So long as `disable_address_translation` is not `true`, `map_*` only
917            // returns valid, allocated subslices of memory.
918            // Contract from `ptr::read_unaligned`: `src` must point to a properly initialized value
919            // of type T.
920            // Evidence: `T: Pod`.
921            ptr::write_unaligned::<T>(ptr.cast(), value);
922            0
923        })
924    }
925
926    /// Returns the `MemoryRegion` which may contain the given address.
927    #[inline(always)]
928    pub fn find_region(&self, vm_addr: u64) -> Option<(usize, &MemoryRegion)> {
929        debug_assert!(self.initialized);
930        match &self.ty {
931            MemoryMappingType::Aligned(inner) => inner.find_region(vm_addr),
932            MemoryMappingType::Unaligned(inner) => inner.find_region(vm_addr),
933        }
934    }
935
936    /// Returns the `MemoryRegion`s in this mapping.
937    #[inline(always)]
938    pub fn get_regions(&self) -> &[MemoryRegion] {
939        match &self.ty {
940            MemoryMappingType::Aligned(inner) => &inner.regions,
941            MemoryMappingType::Unaligned(inner) => &inner.regions,
942        }
943    }
944
945    /// Returns the [`MemoryRegion`]s as mutable.
946    ///
947    /// Modifying the regions might break the initialization constraints, so this function
948    /// uninitializes the mapping. The memory mappings must be initialized
949    /// again with [`Self::initialize`] before further use.
950    pub fn get_regions_mut(&mut self) -> &mut [MemoryRegion] {
951        self.initialized = false;
952
953        let regions = match &mut self.ty {
954            MemoryMappingType::Aligned(inner) => inner.regions.as_mut_slice(),
955            MemoryMappingType::Unaligned(inner) => &mut inner.regions,
956        };
957
958        regions
959    }
960
961    /// Replaces the `MemoryRegion` at the given index
962    ///
963    /// # Safety
964    ///
965    /// Refer to [`MemoryMapping::new_uninitialized`].
966    #[inline(always)]
967    pub unsafe fn replace_region(
968        &mut self,
969        index: usize,
970        region: MemoryRegion,
971    ) -> Result<(), EbpfError> {
972        debug_assert!(self.initialized);
973        let regions = self.get_regions();
974        let next_region_start = regions
975            .get(index.saturating_add(1))
976            .map_or(u64::MAX, |next_region| next_region.vm_addr);
977        if index >= regions.len()
978            || regions[index].vm_addr != region.vm_addr
979            || region.vm_addr_range().end > next_region_start
980        {
981            return Err(EbpfError::InvalidMemoryRegion(index));
982        }
983        match &mut self.ty {
984            MemoryMappingType::Aligned(inner) => inner.replace_region(index, region),
985            MemoryMappingType::Unaligned(inner) => inner.replace_region(index, region),
986        }
987    }
988
989    /// Initialize the MemoryMapping
990    pub fn initialize(&mut self) -> Result<(), EbpfError> {
991        let result = match &mut self.ty {
992            MemoryMappingType::Aligned(inner) => inner.initialize(),
993            MemoryMappingType::Unaligned(inner) => inner.initialize(),
994        };
995        self.initialized = result.is_ok();
996        result
997    }
998
999    fn generate_access_violation(
1000        &self,
1001        access_type: AccessType,
1002        vm_addr: u64,
1003        len: u64,
1004    ) -> EbpfError {
1005        let stack_frame = (vm_addr as i64)
1006            .saturating_sub(ebpf::MM_STACK_START as i64)
1007            .checked_div(self.stack_frame_size)
1008            .unwrap_or(0);
1009        if !self.sbpf_version.manual_stack_frame_bump()
1010            && (-1..self.max_call_depth.saturating_add(1)).contains(&stack_frame)
1011        {
1012            EbpfError::StackAccessViolation(access_type, vm_addr, len, stack_frame)
1013        } else {
1014            let region = self.find_region(vm_addr);
1015            let region_name = match vm_addr & (!ebpf::MM_BYTECODE_START.saturating_sub(1)) {
1016                _ if region.map(|(_, r)| r.vm_addr_range().contains(&vm_addr)) != Some(true) => {
1017                    "unallocated"
1018                }
1019                ebpf::MM_BYTECODE_START => "program",
1020                ebpf::MM_STACK_START => "stack",
1021                ebpf::MM_HEAP_START => "heap",
1022                ebpf::MM_INPUT_START => "input",
1023                _ => "allocated",
1024            };
1025            EbpfError::AccessViolation(access_type, vm_addr, len, region_name)
1026        }
1027    }
1028}
1029
1030/// Fast, small linear cache used to speed up unaligned memory mapping.
1031#[derive(Debug)]
1032struct MappingCache {
1033    // The cached entries.
1034    entries: [(Range<u64>, usize); MappingCache::SIZE],
1035    // Index of the last accessed memory region.
1036    //
1037    // New entries are written backwards, so that find() can always scan
1038    // forward which is faster.
1039    head: usize,
1040}
1041
1042impl MappingCache {
1043    // must be a power of two
1044    const SIZE: usize = 4;
1045
1046    fn new() -> MappingCache {
1047        MappingCache {
1048            entries: array::from_fn(|_| (0..0, 0)),
1049            head: 0,
1050        }
1051    }
1052
1053    #[inline]
1054    fn find(&self, vm_addr: u64) -> Option<usize> {
1055        for i in 0..Self::SIZE {
1056            let index = self.head.wrapping_add(i) % Self::SIZE;
1057            // Safety:
1058            // index is guaranteed to be between 0..Self::SIZE
1059            let (vm_range, region_index) = unsafe { self.entries.get_unchecked(index) };
1060            if vm_range.contains(&vm_addr) {
1061                return Some(*region_index);
1062            }
1063        }
1064
1065        None
1066    }
1067
1068    #[inline]
1069    fn insert(&mut self, vm_range: Range<u64>, region_index: usize) {
1070        self.head = self.head.wrapping_sub(1) % Self::SIZE;
1071        // Safety:
1072        // self.head is guaranteed to be between 0..Self::SIZE
1073        unsafe { *self.entries.get_unchecked_mut(self.head) = (vm_range, region_index) };
1074    }
1075
1076    #[inline]
1077    fn flush(&mut self) {
1078        self.entries = array::from_fn(|_| (0..0, 0));
1079        self.head = 0;
1080    }
1081}
1082
1083#[cfg(test)]
1084mod test {
1085    use std::{cell::RefCell, rc::Rc};
1086    use test_utils::assert_error;
1087
1088    use super::*;
1089
1090    #[test]
1091    fn test_mapping_cache() {
1092        let mut cache = MappingCache::new();
1093        assert_eq!(cache.find(0), None);
1094
1095        let mut ranges = vec![10u64..20, 20..30, 30..40, 40..50];
1096        for (region, range) in ranges.iter().cloned().enumerate() {
1097            cache.insert(range, region);
1098        }
1099        for (region, range) in ranges.iter().enumerate() {
1100            if region > 0 {
1101                assert_eq!(cache.find(range.start - 1), Some(region - 1));
1102            } else {
1103                assert_eq!(cache.find(range.start - 1), None);
1104            }
1105            assert_eq!(cache.find(range.start), Some(region));
1106            assert_eq!(cache.find(range.start + 1), Some(region));
1107            assert_eq!(cache.find(range.end - 1), Some(region));
1108            if region < 3 {
1109                assert_eq!(cache.find(range.end), Some(region + 1));
1110            } else {
1111                assert_eq!(cache.find(range.end), None);
1112            }
1113        }
1114
1115        cache.insert(50..60, 4);
1116        ranges.push(50..60);
1117        for (region, range) in ranges.iter().enumerate() {
1118            if region == 0 {
1119                assert_eq!(cache.find(range.start), None);
1120                continue;
1121            }
1122            if region > 1 {
1123                assert_eq!(cache.find(range.start - 1), Some(region - 1));
1124            } else {
1125                assert_eq!(cache.find(range.start - 1), None);
1126            }
1127            assert_eq!(cache.find(range.start), Some(region));
1128            assert_eq!(cache.find(range.start + 1), Some(region));
1129            assert_eq!(cache.find(range.end - 1), Some(region));
1130            if region < 4 {
1131                assert_eq!(cache.find(range.end), Some(region + 1));
1132            } else {
1133                assert_eq!(cache.find(range.end), None);
1134            }
1135        }
1136    }
1137
1138    #[test]
1139    fn test_mapping_cache_flush() {
1140        let mut cache = MappingCache::new();
1141        assert_eq!(cache.find(0), None);
1142        cache.insert(0..10, 0);
1143        assert_eq!(cache.find(0), Some(0));
1144        cache.flush();
1145        assert_eq!(cache.find(0), None);
1146    }
1147
1148    #[test]
1149    fn test_map_empty() {
1150        for aligned_memory_mapping in [false, true] {
1151            let config = Config {
1152                aligned_memory_mapping,
1153                ..Config::default()
1154            };
1155            let m = unsafe { MemoryMapping::new(vec![], &config, SBPFVersion::V3) }.unwrap();
1156            assert_error!(
1157                m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 8),
1158                "AccessViolation"
1159            );
1160        }
1161    }
1162
1163    #[test]
1164    fn test_gapped_map() {
1165        for aligned_memory_mapping in [false, true] {
1166            let config = Config {
1167                aligned_memory_mapping,
1168                ..Config::default()
1169            };
1170            let mut mem1 = [0xff; 8];
1171            let mem2 = [0; 8];
1172            let mut m = unsafe {
1173                MemoryMapping::new(
1174                    vec![
1175                        MemoryRegion::new(&raw const mem2[..], ebpf::MM_REGION_SIZE),
1176                        MemoryRegion::new_gapped(&raw mut mem1[..], ebpf::MM_REGION_SIZE * 2, 2),
1177                    ],
1178                    &config,
1179                    SBPFVersion::V3,
1180                )
1181                .unwrap()
1182            };
1183            for frame in 0..4 {
1184                let address = ebpf::MM_STACK_START + frame * 4;
1185                assert!(m.find_region(address).is_some());
1186                assert!(m.map(AccessType::Load, address, 2).is_ok());
1187                assert_error!(m.map(AccessType::Load, address + 2, 2), "AccessViolation");
1188                assert_eq!(m.load::<u16>(address).unwrap(), 0xFFFF);
1189                assert_error!(m.load::<u16>(address + 2), "AccessViolation");
1190                assert!(m.store::<u16>(0xFFFF, address).is_ok());
1191                assert_error!(m.store::<u16>(0xFFFF, address + 2), "AccessViolation");
1192            }
1193        }
1194    }
1195
1196    #[test]
1197    fn test_unaligned_map_overlap() {
1198        let config = Config {
1199            aligned_memory_mapping: false,
1200            ..Config::default()
1201        };
1202        let mem1 = [1, 2, 3, 4];
1203        let mem2 = [5, 6];
1204        assert_error!(
1205            unsafe {
1206                MemoryMapping::new(
1207                    vec![
1208                        MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1209                        MemoryRegion::new(
1210                            &raw const mem2,
1211                            ebpf::MM_REGION_SIZE + mem1.len() as u64 - 1,
1212                        ),
1213                    ],
1214                    &config,
1215                    SBPFVersion::V3,
1216                )
1217            },
1218            "InvalidMemoryRegion(1)"
1219        );
1220        assert!(unsafe {
1221            MemoryMapping::new(
1222                vec![
1223                    MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1224                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1225                ],
1226                &config,
1227                SBPFVersion::V3,
1228            )
1229        }
1230        .is_ok());
1231    }
1232
1233    #[test]
1234    fn test_unaligned_map() {
1235        let config = Config {
1236            aligned_memory_mapping: false,
1237            ..Config::default()
1238        };
1239        let mut mem1 = [11];
1240        let mem2 = [22, 22];
1241        let mem3 = [33];
1242        let mem4 = [44, 44];
1243        let m = unsafe {
1244            MemoryMapping::new(
1245                vec![
1246                    MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1247                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1248                    MemoryRegion::new(
1249                        &raw const mem3,
1250                        ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len()) as u64,
1251                    ),
1252                    MemoryRegion::new(
1253                        &raw const mem4,
1254                        ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len() + mem3.len()) as u64,
1255                    ),
1256                ],
1257                &config,
1258                SBPFVersion::V3,
1259            )
1260            .unwrap()
1261        };
1262
1263        assert_eq!(
1264            m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1265                .unwrap()
1266                .ptr()
1267                .addr(),
1268            mem1.as_ptr().addr()
1269        );
1270
1271        assert_eq!(
1272            m.map(AccessType::Store, ebpf::MM_REGION_SIZE, 1)
1273                .unwrap()
1274                .ptr()
1275                .addr(),
1276            mem1.as_ptr().addr()
1277        );
1278
1279        assert_error!(
1280            m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 2),
1281            "AccessViolation"
1282        );
1283
1284        assert_eq!(
1285            m.map(
1286                AccessType::Load,
1287                ebpf::MM_REGION_SIZE + mem1.len() as u64,
1288                1,
1289            )
1290            .unwrap()
1291            .ptr()
1292            .addr(),
1293            mem2.as_ptr().addr()
1294        );
1295
1296        assert_eq!(
1297            m.map(
1298                AccessType::Load,
1299                ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len()) as u64,
1300                1,
1301            )
1302            .unwrap()
1303            .ptr()
1304            .addr(),
1305            mem3.as_ptr().addr()
1306        );
1307
1308        assert_eq!(
1309            m.map(
1310                AccessType::Load,
1311                ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len() + mem3.len()) as u64,
1312                1,
1313            )
1314            .unwrap()
1315            .ptr()
1316            .addr(),
1317            mem4.as_ptr().addr()
1318        );
1319
1320        assert_error!(
1321            m.map(
1322                AccessType::Load,
1323                ebpf::MM_REGION_SIZE + (mem1.len() + mem2.len() + mem3.len() + mem4.len()) as u64,
1324                1,
1325            ),
1326            "AccessViolation"
1327        );
1328    }
1329
1330    #[test]
1331    fn test_unaligned_region() {
1332        let config = Config {
1333            aligned_memory_mapping: false,
1334            ..Config::default()
1335        };
1336
1337        let mut mem1 = [0xFF; 4];
1338        let mem2 = [0xDD; 4];
1339        let m = unsafe {
1340            MemoryMapping::new(
1341                vec![
1342                    MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1343                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + 4),
1344                ],
1345                &config,
1346                SBPFVersion::V3,
1347            )
1348            .unwrap()
1349        };
1350        assert!(m.find_region(ebpf::MM_REGION_SIZE - 1).is_none());
1351        assert_eq!(
1352            HostBuffer::Mutable(&raw mut mem1[..]),
1353            m.find_region(ebpf::MM_REGION_SIZE).unwrap().1.host,
1354        );
1355        assert_eq!(
1356            HostBuffer::Mutable(&raw mut mem1[..]),
1357            m.find_region(ebpf::MM_REGION_SIZE + 3).unwrap().1.host,
1358        );
1359        assert_eq!(
1360            HostBuffer::Immutable(&raw const mem2[..]),
1361            m.find_region(ebpf::MM_REGION_SIZE + 4).unwrap().1.host,
1362        );
1363        assert_eq!(
1364            HostBuffer::Immutable(&raw const mem2[..]),
1365            m.find_region(ebpf::MM_REGION_SIZE + 7).unwrap().1.host,
1366        );
1367        assert!(m.find_region(ebpf::MM_REGION_SIZE + 8).is_some());
1368    }
1369
1370    #[test]
1371    fn test_aligned_region() {
1372        let config = Config {
1373            aligned_memory_mapping: true,
1374            ..Config::default()
1375        };
1376
1377        let mut mem1 = [0xFF; 4];
1378        let mem2 = [0xDD; 4];
1379        let m = unsafe {
1380            MemoryMapping::new(
1381                vec![
1382                    MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1383                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE * 2),
1384                ],
1385                &config,
1386                SBPFVersion::V4,
1387            )
1388            .unwrap()
1389        };
1390        assert_eq!(m.find_region(ebpf::MM_REGION_SIZE - 1).unwrap().1.len(), 0);
1391        assert_eq!(
1392            HostBuffer::Mutable(&raw mut mem1[..]),
1393            m.find_region(ebpf::MM_REGION_SIZE).unwrap().1.host,
1394        );
1395        assert_eq!(
1396            HostBuffer::Mutable(&raw mut mem1[..]),
1397            m.find_region(ebpf::MM_REGION_SIZE + 3).unwrap().1.host,
1398        );
1399        assert!(m.find_region(ebpf::MM_REGION_SIZE + 4).is_some());
1400        assert_eq!(
1401            HostBuffer::Immutable(&raw const mem2[..]),
1402            m.find_region(ebpf::MM_REGION_SIZE * 2).unwrap().1.host,
1403        );
1404        assert_eq!(
1405            HostBuffer::Immutable(&raw const mem2[..]),
1406            m.find_region(ebpf::MM_REGION_SIZE * 2 + 3).unwrap().1.host,
1407        );
1408        assert!(m.find_region(ebpf::MM_REGION_SIZE * 3 + 4).is_none());
1409    }
1410
1411    #[test]
1412    fn test_unaligned_map_load() {
1413        let config = Config {
1414            aligned_memory_mapping: false,
1415            ..Config::default()
1416        };
1417        let mem1 = [0x11, 0x22];
1418        let mem2 = [0x33];
1419        let mut m = unsafe {
1420            MemoryMapping::new(
1421                vec![
1422                    MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1423                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1424                ],
1425                &config,
1426                SBPFVersion::V3,
1427            )
1428            .unwrap()
1429        };
1430
1431        assert_eq!(m.load::<u16>(ebpf::MM_REGION_SIZE).unwrap(), 0x2211);
1432        assert_error!(m.load::<u32>(ebpf::MM_REGION_SIZE), "AccessViolation");
1433        assert_error!(m.load::<u32>(ebpf::MM_REGION_SIZE + 4), "AccessViolation");
1434    }
1435
1436    #[test]
1437    fn test_unaligned_map_store() {
1438        let config = Config {
1439            aligned_memory_mapping: false,
1440            ..Config::default()
1441        };
1442        let mut mem1 = [0xff, 0xff];
1443        let mut mem2 = [0xff];
1444        let mut m = unsafe {
1445            MemoryMapping::new(
1446                vec![
1447                    MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1448                    MemoryRegion::new(&raw mut mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1449                ],
1450                &config,
1451                SBPFVersion::V3,
1452            )
1453            .unwrap()
1454        };
1455
1456        m.store(0x1122u16, ebpf::MM_REGION_SIZE).unwrap();
1457        assert_eq!(m.load::<u16>(ebpf::MM_REGION_SIZE).unwrap(), 0x1122);
1458
1459        assert_error!(
1460            m.store(0x33445566u32, ebpf::MM_REGION_SIZE),
1461            "AccessViolation"
1462        );
1463    }
1464
1465    #[test]
1466    fn test_unaligned_map_store_out_of_bounds() {
1467        let config = Config {
1468            aligned_memory_mapping: false,
1469            ..Config::default()
1470        };
1471
1472        let mut mem1 = [0xFF];
1473        let mut m = unsafe {
1474            MemoryMapping::new(
1475                vec![MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE)],
1476                &config,
1477                SBPFVersion::V3,
1478            )
1479            .unwrap()
1480        };
1481        m.store(0x11u8, ebpf::MM_REGION_SIZE).unwrap();
1482        assert_error!(m.store(0x11u8, ebpf::MM_REGION_SIZE - 1), "AccessViolation");
1483        assert_error!(m.store(0x11u8, ebpf::MM_REGION_SIZE + 1), "AccessViolation");
1484        // this gets us line coverage for the case where we're completely
1485        // outside the address space (the case above is just on the edge)
1486        assert_error!(m.store(0x11u8, ebpf::MM_REGION_SIZE + 2), "AccessViolation");
1487
1488        let mut mem1 = [0xFF; 4];
1489        let mut mem2 = [0xDD; 4];
1490        let mut m = unsafe {
1491            MemoryMapping::new(
1492                vec![
1493                    MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1494                    MemoryRegion::new(&raw mut mem2, ebpf::MM_REGION_SIZE + 4),
1495                ],
1496                &config,
1497                SBPFVersion::V3,
1498            )
1499            .unwrap()
1500        };
1501        assert_error!(
1502            m.store(0x1122334455667788u64, ebpf::MM_REGION_SIZE),
1503            "AccessViolation"
1504        );
1505    }
1506
1507    #[test]
1508    fn test_unaligned_map_load_out_of_bounds() {
1509        let config = Config {
1510            aligned_memory_mapping: false,
1511            ..Config::default()
1512        };
1513
1514        let mem1 = [0xff];
1515        let mut m = unsafe {
1516            MemoryMapping::new(
1517                vec![MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE)],
1518                &config,
1519                SBPFVersion::V3,
1520            )
1521            .unwrap()
1522        };
1523        assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 0xff);
1524        assert_error!(m.load::<u8>(ebpf::MM_REGION_SIZE - 1), "AccessViolation");
1525        assert_error!(m.load::<u8>(ebpf::MM_REGION_SIZE + 1), "AccessViolation");
1526        assert_error!(m.load::<u8>(ebpf::MM_REGION_SIZE + 2), "AccessViolation");
1527
1528        let mem1 = [0xFF; 4];
1529        let mem2 = [0xDD; 4];
1530        let mut m = unsafe {
1531            MemoryMapping::new(
1532                vec![
1533                    MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1534                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + 4),
1535                ],
1536                &config,
1537                SBPFVersion::V3,
1538            )
1539            .unwrap()
1540        };
1541        assert_error!(m.load::<u64>(ebpf::MM_REGION_SIZE), "AccessViolation");
1542    }
1543
1544    #[test]
1545    #[should_panic(expected = "AccessViolation")]
1546    fn test_store_readonly() {
1547        let config = Config {
1548            aligned_memory_mapping: false,
1549            ..Config::default()
1550        };
1551        let mut mem1 = [0xff, 0xff];
1552        let mem2 = [0xff, 0xff];
1553        let mut m = unsafe {
1554            MemoryMapping::new(
1555                vec![
1556                    MemoryRegion::new(&raw mut mem1, ebpf::MM_REGION_SIZE),
1557                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1558                ],
1559                &config,
1560                SBPFVersion::V3,
1561            )
1562            .unwrap()
1563        };
1564        m.store(0x11223344, ebpf::MM_REGION_SIZE).unwrap();
1565    }
1566
1567    #[test]
1568    fn test_unaligned_map_replace_region() {
1569        let config = Config {
1570            aligned_memory_mapping: false,
1571            ..Config::default()
1572        };
1573        let mem1 = [11];
1574        let mem2 = [22, 22];
1575        let mem3 = [33];
1576        let mut m = unsafe {
1577            MemoryMapping::new(
1578                vec![
1579                    MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1580                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1581                ],
1582                &config,
1583                SBPFVersion::V3,
1584            )
1585            .unwrap()
1586        };
1587
1588        assert_eq!(
1589            m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1590                .unwrap()
1591                .ptr()
1592                .addr(),
1593            mem1.as_ptr().addr()
1594        );
1595
1596        assert_eq!(
1597            m.map(
1598                AccessType::Load,
1599                ebpf::MM_REGION_SIZE + mem1.len() as u64,
1600                1,
1601            )
1602            .unwrap()
1603            .ptr()
1604            .addr(),
1605            mem2.as_ptr().addr()
1606        );
1607
1608        assert_error!(
1609            unsafe {
1610                m.replace_region(
1611                    2,
1612                    MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1613                )
1614            },
1615            "InvalidMemoryRegion(2)"
1616        );
1617
1618        let region_index = m
1619            .get_regions()
1620            .iter()
1621            .position(|mem| mem.vm_addr == ebpf::MM_REGION_SIZE + mem1.len() as u64)
1622            .unwrap();
1623
1624        // old.vm_addr != new.vm_addr
1625        assert_error!(
1626            unsafe {
1627                m.replace_region(
1628                    region_index,
1629                    MemoryRegion::new(
1630                        &raw const mem3,
1631                        ebpf::MM_REGION_SIZE + mem1.len() as u64 + 1,
1632                    ),
1633                )
1634            },
1635            "InvalidMemoryRegion({})",
1636            region_index
1637        );
1638
1639        unsafe {
1640            m.replace_region(
1641                region_index,
1642                MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE + mem1.len() as u64),
1643            )
1644            .unwrap()
1645        };
1646
1647        assert_eq!(
1648            m.map(
1649                AccessType::Load,
1650                ebpf::MM_REGION_SIZE + mem1.len() as u64,
1651                1,
1652            )
1653            .unwrap()
1654            .ptr()
1655            .addr(),
1656            mem3.as_ptr().addr()
1657        );
1658    }
1659
1660    #[test]
1661    fn test_aligned_map_replace_region() {
1662        let config = Config {
1663            aligned_memory_mapping: true,
1664            ..Config::default()
1665        };
1666        let mem1 = [11];
1667        let mem2 = [22, 22];
1668        let mem3 = [33, 33];
1669        let mut m = unsafe {
1670            MemoryMapping::new(
1671                vec![
1672                    MemoryRegion::new(&raw const mem1, ebpf::MM_REGION_SIZE),
1673                    MemoryRegion::new(&raw const mem2, ebpf::MM_REGION_SIZE * 2),
1674                ],
1675                &config,
1676                SBPFVersion::V4,
1677            )
1678            .unwrap()
1679        };
1680
1681        assert_eq!(
1682            m.map(AccessType::Load, ebpf::MM_REGION_SIZE * 2, 1)
1683                .unwrap()
1684                .ptr()
1685                .addr(),
1686            mem2.as_ptr().addr()
1687        );
1688
1689        // index > regions.len()
1690        assert_error!(
1691            unsafe {
1692                m.replace_region(
1693                    3,
1694                    MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 2),
1695                )
1696            },
1697            "InvalidMemoryRegion(3)"
1698        );
1699
1700        // index != addr >> VIRTUAL_ADDRESS_BITS
1701        assert_error!(
1702            unsafe {
1703                m.replace_region(
1704                    2,
1705                    MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 3),
1706                )
1707            },
1708            "InvalidMemoryRegion(2)"
1709        );
1710
1711        // index + len != addr >> VIRTUAL_ADDRESS_BITS
1712        assert_error!(
1713            unsafe {
1714                m.replace_region(
1715                    2,
1716                    MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 3 - 1),
1717                )
1718            },
1719            "InvalidMemoryRegion(2)"
1720        );
1721
1722        unsafe {
1723            m.replace_region(
1724                2,
1725                MemoryRegion::new(&raw const mem3, ebpf::MM_REGION_SIZE * 2),
1726            )
1727            .unwrap()
1728        };
1729
1730        assert_eq!(
1731            m.map(AccessType::Load, ebpf::MM_REGION_SIZE * 2, 1)
1732                .unwrap()
1733                .ptr()
1734                .addr(),
1735            mem3.as_ptr().addr()
1736        );
1737    }
1738
1739    #[test]
1740    fn test_access_violation_handler_map() {
1741        for aligned_memory_mapping in [true, false] {
1742            let config = Config {
1743                aligned_memory_mapping,
1744                ..Config::default()
1745            };
1746            let original = [11, 22];
1747            let copied = Rc::new(RefCell::new(Vec::new()));
1748            let mut regions = vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)];
1749            regions[0].access_violation_handler_payload = Some(0);
1750
1751            let c = Rc::clone(&copied);
1752            let mut m = unsafe {
1753                MemoryMapping::new_with_access_violation_handler(
1754                    regions,
1755                    &config,
1756                    SBPFVersion::V3,
1757                    Box::new(move |region, _, _, _, _| {
1758                        let mut vec = c.borrow_mut();
1759                        vec.extend_from_slice(&original);
1760                        region.redirect(&raw mut vec[..]);
1761                    }),
1762                )
1763                .unwrap()
1764            };
1765
1766            assert_eq!(
1767                m.map_with_access_violation_handler(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1768                    .unwrap()
1769                    .ptr()
1770                    .addr(),
1771                original.as_ptr().addr()
1772            );
1773            assert_eq!(
1774                m.map_with_access_violation_handler(AccessType::Store, ebpf::MM_REGION_SIZE, 1)
1775                    .unwrap()
1776                    .ptr()
1777                    .addr(),
1778                copied.borrow().as_ptr().addr()
1779            );
1780        }
1781    }
1782
1783    #[test]
1784    fn test_access_violation_handler_load_store() {
1785        for aligned_memory_mapping in [true, false] {
1786            let config = Config {
1787                aligned_memory_mapping,
1788                ..Config::default()
1789            };
1790            let original = [11, 22];
1791            let copied = Rc::new(RefCell::new(Vec::new()));
1792            let mut regions = vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)];
1793            regions[0].access_violation_handler_payload = Some(0);
1794
1795            let c = Rc::clone(&copied);
1796            let mut m = unsafe {
1797                MemoryMapping::new_with_access_violation_handler(
1798                    regions,
1799                    &config,
1800                    SBPFVersion::V3,
1801                    Box::new(move |region, _, _, _, _| {
1802                        let mut vec = c.borrow_mut();
1803                        vec.extend_from_slice(&original);
1804                        region.redirect(&raw mut vec[..]);
1805                    }),
1806                )
1807                .unwrap()
1808            };
1809
1810            assert_eq!(
1811                m.map(AccessType::Load, ebpf::MM_REGION_SIZE, 1)
1812                    .unwrap()
1813                    .ptr()
1814                    .addr(),
1815                original.as_ptr().addr()
1816            );
1817
1818            assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 11);
1819            assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE + 1).unwrap(), 22);
1820            assert!(copied.borrow().is_empty());
1821
1822            m.store(33u8, ebpf::MM_REGION_SIZE).unwrap();
1823            assert_eq!(original[0], 11);
1824            assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 33);
1825            assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE + 1).unwrap(), 22);
1826        }
1827    }
1828
1829    #[test]
1830    fn test_access_violation_handler_region_id() {
1831        for aligned_memory_mapping in [true, false] {
1832            let config = Config {
1833                aligned_memory_mapping,
1834                ..Config::default()
1835            };
1836            let original1 = [11, 22];
1837            let original2 = [33, 44];
1838            let copied = Rc::new(RefCell::new(Vec::new()));
1839
1840            let mut regions = vec![
1841                MemoryRegion::new(&raw const original1, ebpf::MM_REGION_SIZE),
1842                MemoryRegion::new(&raw const original2, ebpf::MM_REGION_SIZE * 2),
1843            ];
1844            regions[0].access_violation_handler_payload = Some(42);
1845
1846            let c = Rc::clone(&copied);
1847            let mut m = unsafe {
1848                MemoryMapping::new_with_access_violation_handler(
1849                    regions,
1850                    &config,
1851                    SBPFVersion::V3,
1852                    Box::new(move |region, _, _, _, _| {
1853                        // check that the argument passed to MemoryRegion::new is then passed to the
1854                        // callback
1855                        assert_eq!(region.access_violation_handler_payload, Some(42));
1856                        let mut vec = c.borrow_mut();
1857                        vec.extend_from_slice(&original1);
1858                        region.redirect(&raw mut vec[..]);
1859                    }),
1860                )
1861                .unwrap()
1862            };
1863
1864            m.store(55u8, ebpf::MM_REGION_SIZE).unwrap();
1865            assert_eq!(original1[0], 11);
1866            assert_eq!(m.load::<u8>(ebpf::MM_REGION_SIZE).unwrap(), 55);
1867        }
1868    }
1869
1870    #[test]
1871    #[should_panic(expected = "AccessViolation")]
1872    fn test_map_access_violation_handler_error() {
1873        let config = Config::default();
1874        let original = [11, 22];
1875
1876        let m = unsafe {
1877            MemoryMapping::new_with_access_violation_handler(
1878                vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)],
1879                &config,
1880                SBPFVersion::V4,
1881                Box::new(default_access_violation_handler),
1882            )
1883            .unwrap()
1884        };
1885
1886        m.map(AccessType::Store, ebpf::MM_REGION_SIZE, 1).unwrap();
1887    }
1888
1889    #[test]
1890    #[should_panic(expected = "AccessViolation")]
1891    fn test_store_access_violation_handler_error() {
1892        let config = Config::default();
1893        let original = [11, 22];
1894
1895        let mut m = unsafe {
1896            MemoryMapping::new_with_access_violation_handler(
1897                vec![MemoryRegion::new(&raw const original, ebpf::MM_REGION_SIZE)],
1898                &config,
1899                SBPFVersion::V4,
1900                Box::new(default_access_violation_handler),
1901            )
1902            .unwrap()
1903        };
1904
1905        m.store(33u8, ebpf::MM_REGION_SIZE).unwrap();
1906    }
1907
1908    #[test]
1909    fn test_access_violation_region_identification() {
1910        let config = Config::default();
1911        let original = [11, 22];
1912        let region = 0x10_0000_0000;
1913        let mut m = unsafe {
1914            MemoryMapping::new(
1915                vec![MemoryRegion::new(&raw const original, region)],
1916                &config,
1917                SBPFVersion::V4,
1918            )
1919            .unwrap()
1920        };
1921        let store_err_inbound = m.store(33u8, region).unwrap_err();
1922        assert_eq!(
1923            store_err_inbound.to_string(),
1924            "Access violation writing 1 bytes at address 0x1000000000 (in allocated region)"
1925        );
1926        let store_err_oob = m.load::<u64>(region + 3).unwrap_err();
1927        assert_eq!(
1928            store_err_oob.to_string(),
1929            "Access violation reading 8 bytes at address 0x1000000003 (in unallocated region)"
1930        );
1931    }
1932
1933    #[test]
1934    fn v4_aligned_mapping() {
1935        let config = Config {
1936            aligned_memory_mapping: false,
1937            ..Config::default()
1938        };
1939
1940        let mem = [11, 12];
1941        let mapping = unsafe {
1942            MemoryMapping::new_with_access_violation_handler(
1943                vec![MemoryRegion::new(&raw const mem, ebpf::MM_REGION_SIZE)],
1944                &config,
1945                SBPFVersion::V4,
1946                Box::new(default_access_violation_handler),
1947            )
1948            .unwrap()
1949        };
1950
1951        assert!(matches!(mapping.ty, MemoryMappingType::Aligned(_)));
1952    }
1953}