make read() safer and add copy_slice_to_ptr()
This commit is contained in:
parent
f3a453a88e
commit
76354f144a
181
src/lib.rs
181
src/lib.rs
|
@ -1,21 +1,19 @@
|
|||
use std::env;
|
||||
use std::mem;
|
||||
use std::ffi::CStr;
|
||||
#![allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
|
||||
use libc::{
|
||||
c_int,
|
||||
c_uchar,
|
||||
c_uint,
|
||||
c_void,
|
||||
dlsym,
|
||||
getrandom as libc_getrandom,
|
||||
RTLD_NEXT,
|
||||
ssize_t
|
||||
c_int, c_uchar, c_uint, c_void, dlsym, getrandom as libc_getrandom, ssize_t, RTLD_NEXT,
|
||||
};
|
||||
use std::env;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::mem;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
fn fakerand_value() -> Option<i32> {
|
||||
env::var("FAKERAND").ok().and_then(|val| val.parse().ok())
|
||||
static FAKERAND: OnceLock<Option<i32>> = OnceLock::new();
|
||||
*FAKERAND.get_or_init(|| env::var("FAKERAND").ok().and_then(|v| v.parse().ok()))
|
||||
}
|
||||
|
||||
/*
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rand() -> c_int {
|
||||
println!("rand() called");
|
||||
|
@ -29,52 +27,113 @@ pub extern "C" fn rand() -> c_int {
|
|||
|
||||
if !symbol.is_null() {
|
||||
let original_rand: extern "C" fn() -> c_int = std::mem::transmute(symbol);
|
||||
return original_rand();
|
||||
original_rand()
|
||||
} else {
|
||||
eprintln!("Error: original rand() not found");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn read(fd: c_int, buf: *mut c_void, count: usize) -> ssize_t {
|
||||
static mut ORIGINAL_READ: Option<extern "C" fn(c_int, *mut c_void, usize) -> ssize_t> = None;
|
||||
|
||||
unsafe {
|
||||
if ORIGINAL_READ.is_none() {
|
||||
let symbol = dlsym(RTLD_NEXT, "read\0".as_ptr() as *const _);
|
||||
if symbol.is_null() {
|
||||
eprintln!("Error: original read() not found");
|
||||
std::process::exit(1);
|
||||
}
|
||||
ORIGINAL_READ = Some(std::mem::transmute(symbol));
|
||||
#[inline(always)]
|
||||
unsafe fn copy_slice_to_ptr(slice: &[u8], buf: *mut c_void, buf_size: usize) {
|
||||
for offset in (0..buf_size).step_by(4) {
|
||||
unsafe {
|
||||
std::ptr::copy(
|
||||
slice.as_ptr() as *mut c_void,
|
||||
buf.byte_add(offset),
|
||||
std::cmp::min(buf_size - offset, slice.len()),
|
||||
)
|
||||
}
|
||||
|
||||
let original_read = ORIGINAL_READ.unwrap();
|
||||
|
||||
let path = format!("/proc/self/fd/{}", fd);
|
||||
let mut actualpath = [0u8; 1024];
|
||||
|
||||
let len = libc::readlink(
|
||||
path.as_ptr() as *const i8,
|
||||
actualpath.as_mut_ptr() as *mut i8,
|
||||
actualpath.len() as _
|
||||
);
|
||||
|
||||
if len != -1 {
|
||||
let actualpath = CStr::from_ptr(actualpath.as_ptr() as *const i8).to_str().unwrap();
|
||||
if (actualpath == "/dev/urandom" || actualpath == "/dev/random") && fakerand_value().is_some() {
|
||||
let fakerand = fakerand_value().unwrap();
|
||||
*(buf as *mut c_int) = fakerand;
|
||||
return std::mem::size_of::<c_int>() as ssize_t;
|
||||
}
|
||||
}
|
||||
|
||||
original_read(fd, buf, count)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function assumes the `read()` libc function is defined as
|
||||
/// `read(c_int, *mut c_void, usize) -> ssize_t` and calls it as such. This function also invokes
|
||||
/// `libc::readlink()` with two paths each of which not exceeding 4096 (PATH_MAX) size in length.
|
||||
///
|
||||
/// The function may panic if the path can't be encoded as a Rust `str` value.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn read(fd: c_int, buf: *mut c_void, count: usize) -> ssize_t {
|
||||
static ORIGINAL_READ: OnceLock<extern "C" fn(c_int, *mut c_void, usize) -> ssize_t> =
|
||||
OnceLock::new();
|
||||
|
||||
let original_read = ORIGINAL_READ.get_or_init(|| {
|
||||
let symbol = unsafe { dlsym(RTLD_NEXT, "read\0".as_ptr() as *const _) };
|
||||
if symbol.is_null() {
|
||||
eprintln!("Error: original read() not found");
|
||||
std::process::exit(1);
|
||||
}
|
||||
unsafe { std::mem::transmute(symbol) }
|
||||
});
|
||||
|
||||
let path = format!("/proc/self/fd/{}", fd);
|
||||
// PATH_MAX is defined as 4096 chars in a path including nul
|
||||
let mut actualpath = [0u8; 4096];
|
||||
|
||||
let len = unsafe {
|
||||
libc::readlink(
|
||||
path.as_ptr() as *const i8,
|
||||
actualpath.as_mut_ptr() as *mut i8,
|
||||
actualpath.len() as _,
|
||||
)
|
||||
};
|
||||
|
||||
if len != -1 {
|
||||
let actualpath = unsafe { CStr::from_ptr(actualpath.as_ptr() as *const i8) };
|
||||
let urandom = CString::new("/dev/urandom").unwrap();
|
||||
let random = CString::new("/dev/random").unwrap();
|
||||
if actualpath == urandom.as_c_str() || actualpath == random.as_c_str() {
|
||||
let random_value = match fakerand_value() {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
eprintln!("fakerand loaded but unable to generate fake rand");
|
||||
unsafe {
|
||||
let errno = libc::__errno_location();
|
||||
*errno = libc::EINVAL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
copy_slice_to_ptr(&random_value.to_ne_bytes(), buf, count);
|
||||
}
|
||||
/*
|
||||
let value_ptr: *const i32 = &random_value;
|
||||
for offset in (0..count).step_by(4) {
|
||||
unsafe {
|
||||
std::ptr::copy(
|
||||
value_ptr as *mut c_void,
|
||||
buf.byte_add(offset),
|
||||
std::cmp::min(count - offset, 4),
|
||||
)
|
||||
};
|
||||
}
|
||||
*/
|
||||
return count.try_into().expect("size larger than SSIZE_MAX");
|
||||
}
|
||||
/*
|
||||
if (actualpath == "/dev/urandom" || actualpath == "/dev/random")
|
||||
&& fakerand_value().is_some()
|
||||
{
|
||||
let fakerand = fakerand_value().unwrap();
|
||||
*(buf as *mut c_int) = fakerand;
|
||||
return std::mem::size_of::<c_int>() as ssize_t;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
original_read(fd, buf, count)
|
||||
}
|
||||
|
||||
/*
|
||||
/// # Safety
|
||||
///
|
||||
/// Checked by caller. This function may result in undefined behavior if buflen is longer than the
|
||||
/// allocated length of buf or if buf is not allocated, or previously freed, memory.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn getrandom(buf: *mut c_void, buflen: usize, flags: c_uint) -> ssize_t {
|
||||
if let Some(fakerand) = fakerand_value() {
|
||||
|
@ -84,11 +143,13 @@ pub extern "C" fn getrandom(buf: *mut c_void, buflen: usize, flags: c_uint) -> s
|
|||
return std::mem::size_of::<c_int>() as ssize_t;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
libc_getrandom(buf, buflen, flags)
|
||||
}
|
||||
unsafe { libc_getrandom(buf, buflen, flags) }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Checked by caller. This function may result in undefined behavior if num is longer than the
|
||||
/// allocated length of buf or if buf is not allocated, or previously freed, memory.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn RAND_bytes(buf: *mut c_uchar, num: c_int) -> c_int {
|
||||
if let Some(fakerand) = fakerand_value() {
|
||||
|
@ -103,14 +164,16 @@ pub extern "C" fn RAND_bytes(buf: *mut c_uchar, num: c_int) -> c_int {
|
|||
unsafe {
|
||||
let symbol = dlsym(RTLD_NEXT, "RAND_bytes\0".as_ptr() as *const _);
|
||||
if !symbol.is_null() {
|
||||
let original_rand_bytes: extern "C" fn(*mut c_uchar, c_int) -> c_int = mem::transmute(symbol);
|
||||
return original_rand_bytes(buf, num);
|
||||
let original_rand_bytes: extern "C" fn(*mut c_uchar, c_int) -> c_int =
|
||||
mem::transmute(symbol);
|
||||
original_rand_bytes(buf, num)
|
||||
} else {
|
||||
eprintln!("Error: original RAND_bytes() not found");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -138,8 +201,11 @@ mod tests {
|
|||
#[test]
|
||||
fn test_read_with_fakerand() {
|
||||
env::set_var("FAKERAND", "42");
|
||||
|
||||
let file = OpenOptions::new().read(true).open("/dev/urandom").expect("Failed to open /dev/urandom");
|
||||
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open("/dev/urandom")
|
||||
.expect("Failed to open /dev/urandom");
|
||||
let fd = file.as_raw_fd();
|
||||
let mut buf = [0u8; 4];
|
||||
let result = read(fd, buf.as_mut_ptr() as *mut c_void, buf.len());
|
||||
|
@ -154,7 +220,10 @@ mod tests {
|
|||
fn test_read_without_fakerand() {
|
||||
env::remove_var("FAKERAND");
|
||||
|
||||
let file = OpenOptions::new().read(true).open("/dev/urandom").expect("Failed to open /dev/urandom");
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open("/dev/urandom")
|
||||
.expect("Failed to open /dev/urandom");
|
||||
let fd = file.as_raw_fd();
|
||||
let mut buf = [0u8; 4];
|
||||
let result = read(fd, buf.as_mut_ptr() as *mut c_void, buf.len());
|
||||
|
@ -175,4 +244,4 @@ mod tests {
|
|||
|
||||
env::remove_var("FAKERAND");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue