Skip to content

Commit

Permalink
feat: Reduce dependency on libc
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
notgull authored and qmonnet committed Oct 29, 2024
1 parent cfb363c commit 404ccd0
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 37 deletions.
52 changes: 29 additions & 23 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
};
Expand Down
40 changes: 26 additions & 14 deletions src/jit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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(())
Expand All @@ -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,
};

Expand Down Expand Up @@ -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);
}
}
}
Expand Down

0 comments on commit 404ccd0

Please sign in to comment.