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;
|
#![allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||||
use std::mem;
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use libc::{
|
use libc::{
|
||||||
c_int,
|
c_int, c_uchar, c_uint, c_void, dlsym, getrandom as libc_getrandom, ssize_t, RTLD_NEXT,
|
||||||
c_uchar,
|
|
||||||
c_uint,
|
|
||||||
c_void,
|
|
||||||
dlsym,
|
|
||||||
getrandom as libc_getrandom,
|
|
||||||
RTLD_NEXT,
|
|
||||||
ssize_t
|
|
||||||
};
|
};
|
||||||
|
use std::env;
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
use std::mem;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
fn fakerand_value() -> Option<i32> {
|
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn rand() -> c_int {
|
pub extern "C" fn rand() -> c_int {
|
||||||
println!("rand() called");
|
println!("rand() called");
|
||||||
|
@ -29,52 +27,113 @@ pub extern "C" fn rand() -> c_int {
|
||||||
|
|
||||||
if !symbol.is_null() {
|
if !symbol.is_null() {
|
||||||
let original_rand: extern "C" fn() -> c_int = std::mem::transmute(symbol);
|
let original_rand: extern "C" fn() -> c_int = std::mem::transmute(symbol);
|
||||||
return original_rand();
|
original_rand()
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Error: original rand() not found");
|
eprintln!("Error: original rand() not found");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#[no_mangle]
|
#[inline(always)]
|
||||||
pub extern "C" fn read(fd: c_int, buf: *mut c_void, count: usize) -> ssize_t {
|
unsafe fn copy_slice_to_ptr(slice: &[u8], buf: *mut c_void, buf_size: usize) {
|
||||||
static mut ORIGINAL_READ: Option<extern "C" fn(c_int, *mut c_void, usize) -> ssize_t> = None;
|
for offset in (0..buf_size).step_by(4) {
|
||||||
|
unsafe {
|
||||||
unsafe {
|
std::ptr::copy(
|
||||||
if ORIGINAL_READ.is_none() {
|
slice.as_ptr() as *mut c_void,
|
||||||
let symbol = dlsym(RTLD_NEXT, "read\0".as_ptr() as *const _);
|
buf.byte_add(offset),
|
||||||
if symbol.is_null() {
|
std::cmp::min(buf_size - offset, slice.len()),
|
||||||
eprintln!("Error: original read() not found");
|
)
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
ORIGINAL_READ = Some(std::mem::transmute(symbol));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn getrandom(buf: *mut c_void, buflen: usize, flags: c_uint) -> ssize_t {
|
pub extern "C" fn getrandom(buf: *mut c_void, buflen: usize, flags: c_uint) -> ssize_t {
|
||||||
if let Some(fakerand) = fakerand_value() {
|
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;
|
return std::mem::size_of::<c_int>() as ssize_t;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe { libc_getrandom(buf, buflen, flags) }
|
||||||
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn RAND_bytes(buf: *mut c_uchar, num: c_int) -> c_int {
|
pub extern "C" fn RAND_bytes(buf: *mut c_uchar, num: c_int) -> c_int {
|
||||||
if let Some(fakerand) = fakerand_value() {
|
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 {
|
unsafe {
|
||||||
let symbol = dlsym(RTLD_NEXT, "RAND_bytes\0".as_ptr() as *const _);
|
let symbol = dlsym(RTLD_NEXT, "RAND_bytes\0".as_ptr() as *const _);
|
||||||
if !symbol.is_null() {
|
if !symbol.is_null() {
|
||||||
let original_rand_bytes: extern "C" fn(*mut c_uchar, c_int) -> c_int = mem::transmute(symbol);
|
let original_rand_bytes: extern "C" fn(*mut c_uchar, c_int) -> c_int =
|
||||||
return original_rand_bytes(buf, num);
|
mem::transmute(symbol);
|
||||||
|
original_rand_bytes(buf, num)
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Error: original RAND_bytes() not found");
|
eprintln!("Error: original RAND_bytes() not found");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -138,8 +201,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_with_fakerand() {
|
fn test_read_with_fakerand() {
|
||||||
env::set_var("FAKERAND", "42");
|
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 fd = file.as_raw_fd();
|
||||||
let mut buf = [0u8; 4];
|
let mut buf = [0u8; 4];
|
||||||
let result = read(fd, buf.as_mut_ptr() as *mut c_void, buf.len());
|
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() {
|
fn test_read_without_fakerand() {
|
||||||
env::remove_var("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 fd = file.as_raw_fd();
|
||||||
let mut buf = [0u8; 4];
|
let mut buf = [0u8; 4];
|
||||||
let result = read(fd, buf.as_mut_ptr() as *mut c_void, buf.len());
|
let result = read(fd, buf.as_mut_ptr() as *mut c_void, buf.len());
|
||||||
|
@ -175,4 +244,4 @@ mod tests {
|
||||||
|
|
||||||
env::remove_var("FAKERAND");
|
env::remove_var("FAKERAND");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue