Skip to main content

ebpf/memio/
mod.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use derivative::Derivative;
6use smallvec::SmallVec;
7use std::marker::PhantomData;
8use std::mem::MaybeUninit;
9use std::ops::{Range, RangeBounds};
10use zerocopy::{FromBytes, IntoBytes};
11
12#[cfg(target_arch = "aarch64")]
13mod arm64;
14
15#[cfg(target_arch = "aarch64")]
16use arm64 as arch;
17
18#[cfg(target_arch = "x86_64")]
19mod x64;
20
21#[cfg(target_arch = "x86_64")]
22use x64 as arch;
23
24#[cfg(target_arch = "riscv64")]
25mod riscv64;
26
27#[cfg(target_arch = "riscv64")]
28use riscv64 as arch;
29
30/// Pointer to a buffer that may be shared between eBPF programs. It allows to
31/// safely pass around pointers to the data stored in eBPF maps and access the
32/// data referenced by the pointer.
33#[derive(Derivative)]
34#[derivative(Copy(bound = ""), Clone(bound = ""))]
35pub struct EbpfPtr<'a, T> {
36    ptr: *mut T,
37    phantom: PhantomData<&'a T>,
38}
39
40#[allow(clippy::undocumented_unsafe_blocks, reason = "Force documented unsafe blocks in Starnix")]
41unsafe impl<'a, T> Send for EbpfPtr<'a, T> {}
42#[allow(clippy::undocumented_unsafe_blocks, reason = "Force documented unsafe blocks in Starnix")]
43unsafe impl<'a, T> Sync for EbpfPtr<'a, T> {}
44
45impl<'a, T> EbpfPtr<'a, T>
46where
47    T: Sized,
48{
49    /// Creates a new `EbpfPtr` from the specified pointer.
50    ///
51    /// # Safety
52    /// Caller must ensure that the buffer referenced by `ptr` is valid for
53    /// lifetime `'a` and there are no other mutable references to the same memory.
54    pub unsafe fn new(ptr: *mut T) -> Self {
55        Self { ptr, phantom: PhantomData }
56    }
57
58    /// # Safety
59    /// Caller must ensure that the value cannot be updated by other threads
60    /// while the returned reference is live.
61    pub unsafe fn deref(&self) -> &'a T {
62        #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
63        unsafe {
64            &*self.ptr
65        }
66    }
67
68    /// # Safety
69    /// Caller must ensure that the value is not being used by other threads.
70    pub unsafe fn deref_mut(&self) -> &'a mut T {
71        #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
72        unsafe {
73            &mut *self.ptr
74        }
75    }
76
77    pub fn get_field<F, const OFFSET: usize>(&self) -> EbpfPtr<'a, F> {
78        assert!(OFFSET + std::mem::size_of::<F>() <= std::mem::size_of::<T>());
79        // SAFETY: offset is guaranteed to be within the bounds of the struct,
80        // see the assert above.
81        let field_ptr = unsafe { self.ptr.byte_offset(OFFSET as isize) } as *mut F;
82        EbpfPtr::<'a, F> { ptr: field_ptr, phantom: PhantomData }
83    }
84
85    pub fn ptr(&self) -> *mut T {
86        self.ptr
87    }
88}
89
90impl<'a, T> From<&'a mut T> for EbpfPtr<'a, T>
91where
92    T: IntoBytes + FromBytes + Sized,
93{
94    fn from(value: &'a mut T) -> Self {
95        let ptr = value.as_mut_bytes().as_mut_ptr() as *mut T;
96        // SAFETY: We borrow a mutable reference to T for the lifetime 'a.
97        // This guarantees that the returned pointer is valid for the lifetime
98        // 'a and there are no other mutable references.
99        unsafe { Self::new(ptr) }
100    }
101}
102
103impl EbpfPtr<'_, u64> {
104    /// Loads the value referenced by the pointer. Atomicity is guaranteed
105    /// if and only if the pointer is 8-byte aligned.
106    pub fn load_relaxed(&self) -> u64 {
107        // SAFETY: Atomic load of the value referenced by the pointer.
108        unsafe { arch::load_u64(self.ptr) }
109    }
110
111    /// Stores the `value` at the memory referenced by the pointer. Atomicity
112    /// is guaranteed if and only if the pointer is 8-byte aligned.
113    pub fn store_relaxed(&self, value: u64) {
114        // SAFETY: Atomic store of the value referenced by the pointer.
115        unsafe { arch::store_u64(self.ptr, value) }
116    }
117}
118
119impl EbpfPtr<'_, u32> {
120    /// Loads the value referenced by the pointer. Atomicity is guaranteed
121    /// if and only if the pointer is 4-byte aligned.
122    pub fn load_relaxed(&self) -> u32 {
123        // SAFETY: Atomic load of the value referenced by the pointer.
124        unsafe { arch::load_u32(self.ptr) }
125    }
126
127    /// Stores the `value` at the memory referenced by the pointer. Atomicity
128    /// is guaranteed if and only if the pointer is 4-byte aligned.
129    pub fn store_relaxed(&self, value: u32) {
130        // SAFETY: Atomic store of the value referenced by the pointer.
131        unsafe { arch::store_u32(self.ptr, value) }
132    }
133}
134
135impl EbpfPtr<'_, i32> {
136    /// Loads the value referenced by the pointer. Atomicity is guaranteed
137    /// if and only if the pointer is 4-byte aligned.
138    pub fn load_relaxed(&self) -> i32 {
139        // SAFETY: Atomic load of the value referenced by the pointer.
140        unsafe { arch::load_u32(self.ptr as *mut u32) as i32 }
141    }
142
143    /// Stores the `value` at the memory referenced by the pointer. Atomicity
144    /// is guaranteed if and only if the pointer is 4-byte aligned.
145    pub fn store_relaxed(&self, value: i32) {
146        // SAFETY: Atomic store of the value referenced by the pointer.
147        unsafe { arch::store_u32(self.ptr as *mut u32, value as u32) }
148    }
149}
150
151impl EbpfPtr<'_, u16> {
152    /// Loads the value referenced by the pointer. Atomicity is guaranteed
153    /// if and only if the pointer is 2-byte aligned.
154    pub fn load_relaxed(&self) -> u16 {
155        // SAFETY: Atomic load of the value referenced by the pointer.
156        unsafe { arch::load_u16(self.ptr) }
157    }
158
159    /// Stores the `value` at the memory referenced by the pointer. Atomicity
160    /// is guaranteed if and only if the pointer is 2-byte aligned.
161    pub fn store_relaxed(&self, value: u16) {
162        // SAFETY: Atomic store of the value referenced by the pointer.
163        unsafe { arch::store_u16(self.ptr, value) }
164    }
165}
166
167impl EbpfPtr<'_, u8> {
168    /// Loads the value referenced by the pointer.
169    pub fn load_relaxed(&self) -> u8 {
170        // SAFETY: Atomic load of the value referenced by the pointer.
171        unsafe { arch::load_u8(self.ptr) }
172    }
173
174    /// Stores the `value` at the memory referenced by the pointer.
175    pub fn store_relaxed(&self, value: u8) {
176        // SAFETY: Atomic store of the value referenced by the pointer.
177        unsafe { arch::store_u8(self.ptr, value) }
178    }
179}
180
181/// Wraps a pointer to buffer used in eBPF runtime, such as an eBPF maps
182/// entry. The referenced data may be access from multiple threads in parallel,
183/// which makes it unsafe to access it using standard Rust types.
184/// `EbpfBufferPtr` allows to access these buffers safely. It may be used to
185/// reference either a whole VMO allocated for and eBPF map or individual
186/// elements of that VMO (see `slice()`). The address and the size of the
187/// buffer are always 8-byte aligned.
188#[derive(Copy, Clone)]
189pub struct EbpfBufferPtr<'a> {
190    ptr: *mut u8,
191    size: usize,
192    phantom: PhantomData<&'a u8>,
193}
194
195impl<'a> EbpfBufferPtr<'a> {
196    pub const ALIGNMENT: usize = size_of::<u64>();
197
198    /// Creates a new `EbpfBufferPtr` from the specified pointer.
199    ///
200    /// # Safety
201    /// Caller must ensure that the buffer referenced by `ptr` is valid for
202    /// lifetime `'a`.
203    pub unsafe fn new(ptr: *mut u8, size: usize) -> Self {
204        Self { ptr, size, phantom: PhantomData }
205    }
206
207    /// Size of the buffer in bytes.
208    pub fn len(&self) -> usize {
209        self.size
210    }
211
212    /// Raw pointer to the start of the buffer.
213    pub fn raw_ptr(&self) -> *mut u8 {
214        self.ptr
215    }
216
217    // SAFETY: caller must ensure that the value at the specified offset fits
218    // the buffer.
219    unsafe fn get_ptr_internal<T>(&self, offset: usize) -> EbpfPtr<'a, T> {
220        // SAFETY: The caller is expected to ensure that the pointer is valid
221        // for the lifetime 'a.
222        unsafe { EbpfPtr::new(self.ptr.byte_offset(offset as isize) as *mut T) }
223    }
224
225    /// Returns a pointer to a value of type `T` at the specified `offset`.
226    pub fn get_ptr<T>(&self, offset: usize) -> Option<EbpfPtr<'a, T>> {
227        if offset + std::mem::size_of::<T>() <= self.size {
228            // SAFETY: Buffer bounds are verified above.
229            Some(unsafe { self.get_ptr_internal(offset) })
230        } else {
231            None
232        }
233    }
234
235    /// Returns pointer to the specified range in the buffer.
236    pub fn slice(&self, range: impl RangeBounds<usize>) -> Option<Self> {
237        let start = match range.start_bound() {
238            std::ops::Bound::Included(&start) => start,
239            std::ops::Bound::Excluded(&start) => start + 1,
240            std::ops::Bound::Unbounded => 0,
241        };
242        let end = match range.end_bound() {
243            std::ops::Bound::Included(&end) => end + 1,
244            std::ops::Bound::Excluded(&end) => end,
245            std::ops::Bound::Unbounded => self.size,
246        };
247
248        assert!(start <= end);
249        (end <= self.size).then(|| {
250            // SAFETY: Returned buffer has the same lifetime as `self`, which
251            // ensures that the `ptr` stays valid for the lifetime of the
252            // result.
253            unsafe {
254                Self {
255                    ptr: self.ptr.byte_offset(start as isize),
256                    size: end - start,
257                    phantom: PhantomData,
258                }
259            }
260        })
261    }
262
263    /// Loads contents of the buffer into the specified slice, `dst` must be
264    /// of the same size as `self`.
265    pub fn load_to_slice(&self, dst: &mut [MaybeUninit<u8>]) {
266        assert_eq!(dst.len(), self.size);
267
268        let mut src_ptr = self.ptr;
269        // SAFETY: `len` is validated above to be within bounds.
270        let src_end = unsafe { src_ptr.add(self.size) };
271
272        let Range { start: dst_ptr, end: dst_end } = dst.as_mut_ptr_range();
273        let mut dst_ptr = dst_ptr as *mut u8;
274        let dst_end = dst_end as *mut u8;
275
276        if src_ptr as usize % 8 > 0 {
277            if src_ptr < src_end && src_ptr as usize % 2 > 0 {
278                // SAFETY: Pointers are verified to be within the buffer bounds.
279                unsafe {
280                    let value: u8 = arch::load_u8(src_ptr as *const u8);
281                    std::ptr::write_unaligned(dst_ptr, value);
282                    src_ptr = src_ptr.add(1);
283                    dst_ptr = dst_ptr.add(1);
284                };
285            }
286
287            if src_ptr as usize + 2 <= src_end as usize && src_ptr as usize % 4 > 0 {
288                // SAFETY: Pointers are verified to be within the buffer bounds.
289                unsafe {
290                    let value: u16 = arch::load_u16(src_ptr as *const u16);
291                    std::ptr::write_unaligned(dst_ptr as *mut u16, value);
292                    src_ptr = src_ptr.add(2);
293                    dst_ptr = dst_ptr.add(2);
294                }
295            }
296
297            if src_ptr as usize + 4 <= src_end as usize && src_ptr as usize % 8 > 0 {
298                // SAFETY: Pointers are verified to be within the buffer bounds.
299                unsafe {
300                    let value: u32 = arch::load_u32(src_ptr as *const u32);
301                    std::ptr::write_unaligned(dst_ptr as *mut u32, value);
302                    src_ptr = src_ptr.add(4);
303                    dst_ptr = dst_ptr.add(4);
304                }
305            }
306        }
307
308        while src_ptr as usize + 8 <= src_end as usize {
309            // SAFETY: Pointers are verified to be within the buffer bounds.
310            unsafe {
311                let value: u64 = arch::load_u64(src_ptr as *const u64);
312                std::ptr::write_unaligned(dst_ptr as *mut u64, value);
313                src_ptr = src_ptr.add(8);
314                dst_ptr = dst_ptr.add(8);
315            }
316        }
317
318        if src_ptr < src_end {
319            if src_ptr as usize + 4 <= src_end as usize {
320                // SAFETY: Pointers are verified to be within the buffer bounds.
321                unsafe {
322                    let value: u32 = arch::load_u32(src_ptr as *const u32);
323                    std::ptr::write_unaligned(dst_ptr as *mut u32, value);
324                    src_ptr = src_ptr.add(4);
325                    dst_ptr = dst_ptr.add(4);
326                }
327            }
328
329            if src_ptr as usize + 2 <= src_end as usize {
330                // SAFETY: Pointers are verified to be within the buffer bounds.
331                unsafe {
332                    let value: u16 = arch::load_u16(src_ptr as *const u16);
333                    std::ptr::write_unaligned(dst_ptr as *mut u16, value);
334                    src_ptr = src_ptr.add(2);
335                    dst_ptr = dst_ptr.add(2);
336                }
337            }
338
339            if src_ptr < src_end {
340                // SAFETY: Pointers are verified to be within the buffer bounds.
341                unsafe {
342                    let value: u8 = arch::load_u8(src_ptr as *const u8);
343                    std::ptr::write_unaligned(dst_ptr, value);
344                    src_ptr = src_ptr.add(1);
345                    dst_ptr = dst_ptr.add(1);
346                }
347            }
348        }
349
350        debug_assert_eq!(src_ptr, src_end);
351        debug_assert_eq!(dst_ptr, dst_end);
352    }
353
354    /// Loads all buffer contents into a `SmallVec`.
355    pub fn load<const N: usize>(&self) -> SmallVec<[u8; N]> {
356        if self.size <= N {
357            let mut buf = MaybeUninit::<[u8; N]>::uninit();
358            self.load_to_slice(&mut AsMut::<[MaybeUninit<u8>]>::as_mut(&mut buf)[..self.size]);
359            // SAFETY: load() fills the buffer.
360            unsafe { SmallVec::from_buf_and_len_unchecked(buf, self.size) }
361        } else {
362            let mut vec = Vec::<u8>::with_capacity(self.size);
363            self.load_to_slice(vec.spare_capacity_mut());
364            // SAFETY: load() fills the buffer.
365            unsafe { vec.set_len(self.size) };
366            SmallVec::from_vec(vec)
367        }
368    }
369
370    /// Stores `data` in the buffer. `data` must not be larger than the buffer.
371    pub fn store(&self, data: &[u8]) {
372        assert!(data.len() <= self.size);
373
374        let mut ptr = self.ptr;
375        // SAFETY: `len` is validated above to be within bounds.
376        let end = unsafe { ptr.add(data.len()) };
377        let mut data_offset = 0;
378
379        // Write the head of the buffer with u8, u16, u32 stores.
380        if ptr as usize % 8 > 0 {
381            if ptr < end && ptr as usize % 2 > 0 {
382                let value = data[data_offset];
383                data_offset += 1;
384                // SAFETY: Pointers are verified to be within the buffer bounds.
385                unsafe {
386                    arch::store_u8(ptr, value);
387                    ptr = ptr.add(1)
388                };
389            }
390
391            if (ptr as usize) + 2 <= end as usize && ptr as usize % 4 > 0 {
392                let value = u16::read_from_bytes(&data[data_offset..(data_offset + 2)]).unwrap();
393                data_offset += 2;
394                // SAFETY: Pointers are verified to be within the buffer bounds.
395                unsafe {
396                    arch::store_u16(ptr as *mut u16, value);
397                    ptr = ptr.add(2)
398                };
399            }
400
401            if (ptr as usize) + 4 <= end as usize && ptr as usize % 8 > 0 {
402                let value = u32::read_from_bytes(&data[data_offset..(data_offset + 4)]).unwrap();
403                data_offset += 4;
404                // SAFETY: Pointers are verified to be within the buffer bounds.
405                unsafe {
406                    arch::store_u32(ptr as *mut u32, value);
407                    ptr = ptr.add(4)
408                };
409            }
410        }
411
412        // Write the body of the buffer with u64 stores.
413        while (ptr as usize) + 8 <= end as usize {
414            let value = u64::read_from_bytes(&data[data_offset..(data_offset + 8)]).unwrap();
415            data_offset += 8;
416            // SAFETY: Pointers are verified to be within the buffer bounds.
417            unsafe {
418                arch::store_u64(ptr as *mut u64, value);
419                ptr = ptr.add(8)
420            };
421        }
422
423        // Write the tail of the buffer with u32, u16, u8 stores.
424        if ptr < end {
425            if (ptr as usize) + 4 <= end as usize {
426                let value = u32::read_from_bytes(&data[data_offset..(data_offset + 4)]).unwrap();
427                data_offset += 4;
428                // SAFETY: Pointers are verified to be within the buffer bounds.
429                unsafe {
430                    arch::store_u32(ptr as *mut u32, value);
431                    ptr = ptr.add(4)
432                };
433            }
434
435            if (ptr as usize) + 2 <= end as usize {
436                let value = u16::read_from_bytes(&data[data_offset..(data_offset + 2)]).unwrap();
437                data_offset += 2;
438                // SAFETY: Pointers are verified to be within the buffer bounds.
439                unsafe {
440                    arch::store_u16(ptr as *mut u16, value);
441                    ptr = ptr.add(2)
442                };
443            }
444
445            if ptr < end {
446                let value = data[data_offset];
447                data_offset += 1;
448                // SAFETY: Pointers are verified to be within the buffer bounds.
449                unsafe {
450                    arch::store_u8(ptr, value);
451                    ptr = ptr.add(1)
452                };
453            }
454        }
455
456        debug_assert_eq!(ptr, end);
457        debug_assert_eq!(data_offset, data.len());
458    }
459
460    /// Copies the data from another `EbpfBufferPtr`. `src` may be smaller than
461    /// `self`. In this case it's copied to the beginning of `self`.
462    pub fn copy(&self, src: &EbpfBufferPtr<'_>) {
463        assert!(src.len() <= self.size);
464
465        let mut dst_ptr = self.ptr;
466        // SAFETY: src.len() <= self.size is asserted above.
467        let dst_end = unsafe { dst_ptr.add(src.len()) };
468
469        let mut src_ptr = src.ptr;
470        // SAFETY: Calculate ptr to the end of the source buffer.
471        let src_end = unsafe { src_ptr.add(src.len()) };
472
473        // Fast path when both buffers are 8-byte aligned.
474        if (src_ptr as usize) % 8 == 0 && (dst_ptr as usize) % 8 == 0 {
475            while (src_ptr as usize) + 8 <= src_end as usize {
476                // SAFETY: Pointers are verified to be within the bounds of valid buffers.
477                unsafe {
478                    let value: u64 = arch::load_u64(src_ptr as *const u64);
479                    arch::store_u64(dst_ptr as *mut u64, value);
480                    src_ptr = src_ptr.add(8);
481                    dst_ptr = dst_ptr.add(8);
482                }
483            }
484
485            if src_ptr < src_end {
486                if (src_ptr as usize) + 4 <= src_end as usize {
487                    // SAFETY: Pointers are verified to be within the bounds of valid buffers.
488                    unsafe {
489                        let value: u32 = arch::load_u32(src_ptr as *const u32);
490                        arch::store_u32(dst_ptr as *mut u32, value);
491                        src_ptr = src_ptr.add(4);
492                        dst_ptr = dst_ptr.add(4);
493                    }
494                }
495
496                if (src_ptr as usize) + 2 <= src_end as usize {
497                    // SAFETY: Pointers are verified to be within the bounds of valid buffers.
498                    unsafe {
499                        let value: u16 = arch::load_u16(src_ptr as *const u16);
500                        arch::store_u16(dst_ptr as *mut u16, value);
501                        src_ptr = src_ptr.add(2);
502                        dst_ptr = dst_ptr.add(2);
503                    }
504                }
505
506                if src_ptr < src_end {
507                    // SAFETY: Pointers are verified to be within the bounds of valid buffers.
508                    unsafe {
509                        let value: u8 = arch::load_u8(src_ptr as *const u8);
510                        arch::store_u8(dst_ptr as *mut u8, value);
511                        src_ptr = src_ptr.add(1);
512                        dst_ptr = dst_ptr.add(1);
513                    }
514                }
515            }
516
517            debug_assert_eq!(src_ptr, src_end);
518            debug_assert_eq!(dst_ptr, dst_end);
519        } else {
520            // Slow path fallback for unaligned buffers: Load the source values
521            // into a temporary buffer and then store them into the destination
522            // buffer.
523            self.store(&src.load::<128>());
524        }
525    }
526}
527
528impl<'a> From<&'a mut [u8]> for EbpfBufferPtr<'a> {
529    fn from(value: &'a mut [u8]) -> Self {
530        let ptr = value.as_mut_ptr() as *mut u8;
531        // SAFETY: We borrow a mutable reference to the slice. This guarantees
532        // that the returned pointer is valid for the lifetime 'a and there are
533        // no other mutable references.
534        unsafe { Self::new(ptr, value.len()) }
535    }
536}
537
538impl<'a> From<&'a mut Vec<u8>> for EbpfBufferPtr<'a> {
539    fn from(value: &'a mut Vec<u8>) -> Self {
540        let ptr = value.as_mut_ptr() as *mut u8;
541        // SAFETY: We borrow a mutable reference to the slice. This guarantees
542        // that the returned pointer is valid for the lifetime 'a and there are
543        // no other mutable references.
544        unsafe { Self::new(ptr, value.len()) }
545    }
546}
547impl<'a, const N: usize> From<&'a mut [u8; N]> for EbpfBufferPtr<'a> {
548    fn from(value: &'a mut [u8; N]) -> Self {
549        let ptr = value.as_mut_ptr() as *mut u8;
550        // SAFETY: We borrow a mutable reference to the array. This guarantees
551        // that the returned pointer is valid for the lifetime 'a and there are
552        // no other mutable references.
553        unsafe { Self::new(ptr, N) }
554    }
555}
556
557#[cfg(test)]
558mod test {
559    use super::*;
560    use fuchsia_runtime::vmar_root_self;
561    use std::sync::Barrier;
562    use std::sync::atomic::{AtomicU32, Ordering};
563    use std::thread;
564
565    #[test]
566    fn test_u64_atomicity() {
567        let vmo_size = zx::system_get_page_size() as usize;
568        let vmo = zx::Vmo::create(vmo_size as u64).unwrap();
569        let addr = vmar_root_self()
570            .map(0, &vmo, 0, vmo_size, zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE)
571            .unwrap();
572        #[allow(
573            clippy::undocumented_unsafe_blocks,
574            reason = "Force documented unsafe blocks in Starnix"
575        )]
576        let shared_ptr = unsafe { EbpfPtr::new(addr as *mut u64) };
577
578        const NUM_THREADS: usize = 10;
579
580        // Barrier used to synchronize start of the threads.
581        let barrier = Barrier::new(NUM_THREADS * 2);
582
583        let finished_writers = AtomicU32::new(0);
584
585        thread::scope(|scope| {
586            let mut threads = Vec::new();
587
588            for _ in 0..10 {
589                threads.push(scope.spawn(|| {
590                    barrier.wait();
591                    for _ in 0..1000 {
592                        for i in 0..255 {
593                            // Store a value with the same value repeated in every byte.
594                            let v = i << 8 | i;
595                            let v = v << 16 | v;
596                            let v = v << 32 | v;
597                            shared_ptr.store_relaxed(v);
598                        }
599                    }
600                    finished_writers.fetch_add(1, Ordering::Relaxed);
601                }));
602
603                threads.push(scope.spawn(|| {
604                    barrier.wait();
605                    loop {
606                        for _ in 0..1000 {
607                            let v = shared_ptr.load_relaxed();
608                            // Verify that all bytes in `v` are set to the same.
609                            assert!(v >> 32 == v & 0xffff_ffff);
610                            assert!((v >> 16) & 0xffff == v & 0xffff);
611                            assert!((v >> 8) & 0xff == v & 0xff);
612                        }
613                        if finished_writers.load(Ordering::Relaxed) == NUM_THREADS as u32 {
614                            break;
615                        }
616                    }
617                }));
618            }
619
620            for t in threads.into_iter() {
621                t.join().expect("failed to join a test thread");
622            }
623        });
624
625        #[allow(
626            clippy::undocumented_unsafe_blocks,
627            reason = "Force documented unsafe blocks in Starnix"
628        )]
629        unsafe {
630            vmar_root_self().unmap(addr, vmo_size).unwrap()
631        };
632    }
633
634    #[test]
635    fn test_buffer_slice() {
636        const SIZE: usize = 32;
637
638        let mut buf = [0; SIZE];
639        #[allow(
640            clippy::undocumented_unsafe_blocks,
641            reason = "Force documented unsafe blocks in Starnix"
642        )]
643        let buf_ptr = unsafe { EbpfBufferPtr::new(buf.as_mut_ptr(), SIZE) };
644
645        buf_ptr.slice(8..16).unwrap().store(&[1, 2, 3, 4, 5, 6, 7, 8]);
646        let value = buf_ptr.slice(0..24).unwrap().load::<16>();
647        assert_eq!(
648            &value[..],
649            &[0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0]
650        );
651
652        assert!(buf_ptr.slice(8..40).is_none());
653    }
654
655    #[test]
656    fn test_buffer_load() {
657        const FULL_SIZE: usize = 40;
658        let mut buf = (0..(FULL_SIZE as u8)).map(|v| v as u8).collect::<Vec<_>>();
659        // SAFETY: Creating EbpfBufferPtr for the buffer allocated above.
660        let buf_ptr = unsafe { EbpfBufferPtr::new(buf.as_mut_ptr(), FULL_SIZE) };
661
662        for start in 0..FULL_SIZE {
663            for end in start..=FULL_SIZE {
664                let slice = buf_ptr.slice(start..end).unwrap();
665                let loaded = slice.load::<16>();
666
667                let expected = (start..end).map(|v| v as u8).collect::<Vec<_>>();
668                assert_eq!(&loaded[..], &expected[..], "failed for range {}..{}", start, end);
669            }
670        }
671    }
672
673    #[test]
674    fn test_buffer_store() {
675        const FULL_SIZE: usize = 40;
676        let mut buf = [0u8; FULL_SIZE];
677        // SAFETY: Creating EbpfBufferPtr for the buffer allocated above.
678        let buf_ptr = unsafe { EbpfBufferPtr::new(buf.as_mut_ptr(), FULL_SIZE) };
679
680        for start in 0..FULL_SIZE {
681            for end in start..=FULL_SIZE {
682                let slice = buf_ptr.slice(start..end).unwrap();
683                let data_to_store = (start..end).map(|v| v as u8).collect::<Vec<_>>();
684                slice.store(&data_to_store);
685
686                let loaded = slice.load::<16>();
687                assert_eq!(&loaded[..], &data_to_store[..], "failed for range {}..{}", start, end);
688            }
689        }
690    }
691
692    #[test]
693    fn test_buffer_copy() {
694        const BASE_SIZE: usize = 48;
695        let mut src_buf = (0..(BASE_SIZE as u8)).map(|v| v as u8).collect::<Vec<_>>();
696        let mut dst_buf = [0u8; BASE_SIZE];
697
698        // SAFETY: Creating EbpfBufferPtr for the buffer allocated above.
699        let src_base = unsafe { EbpfBufferPtr::new(src_buf.as_mut_ptr(), BASE_SIZE) };
700
701        // SAFETY: Creating EbpfBufferPtr for the buffer allocated above.
702        let dst_base = unsafe { EbpfBufferPtr::new(dst_buf.as_mut_ptr(), BASE_SIZE) };
703
704        for src_align in 0..8 {
705            for dst_align in 0..8 {
706                for len in 0..=32 {
707                    dst_buf.fill(0);
708
709                    let src_slice = src_base.slice(src_align..(src_align + len)).unwrap();
710                    let dst_slice = dst_base.slice(dst_align..(dst_align + len)).unwrap();
711
712                    dst_slice.copy(&src_slice);
713
714                    let loaded = dst_slice.load::<16>();
715                    let expected =
716                        (src_align..(src_align + len)).map(|v| v as u8).collect::<Vec<_>>();
717                    assert_eq!(
718                        &loaded[..],
719                        &expected[..],
720                        "copy failed for length {} with src align {} and dst align {}",
721                        len,
722                        src_align,
723                        dst_align
724                    );
725
726                    for i in 0..BASE_SIZE {
727                        if i < dst_align || i >= dst_align + len {
728                            assert_eq!(
729                                dst_buf[i], 0,
730                                "out-of-bounds memory modified at index {} (dst_align={}, len={})",
731                                i, dst_align, len
732                            );
733                        }
734                    }
735                }
736            }
737        }
738    }
739}