use alloc::{rc::Rc, string::ToString};
use core::{
cell::RefCell,
fmt::{self, Debug, Display},
mem::ManuallyDrop,
};
#[cfg(feature = "std")]
use std::env;
#[cfg(all(unix, feature = "std"))]
use std::io::Read;
#[cfg(feature = "std")]
use std::io::Write;
use serde::{Deserialize, Serialize};
#[cfg(all(feature = "std", unix, not(target_os = "android")))]
pub use unix_shmem::{MmapShMem, MmapShMemProvider};
#[cfg(all(feature = "std", unix))]
pub use unix_shmem::{UnixShMem, UnixShMemProvider};
#[cfg(all(windows, feature = "std"))]
pub use win32_shmem::{Win32ShMem, Win32ShMemProvider};
#[cfg(all(unix, feature = "std"))]
use crate::bolts::os::pipes::Pipe;
#[cfg(all(feature = "std", unix))]
pub use crate::bolts::os::unix_shmem_server::{ServedShMemProvider, ShMemService};
use crate::{
bolts::{AsMutSlice, AsSlice},
Error,
};
#[cfg(all(windows, feature = "std"))]
pub type StdShMemProvider = Win32ShMemProvider;
#[cfg(all(target_os = "android", feature = "std"))]
pub type StdShMemProvider =
RcShMemProvider<ServedShMemProvider<unix_shmem::ashmem::AshmemShMemProvider>>;
#[cfg(all(target_os = "android", feature = "std"))]
pub type StdShMemService = ShMemService<unix_shmem::ashmem::AshmemShMemProvider>;
#[cfg(all(feature = "std", target_vendor = "apple"))]
pub type StdShMemProvider = RcShMemProvider<ServedShMemProvider<MmapShMemProvider>>;
#[cfg(all(feature = "std", target_vendor = "apple"))]
pub type StdShMemService = ShMemService<MmapShMemProvider>;
#[cfg(all(
feature = "std",
unix,
not(any(target_os = "android", target_vendor = "apple"))
))]
pub type StdShMemProvider = UnixShMemProvider;
#[cfg(any(
not(any(target_os = "android", target_vendor = "apple")),
not(feature = "std")
))]
pub type StdShMemService = DummyShMemService;
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct ShMemDescription {
pub size: usize,
pub id: ShMemId,
}
impl ShMemDescription {
#[must_use]
pub fn from_string_and_size(id_str: &str, size: usize) -> Self {
Self {
size,
id: ShMemId::from_string(id_str),
}
}
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
pub struct ShMemId {
id: [u8; 20],
}
impl ShMemId {
#[must_use]
pub fn from_array(array: &[u8; 20]) -> Self {
Self { id: *array }
}
pub fn try_from_slice(slice: &[u8]) -> Result<Self, Error> {
Ok(Self::from_array(&slice[0..20].try_into()?))
}
#[must_use]
pub fn from_int(val: i32) -> Self {
Self::from_string(&val.to_string())
}
#[must_use]
pub fn from_string(val: &str) -> Self {
let mut slice: [u8; 20] = [0; 20];
for (i, val) in val.as_bytes().iter().enumerate() {
slice[i] = *val;
}
Self { id: slice }
}
#[must_use]
pub fn as_array(&self) -> &[u8; 20] {
&self.id
}
#[must_use]
pub fn null_pos(&self) -> usize {
self.id.iter().position(|&c| c == 0).unwrap()
}
#[must_use]
pub fn as_str(&self) -> &str {
alloc::str::from_utf8(&self.id[..self.null_pos()]).unwrap()
}
}
impl AsSlice for ShMemId {
type Entry = u8;
fn as_slice(&self) -> &[u8] {
&self.id
}
}
impl From<ShMemId> for i32 {
fn from(id: ShMemId) -> i32 {
id.as_str().parse().unwrap()
}
}
impl Display for ShMemId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
pub trait ShMem: Sized + Debug + Clone + AsSlice<Entry = u8> + AsMutSlice<Entry = u8> {
fn id(&self) -> ShMemId;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
unsafe fn as_object<T: Sized + 'static>(&self) -> &T {
assert!(self.len() >= core::mem::size_of::<T>());
(self.as_slice().as_ptr() as *const () as *const T)
.as_ref()
.unwrap()
}
unsafe fn as_object_mut<T: Sized + 'static>(&mut self) -> &mut T {
assert!(self.len() >= core::mem::size_of::<T>());
(self.as_mut_slice().as_mut_ptr() as *mut () as *mut T)
.as_mut()
.unwrap()
}
fn description(&self) -> ShMemDescription {
ShMemDescription {
size: self.len(),
id: self.id(),
}
}
#[cfg(feature = "std")]
fn write_to_env(&self, env_name: &str) -> Result<(), Error> {
let map_size = self.len();
let map_size_env = format!("{env_name}_SIZE");
env::set_var(env_name, self.id().to_string());
env::set_var(map_size_env, format!("{map_size}"));
Ok(())
}
}
pub trait ShMemProvider: Clone + Default + Debug {
type ShMem: ShMem;
fn new() -> Result<Self, Error>;
fn new_shmem(&mut self, map_size: usize) -> Result<Self::ShMem, Error>;
fn shmem_from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result<Self::ShMem, Error>;
fn new_shmem_object<T: Sized + 'static>(&mut self) -> Result<Self::ShMem, Error> {
self.new_shmem(core::mem::size_of::<T>())
}
fn shmem_object_from_id<T: Sized + 'static>(
&mut self,
id: ShMemId,
) -> Result<Self::ShMem, Error> {
self.shmem_from_id_and_size(id, core::mem::size_of::<T>())
}
fn shmem_from_description(
&mut self,
description: ShMemDescription,
) -> Result<Self::ShMem, Error> {
self.shmem_from_id_and_size(description.id, description.size)
}
fn clone_ref(&mut self, mapping: &Self::ShMem) -> Result<Self::ShMem, Error> {
self.shmem_from_id_and_size(mapping.id(), mapping.len())
}
#[cfg(feature = "std")]
fn existing_from_env(&mut self, env_name: &str) -> Result<Self::ShMem, Error> {
let map_shm_str = env::var(env_name)?;
let map_size = str::parse::<usize>(&env::var(format!("{env_name}_SIZE"))?)?;
self.shmem_from_description(ShMemDescription::from_string_and_size(
&map_shm_str,
map_size,
))
}
fn pre_fork(&mut self) -> Result<(), Error> {
Ok(())
}
fn post_fork(&mut self, _is_child: bool) -> Result<(), Error> {
Ok(())
}
fn release_shmem(&mut self, _shmem: &mut Self::ShMem) {
}
}
#[derive(Debug, Clone)]
pub struct RcShMem<T: ShMemProvider> {
internal: ManuallyDrop<T::ShMem>,
provider: Rc<RefCell<T>>,
}
impl<T> ShMem for RcShMem<T>
where
T: ShMemProvider + Debug,
{
fn id(&self) -> ShMemId {
self.internal.id()
}
fn len(&self) -> usize {
self.internal.len()
}
}
impl<T> AsSlice for RcShMem<T>
where
T: ShMemProvider + Debug,
{
type Entry = u8;
fn as_slice(&self) -> &[u8] {
self.internal.as_slice()
}
}
impl<T> AsMutSlice for RcShMem<T>
where
T: ShMemProvider + Debug,
{
type Entry = u8;
fn as_mut_slice(&mut self) -> &mut [u8] {
self.internal.as_mut_slice()
}
}
impl<T: ShMemProvider> Drop for RcShMem<T> {
fn drop(&mut self) {
self.provider.borrow_mut().release_shmem(&mut self.internal);
}
}
#[derive(Debug, Clone)]
#[cfg(all(unix, feature = "std"))]
pub struct RcShMemProvider<SP>
where
SP: ShMemProvider,
{
internal: Rc<RefCell<SP>>,
#[cfg(unix)]
child_parent_pipe: Option<Pipe>,
#[cfg(unix)]
parent_child_pipe: Option<Pipe>,
}
#[cfg(all(unix, feature = "std"))]
impl<SP> ShMemProvider for RcShMemProvider<SP>
where
SP: ShMemProvider + Debug,
{
type ShMem = RcShMem<SP>;
fn new() -> Result<Self, Error> {
Ok(Self {
internal: Rc::new(RefCell::new(SP::new()?)),
child_parent_pipe: None,
parent_child_pipe: None,
})
}
fn new_shmem(&mut self, map_size: usize) -> Result<Self::ShMem, Error> {
Ok(Self::ShMem {
internal: ManuallyDrop::new(self.internal.borrow_mut().new_shmem(map_size)?),
provider: self.internal.clone(),
})
}
fn shmem_from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result<Self::ShMem, Error> {
Ok(Self::ShMem {
internal: ManuallyDrop::new(
self.internal
.borrow_mut()
.shmem_from_id_and_size(id, size)?,
),
provider: self.internal.clone(),
})
}
fn release_shmem(&mut self, map: &mut Self::ShMem) {
self.internal.borrow_mut().release_shmem(&mut map.internal);
}
fn clone_ref(&mut self, mapping: &Self::ShMem) -> Result<Self::ShMem, Error> {
Ok(Self::ShMem {
internal: ManuallyDrop::new(self.internal.borrow_mut().clone_ref(&mapping.internal)?),
provider: self.internal.clone(),
})
}
fn pre_fork(&mut self) -> Result<(), Error> {
self.child_parent_pipe = Some(Pipe::new()?);
self.parent_child_pipe = Some(Pipe::new()?);
self.internal.borrow_mut().pre_fork()
}
fn post_fork(&mut self, is_child: bool) -> Result<(), Error> {
if is_child {
self.await_parent_done()?;
}
self.internal.borrow_mut().post_fork(is_child)?;
if is_child {
self.set_child_done()?;
} else {
self.set_parent_done()?;
self.await_child_done()?;
}
self.parent_child_pipe = None;
self.child_parent_pipe = None;
Ok(())
}
}
#[cfg(all(unix, feature = "std"))]
impl<SP> RcShMemProvider<SP>
where
SP: ShMemProvider,
{
fn pipe_set(pipe: &mut Option<Pipe>) -> Result<(), Error> {
match pipe {
Some(pipe) => {
let ok = [0_u8; 4];
pipe.write_all(&ok)?;
Ok(())
}
None => Err(Error::illegal_state(
"Unexpected `None` Pipe in RcShMemProvider! Missing post_fork()?".to_string(),
)),
}
}
fn pipe_await(pipe: &mut Option<Pipe>) -> Result<(), Error> {
match pipe {
Some(pipe) => {
let ok = [0_u8; 4];
let mut ret = ok;
pipe.read_exact(&mut ret)?;
if ret == ok {
Ok(())
} else {
Err(Error::unknown(format!(
"Wrong result read from pipe! Expected 0, got {ret:?}"
)))
}
}
None => Err(Error::illegal_state(
"Unexpected `None` Pipe in RcShMemProvider! Missing post_fork()?".to_string(),
)),
}
}
fn await_parent_done(&mut self) -> Result<(), Error> {
Self::pipe_await(&mut self.parent_child_pipe)
}
fn set_parent_done(&mut self) -> Result<(), Error> {
Self::pipe_set(&mut self.parent_child_pipe)
}
fn await_child_done(&mut self) -> Result<(), Error> {
Self::pipe_await(&mut self.child_parent_pipe)
}
fn set_child_done(&mut self) -> Result<(), Error> {
Self::pipe_set(&mut self.child_parent_pipe)
}
}
#[cfg(all(unix, feature = "std"))]
impl<SP> Default for RcShMemProvider<SP>
where
SP: ShMemProvider + Debug,
{
fn default() -> Self {
Self::new().unwrap()
}
}
#[cfg(all(unix, feature = "std"))]
pub mod unix_shmem {
#[cfg(target_os = "android")]
pub type UnixShMemProvider = ashmem::AshmemShMemProvider;
#[cfg(target_os = "android")]
pub type UnixShMem = ashmem::AshmemShMem;
#[cfg(not(target_os = "android"))]
pub type UnixShMemProvider = default::CommonUnixShMemProvider;
#[cfg(not(target_os = "android"))]
pub type UnixShMem = default::CommonUnixShMem;
#[cfg(not(target_os = "android"))]
pub use default::MmapShMem;
#[cfg(not(target_os = "android"))]
pub use default::MmapShMemProvider;
#[cfg(all(unix, feature = "std", not(target_os = "android")))]
mod default {
use alloc::string::ToString;
use core::{ptr, slice};
use std::{io::Write, process};
use libc::{
c_int, c_long, c_uchar, c_uint, c_ulong, c_ushort, close, ftruncate, mmap, munmap,
perror, shm_open, shm_unlink, shmat, shmctl, shmget,
};
use crate::{
bolts::{
rands::{Rand, RandomSeed, StdRand},
shmem::{ShMem, ShMemId, ShMemProvider},
AsMutSlice, AsSlice,
},
Error,
};
#[cfg(unix)]
#[derive(Copy, Clone)]
#[repr(C)]
struct ipc_perm {
pub __key: c_int,
pub uid: c_uint,
pub gid: c_uint,
pub cuid: c_uint,
pub cgid: c_uint,
pub mode: c_ushort,
pub __pad1: c_ushort,
pub __seq: c_ushort,
pub __pad2: c_ushort,
pub __glibc_reserved1: c_ulong,
pub __glibc_reserved2: c_ulong,
}
#[cfg(unix)]
#[derive(Copy, Clone)]
#[repr(C)]
struct shmid_ds {
pub shm_perm: ipc_perm,
pub shm_segsz: c_ulong,
pub shm_atime: c_long,
pub shm_dtime: c_long,
pub shm_ctime: c_long,
pub shm_cpid: c_int,
pub shm_lpid: c_int,
pub shm_nattch: c_ulong,
pub __glibc_reserved4: c_ulong,
pub __glibc_reserved5: c_ulong,
}
#[cfg(target_vendor = "apple")]
const MAX_MMAP_FILENAME_LEN: usize = 31;
#[cfg(not(target_vendor = "apple"))]
const MAX_MMAP_FILENAME_LEN: usize = 256;
#[derive(Clone, Debug)]
pub struct MmapShMem {
filename_path: Option<[u8; MAX_MMAP_FILENAME_LEN]>,
map_size: usize,
map: *mut u8,
id: ShMemId,
shm_fd: c_int,
}
impl MmapShMem {
pub fn new(map_size: usize, rand_id: u32) -> Result<Self, Error> {
unsafe {
let mut filename_path = [0_u8; MAX_MMAP_FILENAME_LEN];
write!(
&mut filename_path[..MAX_MMAP_FILENAME_LEN - 1],
"/libafl_{}_{}",
process::id(),
rand_id
)?;
let shm_fd = shm_open(
filename_path.as_ptr() as *const _,
libc::O_CREAT | libc::O_RDWR | libc::O_EXCL,
0o600,
);
if shm_fd == -1 {
perror(b"shm_open\0".as_ptr() as *const _);
return Err(Error::unknown(format!(
"Failed to shm_open map with id {filename_path:?}",
)));
}
if ftruncate(shm_fd, map_size.try_into()?) != 0 {
perror(b"ftruncate\0".as_ptr() as *const _);
shm_unlink(filename_path.as_ptr() as *const _);
return Err(Error::unknown(format!(
"setup_shm(): ftruncate() failed for map with id {filename_path:?}",
)));
}
let map = mmap(
ptr::null_mut(),
map_size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
shm_fd,
0,
);
if map == libc::MAP_FAILED || map.is_null() {
perror(b"mmap\0".as_ptr() as *const _);
close(shm_fd);
shm_unlink(filename_path.as_ptr() as *const _);
return Err(Error::unknown(format!(
"mmap() failed for map with id {filename_path:?}",
)));
}
Ok(Self {
filename_path: Some(filename_path),
map: map as *mut u8,
map_size,
shm_fd,
id: ShMemId::from_string(&format!("{shm_fd}")),
})
}
}
fn shmem_from_id_and_size(id: ShMemId, map_size: usize) -> Result<Self, Error> {
unsafe {
let shm_fd: i32 = id.to_string().parse().unwrap();
let map = mmap(
ptr::null_mut(),
map_size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
shm_fd,
0,
);
if map == libc::MAP_FAILED || map.is_null() {
perror(b"mmap\0".as_ptr() as *const _);
close(shm_fd);
return Err(Error::unknown(format!(
"mmap() failed for map with fd {shm_fd:?}"
)));
}
Ok(Self {
filename_path: None,
map: map as *mut u8,
map_size,
shm_fd,
id: ShMemId::from_string(&format!("{shm_fd}")),
})
}
}
#[must_use]
pub fn filename_path(&self) -> &Option<[u8; MAX_MMAP_FILENAME_LEN]> {
&self.filename_path
}
}
#[cfg(unix)]
#[derive(Clone, Debug)]
pub struct MmapShMemProvider {
rand: StdRand,
}
unsafe impl Send for MmapShMemProvider {}
#[cfg(unix)]
impl Default for MmapShMemProvider {
fn default() -> Self {
Self::new().unwrap()
}
}
#[cfg(unix)]
impl ShMemProvider for MmapShMemProvider {
type ShMem = MmapShMem;
fn new() -> Result<Self, Error> {
Ok(Self {
rand: StdRand::new(),
})
}
fn new_shmem(&mut self, map_size: usize) -> Result<Self::ShMem, Error> {
let id = self.rand.next() as u32;
MmapShMem::new(map_size, id)
}
fn shmem_from_id_and_size(
&mut self,
id: ShMemId,
size: usize,
) -> Result<Self::ShMem, Error> {
MmapShMem::shmem_from_id_and_size(id, size)
}
}
impl ShMem for MmapShMem {
fn id(&self) -> ShMemId {
self.id
}
fn len(&self) -> usize {
self.map_size
}
}
impl AsSlice for MmapShMem {
type Entry = u8;
fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.map, self.map_size) }
}
}
impl AsMutSlice for MmapShMem {
type Entry = u8;
fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.map, self.map_size) }
}
}
impl Drop for MmapShMem {
fn drop(&mut self) {
unsafe {
assert!(
!self.map.is_null(),
"Map should never be null for MmapShMem (on Drop)"
);
munmap(self.map as *mut _, self.map_size);
self.map = ptr::null_mut();
assert!(
self.shm_fd != -1,
"FD should never be -1 for MmapShMem (on Drop)"
);
if let Some(filename_path) = self.filename_path {
shm_unlink(filename_path.as_ptr() as *const _);
}
}
}
}
#[derive(Clone, Debug)]
pub struct CommonUnixShMem {
id: ShMemId,
map: *mut u8,
map_size: usize,
}
impl CommonUnixShMem {
#[allow(unused_qualifications)]
pub fn new(map_size: usize) -> Result<Self, Error> {
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
const SHM_R: libc::c_int = 0o400;
#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
const SHM_R: libc::c_int = libc::SHM_R;
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
const SHM_W: libc::c_int = 0o200;
#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
const SHM_W: libc::c_int = libc::SHM_W;
unsafe {
let os_id = shmget(
libc::IPC_PRIVATE,
map_size,
libc::IPC_CREAT | libc::IPC_EXCL | SHM_R | SHM_W,
);
if os_id < 0_i32 {
return Err(Error::unknown(format!("Failed to allocate a shared mapping of size {map_size} - check OS limits (i.e shmall, shmmax)")));
}
let map = shmat(os_id, ptr::null(), 0) as *mut c_uchar;
if map as c_int == -1 || map.is_null() {
shmctl(os_id, libc::IPC_RMID, ptr::null_mut());
return Err(Error::unknown(
"Failed to map the shared mapping".to_string(),
));
}
Ok(Self {
id: ShMemId::from_int(os_id),
map,
map_size,
})
}
}
pub fn shmem_from_id_and_size(id: ShMemId, map_size: usize) -> Result<Self, Error> {
unsafe {
let id_int: i32 = id.into();
let map = shmat(id_int, ptr::null(), 0) as *mut c_uchar;
if map.is_null() || map == ptr::null_mut::<c_uchar>().wrapping_sub(1) {
return Err(Error::unknown(
"Failed to map the shared mapping".to_string(),
));
}
Ok(Self { id, map, map_size })
}
}
}
#[cfg(unix)]
impl ShMem for CommonUnixShMem {
fn id(&self) -> ShMemId {
self.id
}
fn len(&self) -> usize {
self.map_size
}
}
impl AsSlice for CommonUnixShMem {
type Entry = u8;
fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.map, self.map_size) }
}
}
impl AsMutSlice for CommonUnixShMem {
type Entry = u8;
fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.map, self.map_size) }
}
}
#[cfg(unix)]
impl Drop for CommonUnixShMem {
fn drop(&mut self) {
unsafe {
let id_int: i32 = self.id.into();
shmctl(id_int, libc::IPC_RMID, ptr::null_mut());
}
}
}
#[cfg(unix)]
#[derive(Clone, Debug)]
pub struct CommonUnixShMemProvider {}
unsafe impl Send for CommonUnixShMemProvider {}
#[cfg(unix)]
impl Default for CommonUnixShMemProvider {
fn default() -> Self {
Self::new().unwrap()
}
}
#[cfg(unix)]
impl ShMemProvider for CommonUnixShMemProvider {
type ShMem = CommonUnixShMem;
fn new() -> Result<Self, Error> {
Ok(Self {})
}
fn new_shmem(&mut self, map_size: usize) -> Result<Self::ShMem, Error> {
CommonUnixShMem::new(map_size)
}
fn shmem_from_id_and_size(
&mut self,
id: ShMemId,
size: usize,
) -> Result<Self::ShMem, Error> {
CommonUnixShMem::shmem_from_id_and_size(id, size)
}
}
}
#[cfg(all(unix, feature = "std"))]
pub mod ashmem {
use alloc::string::ToString;
use core::{ptr, slice};
use std::ffi::CString;
use libc::{
c_uint, c_ulong, c_void, close, ioctl, mmap, open, MAP_SHARED, O_RDWR, PROT_READ,
PROT_WRITE,
};
use crate::{
bolts::{
shmem::{ShMem, ShMemId, ShMemProvider},
AsMutSlice, AsSlice,
},
Error,
};
#[cfg(unix)]
#[derive(Clone, Debug)]
pub struct AshmemShMem {
id: ShMemId,
map: *mut u8,
map_size: usize,
}
#[derive(Copy, Clone)]
#[repr(C)]
struct ashmem_pin {
pub offset: c_uint,
pub len: c_uint,
}
const ASHMEM_GET_SIZE: c_ulong = 0x00007704;
const ASHMEM_UNPIN: c_ulong = 0x40087708;
const ASHMEM_SET_SIZE: c_ulong = 0x40087703;
impl AshmemShMem {
pub fn new(map_size: usize) -> Result<Self, Error> {
unsafe {
let device_path = CString::new(
if let Ok(boot_id) =
std::fs::read_to_string("/proc/sys/kernel/random/boot_id")
{
let path_str = format!("/dev/ashmem{boot_id}").trim().to_string();
if std::path::Path::new(&path_str).exists() {
path_str
} else {
"/dev/ashmem".to_string()
}
} else {
"/dev/ashmem".to_string()
},
)
.unwrap();
let fd = open(device_path.as_ptr(), O_RDWR);
if fd == -1 {
return Err(Error::unknown(format!(
"Failed to open the ashmem device at {device_path:?}"
)));
}
#[allow(trivial_numeric_casts)]
if ioctl(fd, ASHMEM_SET_SIZE as _, map_size) != 0 {
close(fd);
return Err(Error::unknown(
"Failed to set the ashmem mapping's size".to_string(),
));
};
let map = mmap(
ptr::null_mut(),
map_size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0,
);
if map == usize::MAX as *mut c_void {
close(fd);
return Err(Error::unknown(
"Failed to map the ashmem mapping".to_string(),
));
}
Ok(Self {
id: ShMemId::from_string(&format!("{fd}")),
map: map as *mut u8,
map_size,
})
}
}
pub fn shmem_from_id_and_size(id: ShMemId, map_size: usize) -> Result<Self, Error> {
unsafe {
let fd: i32 = id.to_string().parse().unwrap();
#[allow(trivial_numeric_casts, clippy::cast_sign_loss)]
if ioctl(fd, ASHMEM_GET_SIZE as _) as u32 as usize != map_size {
return Err(Error::unknown(
"The mapping's size differs from the requested size".to_string(),
));
};
let map = mmap(
ptr::null_mut(),
map_size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0,
);
if map == usize::MAX as *mut c_void {
close(fd);
return Err(Error::unknown(
"Failed to map the ashmem mapping".to_string(),
));
}
Ok(Self {
id,
map: map as *mut u8,
map_size,
})
}
}
}
#[cfg(unix)]
impl ShMem for AshmemShMem {
fn id(&self) -> ShMemId {
self.id
}
fn len(&self) -> usize {
self.map_size
}
}
impl AsSlice for AshmemShMem {
type Entry = u8;
fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.map, self.map_size) }
}
}
impl AsMutSlice for AshmemShMem {
type Entry = u8;
fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.map, self.map_size) }
}
}
#[cfg(unix)]
impl Drop for AshmemShMem {
#[allow(trivial_numeric_casts)]
fn drop(&mut self) {
unsafe {
let fd: i32 = self.id.to_string().parse().unwrap();
#[allow(trivial_numeric_casts)]
#[allow(clippy::cast_sign_loss)]
let length = ioctl(fd, ASHMEM_GET_SIZE as _) as u32;
let ap = ashmem_pin {
offset: 0,
len: length,
};
ioctl(fd, ASHMEM_UNPIN as _, &ap);
close(fd);
}
}
}
#[cfg(unix)]
#[derive(Clone, Debug)]
pub struct AshmemShMemProvider {}
unsafe impl Send for AshmemShMemProvider {}
#[cfg(unix)]
impl Default for AshmemShMemProvider {
fn default() -> Self {
Self::new().unwrap()
}
}
#[cfg(unix)]
impl ShMemProvider for AshmemShMemProvider {
type ShMem = AshmemShMem;
fn new() -> Result<Self, Error> {
Ok(Self {})
}
fn new_shmem(&mut self, map_size: usize) -> Result<Self::ShMem, Error> {
let mapping = AshmemShMem::new(map_size)?;
Ok(mapping)
}
fn shmem_from_id_and_size(
&mut self,
id: ShMemId,
size: usize,
) -> Result<Self::ShMem, Error> {
AshmemShMem::shmem_from_id_and_size(id, size)
}
}
}
}
#[cfg(all(feature = "std", windows))]
pub mod win32_shmem {
use alloc::string::String;
use core::{
ffi::c_void,
fmt::{self, Debug, Formatter},
slice,
};
use uuid::Uuid;
use crate::{
bolts::{
shmem::{ShMem, ShMemId, ShMemProvider},
AsMutSlice, AsSlice,
},
Error,
};
const INVALID_HANDLE_VALUE: isize = -1;
use windows::{
core::PCSTR,
Win32::{
Foundation::{CloseHandle, BOOL, HANDLE},
System::Memory::{
CreateFileMappingA, MapViewOfFile, OpenFileMappingA, UnmapViewOfFile,
FILE_MAP_ALL_ACCESS, PAGE_READWRITE,
},
},
};
#[derive(Clone)]
pub struct Win32ShMem {
id: ShMemId,
handle: HANDLE,
map: *mut u8,
map_size: usize,
}
impl Debug for Win32ShMem {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Win32ShMem")
.field("id", &self.id)
.field("handle", &self.handle.0)
.field("map", &self.map)
.field("map_size", &self.map_size)
.finish()
}
}
impl Win32ShMem {
fn new_shmem(map_size: usize) -> Result<Self, Error> {
unsafe {
let uuid = Uuid::new_v4();
let mut map_str = format!("libafl_{}", uuid.simple());
let map_str_bytes = map_str.as_mut_vec();
map_str_bytes[19] = 0; let handle = CreateFileMappingA(
HANDLE(INVALID_HANDLE_VALUE),
None,
PAGE_READWRITE,
0,
map_size as u32,
PCSTR(map_str_bytes.as_mut_ptr()),
)?;
let map = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8;
if map.is_null() {
return Err(Error::unknown(format!(
"Cannot map shared memory {}",
String::from_utf8_lossy(map_str_bytes)
)));
}
Ok(Self {
id: ShMemId::try_from_slice(map_str_bytes).unwrap(),
handle,
map,
map_size,
})
}
}
fn shmem_from_id_and_size(id: ShMemId, map_size: usize) -> Result<Self, Error> {
unsafe {
let map_str_bytes = id.id;
let handle = OpenFileMappingA(
FILE_MAP_ALL_ACCESS.0,
BOOL(0),
PCSTR(map_str_bytes.as_ptr() as *mut _),
)?;
let map = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8;
if map.is_null() {
return Err(Error::unknown(format!(
"Cannot map shared memory {}",
String::from_utf8_lossy(&map_str_bytes)
)));
}
Ok(Self {
id,
handle,
map,
map_size,
})
}
}
}
impl ShMem for Win32ShMem {
fn id(&self) -> ShMemId {
self.id
}
fn len(&self) -> usize {
self.map_size
}
}
impl AsSlice for Win32ShMem {
type Entry = u8;
fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.map, self.map_size) }
}
}
impl AsMutSlice for Win32ShMem {
type Entry = u8;
fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.map, self.map_size) }
}
}
impl Drop for Win32ShMem {
fn drop(&mut self) {
unsafe {
UnmapViewOfFile(self.map as *mut c_void);
CloseHandle(self.handle);
}
}
}
#[derive(Clone, Debug)]
pub struct Win32ShMemProvider {}
impl Default for Win32ShMemProvider {
fn default() -> Self {
Self::new().unwrap()
}
}
impl ShMemProvider for Win32ShMemProvider {
type ShMem = Win32ShMem;
fn new() -> Result<Self, Error> {
Ok(Self {})
}
fn new_shmem(&mut self, map_size: usize) -> Result<Self::ShMem, Error> {
Win32ShMem::new_shmem(map_size)
}
fn shmem_from_id_and_size(
&mut self,
id: ShMemId,
size: usize,
) -> Result<Self::ShMem, Error> {
Win32ShMem::shmem_from_id_and_size(id, size)
}
}
}
#[derive(Debug)]
pub struct DummyShMemService;
impl DummyShMemService {
#[inline]
pub fn start() -> Result<Self, Error> {
Ok(Self {})
}
}
#[cfg(feature = "std")]
#[derive(Debug)]
pub struct ShMemCursor<T: ShMem> {
inner: T,
pos: usize,
}
#[cfg(feature = "std")]
impl<T: ShMem> ShMemCursor<T> {
pub fn new(shmem: T) -> Self {
Self {
inner: shmem,
pos: 0,
}
}
fn empty_slice_mut(&mut self) -> &mut [u8] {
&mut (self.inner.as_mut_slice()[self.pos..])
}
}
#[cfg(feature = "std")]
impl<T: ShMem> Write for ShMemCursor<T> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
match self.empty_slice_mut().write(buf) {
Ok(w) => {
self.pos += w;
Ok(w)
}
Err(e) => Err(e),
}
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
match self.empty_slice_mut().write_vectored(bufs) {
Ok(w) => {
self.pos += w;
Ok(w)
}
Err(e) => Err(e),
}
}
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
match self.empty_slice_mut().write_all(buf) {
Ok(w) => {
self.pos += buf.len();
Ok(w)
}
Err(e) => Err(e),
}
}
}
#[cfg(feature = "std")]
impl<T: ShMem> std::io::Seek for ShMemCursor<T> {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
let effective_new_pos = match pos {
std::io::SeekFrom::Start(s) => s,
std::io::SeekFrom::End(offset) => {
let map_len = self.inner.as_slice().len();
i64::try_from(map_len).unwrap();
let signed_pos = map_len as i64;
let effective = signed_pos.checked_add(offset).unwrap();
assert!(effective >= 0);
effective.try_into().unwrap()
}
std::io::SeekFrom::Current(offset) => {
let current_pos = self.pos;
i64::try_from(current_pos).unwrap();
let signed_pos = current_pos as i64;
let effective = signed_pos.checked_add(offset).unwrap();
assert!(effective >= 0);
effective.try_into().unwrap()
}
};
usize::try_from(effective_new_pos).unwrap();
self.pos = effective_new_pos as usize;
Ok(effective_new_pos)
}
}
#[cfg(feature = "std")]
#[cfg(test)]
mod tests {
use serial_test::serial;
use crate::bolts::{
shmem::{ShMemProvider, StdShMemProvider},
AsMutSlice, AsSlice,
};
#[test]
#[serial]
fn test_shmem_service() {
let mut provider = StdShMemProvider::new().unwrap();
let mut map = provider.new_shmem(1024).unwrap();
map.as_mut_slice()[0] = 1;
assert!(map.as_slice()[0] == 1);
}
}