فهرست منبع

feat: Reduce dependency on libc

I am attempting to reduce the dependence on libc in my indirect
dependencies. While looking at this crate I noticed some low-hanging
fruit that can be very easily replaced with libc.

- The rand() function can be re-implemented as a simple Wyrand seeded by
  the thread ID and the current time.
- libc::memcpy is used at a point where it can be replaced with
  ptr::copy_nonoverlapping.
- libc is used for memory allocation at a point where std::alloc can be
  used instead. So I use this.

I'm not too familiar with this codebase, so if any of these replacements
are incorrect, please let me know.

The only leftover libc call is mprotect().

Signed-off-by: John Nunley <dev@notgull.net>
John Nunley 9 ماه پیش
والد
کامیت
404ccd0916
2فایلهای تغییر یافته به همراه55 افزوده شده و 37 حذف شده
  1. 29 23
      src/helpers.rs
  2. 26 14
      src/jit.rs

+ 29 - 23
src/helpers.rs

@@ -16,9 +16,6 @@
 //! value. Hence some helpers have unused arguments, or return a 0 value in all cases, in order to
 //! respect this convention.
 
-#[cfg(feature = "std")]
-extern crate libc;
-
 use crate::lib::*;
 
 // Helpers associated to kernel helpers
@@ -243,30 +240,39 @@ pub fn strcmp (arg1: u64, arg2: u64, arg3: u64, unused4: u64, unused5: u64) -> u
 /// Returns a random u64 value comprised between `min` and `max` values (inclusive). Arguments 3 to
 /// 5 are unused.
 ///
-/// Relies on `rand()` function from libc, so `libc::srand()` should be called once before this
-/// helper is used.
-///
-/// # Examples
-///
-/// ```
-/// extern crate libc;
-/// extern crate rbpf;
-/// extern crate time;
-///
-/// unsafe {
-///     libc::srand(time::precise_time_ns() as u32)
-/// }
-///
-/// let n = rbpf::helpers::rand(3, 6, 0, 0, 0);
-/// assert!(3 <= n && n <= 6);
-/// ```
+/// This does not rely on `libc::rand()` and therefore can be called without `libc::srand()`.
 #[allow(dead_code)]
 #[allow(unused_variables)]
 #[cfg(feature = "std")]
 pub fn rand (min: u64, max: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 {
-    let mut n = unsafe {
-        (libc::rand() as u64).wrapping_shl(32) + libc::rand() as u64
-    };
+    use std::cell::Cell;
+    use std::collections::hash_map::DefaultHasher;
+    use std::hash::{Hash, Hasher};
+    use std::thread;
+    use std::time::Instant;
+
+    // Constants for WyRand taken from: https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h#L151
+    const WY_CONST_0: u64 = 0x2d35_8dcc_aa6c_78a5;
+    const WY_CONST_1: u64 = 0x8bb8_4b93_962e_acc9;
+
+    std::thread_local! {
+        static RNG: Cell<u64> = {
+            // Seed the RNG with the thread ID and the current time.
+            let mut hasher = DefaultHasher::new();
+            Instant::now().hash(&mut hasher);
+            thread::current().id().hash(&mut hasher);
+            Cell::new(hasher.finish())
+        };
+    }
+
+    // Run one round of WyRand.
+    let mut n = RNG.with(|rng| {
+        let s = rng.get().wrapping_add(WY_CONST_0);
+        rng.set(s);
+        let t = u128::from(s) * u128::from(s ^ WY_CONST_1);
+        (t as u64) ^ (t >> 64) as u64
+    });
+
     if min < max {
         n = n % (max + 1 - min) + min;
     };

+ 26 - 14
src/jit.rs

@@ -5,12 +5,14 @@
 // Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
 //      (Translation to Rust, MetaBuff addition)
 
+use std::alloc;
 use std::mem;
 use std::collections::HashMap;
 use std::fmt::Formatter;
 use std::fmt::Error as FormatterError;
 use std::io::{Error, ErrorKind};
 use std::ops::{Index, IndexMut};
+use std::ptr;
 
 use ebpf;
 
@@ -931,10 +933,8 @@ impl JitCompiler {
                 let offset_loc = jump.offset_loc as i32 + std::mem::size_of::<i32>() as i32;
                 let rel = &(target_loc as i32 - offset_loc) as *const i32;
 
-                let offset_ptr = mem.contents.as_ptr().add(jump.offset_loc);
-
-                libc::memcpy(offset_ptr as *mut libc::c_void, rel as *const libc::c_void,
-                             std::mem::size_of::<i32>());
+                let offset_ptr = mem.contents.as_ptr().add(jump.offset_loc) as *mut u8;
+                ptr::copy_nonoverlapping(rel.cast::<u8>(), offset_ptr, std::mem::size_of::<i32>());
             }
         }
         Ok(())
@@ -943,25 +943,37 @@ impl JitCompiler {
 
 pub struct JitMemory<'a> {
     contents: &'a mut [u8],
+    layout: alloc::Layout,
     offset:   usize,
 }
 
 impl<'a> JitMemory<'a> {
     pub fn new(prog: &[u8], helpers: &HashMap<u32, ebpf::Helper>, use_mbuff: bool,
                update_data_ptr: bool) -> Result<JitMemory<'a>, Error> {
-        let contents: &mut[u8];
-        let mut raw: mem::MaybeUninit<*mut libc::c_void> = mem::MaybeUninit::uninit();
-        unsafe {
+        let layout;
+
+        // Allocate the appropriately sized memory.
+        let contents = unsafe {
+            // Create a layout with the proper size and alignment.
             let size = NUM_PAGES * PAGE_SIZE;
-            libc::posix_memalign(raw.as_mut_ptr(), PAGE_SIZE, size);
-            libc::mprotect(*raw.as_mut_ptr(), size, libc::PROT_EXEC | libc::PROT_READ | libc::PROT_WRITE);
-            std::ptr::write_bytes(*raw.as_mut_ptr(), 0xc3, size);  // for now, prepopulate with 'RET' calls
-            contents = std::slice::from_raw_parts_mut(*raw.as_mut_ptr() as *mut u8, NUM_PAGES * PAGE_SIZE);
-            raw.assume_init();
-        }
+            layout = alloc::Layout::from_size_align_unchecked(size, PAGE_SIZE);
+
+            // Allocate the region of memory.
+            let ptr = alloc::alloc(layout);
+            if ptr.is_null() {
+                return Err(Error::from(std::io::ErrorKind::OutOfMemory));
+            }
+
+            // Protect it.
+            libc::mprotect(ptr.cast(), size, libc::PROT_EXEC | libc::PROT_WRITE);
+
+            // Convert to a slice.
+            std::slice::from_raw_parts_mut(ptr, size)
+        };
 
         let mut mem = JitMemory {
             contents,
+            layout,
             offset: 0,
         };
 
@@ -994,7 +1006,7 @@ impl<'a> IndexMut<usize> for JitMemory<'a> {
 impl<'a> Drop for JitMemory<'a> {
     fn drop(&mut self) {
         unsafe {
-            libc::free(self.contents.as_mut_ptr() as *mut libc::c_void);
+            alloc::dealloc(self.contents.as_mut_ptr(), self.layout);
         }
     }
 }