ShMem Server for MacOS (#238)
* generalized ashmem server * fixed macos testcases * added StdShMemService * no_st * fmt * added testcase, fixed some bugs (not all) * solidified unix shmem * initial impl for MmapShMem * Added shmem service start to more testcases * clippy * fixed tetcases * added frida_libpng makefile for easy use * trying to fix build on ubuntu * fixed ubuntu build for libpng * no_std * fixed testcase
This commit is contained in:
parent
704830a501
commit
16c3a07be7
@ -97,7 +97,7 @@ pub fn main() {
|
|||||||
#[cfg(any(windows, unix))]
|
#[cfg(any(windows, unix))]
|
||||||
unsafe {
|
unsafe {
|
||||||
printf(
|
printf(
|
||||||
[b'%' as c_char, b's' as c_char, b'\n' as c_char, 0 as c_char].as_ptr(),
|
b"%s\n\0".as_ptr() as *const c_char,
|
||||||
CString::new(s).unwrap().as_ptr() as *const c_char,
|
CString::new(s).unwrap().as_ptr() as *const c_char,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use libafl::{
|
|||||||
bolts::{
|
bolts::{
|
||||||
current_nanos,
|
current_nanos,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMem, ShMemProvider, StdShMemProvider},
|
shmem::{ShMem, ShMemProvider, StdShMemProvider, StdShMemService},
|
||||||
tuples::tuple_list,
|
tuples::tuple_list,
|
||||||
},
|
},
|
||||||
corpus::{
|
corpus::{
|
||||||
@ -29,6 +29,9 @@ pub fn main() {
|
|||||||
let corpus_dirs = vec![PathBuf::from("./corpus")];
|
let corpus_dirs = vec![PathBuf::from("./corpus")];
|
||||||
|
|
||||||
const MAP_SIZE: usize = 65536;
|
const MAP_SIZE: usize = 65536;
|
||||||
|
|
||||||
|
let _service = StdShMemService::start().unwrap();
|
||||||
|
|
||||||
//Coverage map shared between observer and executor
|
//Coverage map shared between observer and executor
|
||||||
let mut shmem = StdShMemProvider::new().unwrap().new_map(MAP_SIZE).unwrap();
|
let mut shmem = StdShMemProvider::new().unwrap().new_map(MAP_SIZE).unwrap();
|
||||||
//let the forkserver know the shmid
|
//let the forkserver know the shmid
|
||||||
|
4
fuzzers/frida_libpng/.gitignore
vendored
4
fuzzers/frida_libpng/.gitignore
vendored
@ -1 +1,3 @@
|
|||||||
libpng-*
|
libpng-*
|
||||||
|
corpus_discovered
|
||||||
|
libafl_frida
|
39
fuzzers/frida_libpng/Makefile
Normal file
39
fuzzers/frida_libpng/Makefile
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
FUZZER_NAME="libafl_frida"
|
||||||
|
PROJECT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||||
|
|
||||||
|
PHONY: all
|
||||||
|
|
||||||
|
all: libafl_frida libpng-harness.so
|
||||||
|
|
||||||
|
libpng-1.6.37:
|
||||||
|
wget https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz
|
||||||
|
tar -xvf libpng-1.6.37.tar.xz
|
||||||
|
|
||||||
|
target/release/frida_libpng: src/*
|
||||||
|
# Build the frida libpng libfuzzer fuzzer
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
libpng-1.6.37/.libs/libpng16.a: libpng-1.6.37
|
||||||
|
cd libpng-1.6.37 && ./configure --enable-hardware-optimizations=yes --with-pic=yes
|
||||||
|
$(MAKE) -C libpng-1.6.37
|
||||||
|
|
||||||
|
libpng-harness.so: libpng-1.6.37/.libs/libpng16.a
|
||||||
|
$(CXX) -O3 -c -fPIC harness.cc -o harness.o
|
||||||
|
$(CXX) -O3 harness.o libpng-1.6.37/.libs/libpng16.a -shared -lz -o libpng-harness.so
|
||||||
|
|
||||||
|
libafl_frida: target/release/frida_libpng
|
||||||
|
cp target/release/frida_libpng libafl_frida
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(MAKE) -C libpng-1.6.37 clean
|
||||||
|
rm $(FUZZER_NAME)
|
||||||
|
|
||||||
|
run: all
|
||||||
|
./$(FUZZER_NAME) ./libpng-harness.so LLVMFuzzerTestOneInput ./libpng-harness.so --cores=0
|
||||||
|
|
||||||
|
short_test: all
|
||||||
|
# We allow exit code 124 too, which is sigterm
|
||||||
|
(timeout 3s ./libafl_frida ./libpng-harness.so LLVMFuzzerTestOneInput ./libpng-harness.so --cores=0,1 || [ $$? -eq 124 ])
|
||||||
|
|
||||||
|
test: all
|
||||||
|
timeout 60s ./$(FUZZER_NAME) ./libpng-harness.so LLVMFuzzerTestOneInput ./libpng-harness.so --cores=0,1
|
BIN
fuzzers/frida_libpng/corpus/not_kitty.png
Normal file
BIN
fuzzers/frida_libpng/corpus/not_kitty.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 218 B |
BIN
fuzzers/frida_libpng/corpus/not_kitty_alpha.png
Normal file
BIN
fuzzers/frida_libpng/corpus/not_kitty_alpha.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 376 B |
BIN
fuzzers/frida_libpng/corpus/not_kitty_gamma.png
Normal file
BIN
fuzzers/frida_libpng/corpus/not_kitty_gamma.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 228 B |
BIN
fuzzers/frida_libpng/corpus/not_kitty_icc.png
Normal file
BIN
fuzzers/frida_libpng/corpus/not_kitty_icc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 427 B |
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(all(cfg = "std", unix))]
|
||||||
use libafl::bolts::os::ashmem_server::AshmemService;
|
use libafl::bolts::os::unix_shmem_server::ShMemService;
|
||||||
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
bolts::{
|
bolts::{
|
||||||
@ -12,7 +12,7 @@ use libafl::{
|
|||||||
launcher::Launcher,
|
launcher::Launcher,
|
||||||
os::parse_core_bind_arg,
|
os::parse_core_bind_arg,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider, StdShMemService},
|
||||||
tuples::{tuple_list, Merge},
|
tuples::{tuple_list, Merge},
|
||||||
},
|
},
|
||||||
corpus::{
|
corpus::{
|
||||||
@ -233,7 +233,7 @@ pub fn main() {
|
|||||||
.map(|addrstr| addrstr.parse().unwrap());
|
.map(|addrstr| addrstr.parse().unwrap());
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
fuzz(
|
match fuzz(
|
||||||
matches.value_of("harness").unwrap(),
|
matches.value_of("harness").unwrap(),
|
||||||
matches.value_of("symbol").unwrap(),
|
matches.value_of("symbol").unwrap(),
|
||||||
&matches
|
&matches
|
||||||
@ -252,8 +252,10 @@ pub fn main() {
|
|||||||
.value_of("configuration")
|
.value_of("configuration")
|
||||||
.unwrap_or("default launcher")
|
.unwrap_or("default launcher")
|
||||||
.to_string(),
|
.to_string(),
|
||||||
)
|
) {
|
||||||
.expect("An error occurred while fuzzing");
|
Ok(()) | Err(Error::ShuttingDown) => println!("Finished fuzzing. Good bye."),
|
||||||
|
Err(e) => panic!("Error during fuzzing: {:?}", e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,8 +294,7 @@ unsafe fn fuzz(
|
|||||||
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
|
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
|
||||||
let stats = MultiStats::new(|s| println!("{}", s));
|
let stats = MultiStats::new(|s| println!("{}", s));
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
let _service = StdShMemService::start().expect("Failed to start ShMem service");
|
||||||
AshmemService::start().expect("Failed to start Ashmem service");
|
|
||||||
let shmem_provider = StdShMemProvider::new()?;
|
let shmem_provider = StdShMemProvider::new()?;
|
||||||
|
|
||||||
let mut run_client = |state: Option<StdState<_, _, _, _, _>>, mut mgr| {
|
let mut run_client = |state: Option<StdState<_, _, _, _, _>>, mut mgr| {
|
||||||
@ -317,7 +318,7 @@ unsafe fn fuzz(
|
|||||||
&gum,
|
&gum,
|
||||||
&frida_options,
|
&frida_options,
|
||||||
module_name,
|
module_name,
|
||||||
&modules_to_instrument,
|
modules_to_instrument,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
// Create an observation channel using the coverage map
|
||||||
@ -411,7 +412,7 @@ unsafe fn fuzz(
|
|||||||
// In case the corpus is empty (on first run), reset
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.corpus().count() < 1 {
|
if state.corpus().count() < 1 {
|
||||||
state
|
state
|
||||||
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs)
|
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, corpus_dirs)
|
||||||
.unwrap_or_else(|_| panic!("Failed to load initial corpus at {:?}", &corpus_dirs));
|
.unwrap_or_else(|_| panic!("Failed to load initial corpus at {:?}", &corpus_dirs));
|
||||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ use libafl::{
|
|||||||
current_nanos, current_time,
|
current_nanos, current_time,
|
||||||
os::dup2,
|
os::dup2,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider, StdShMemService},
|
||||||
tuples::{tuple_list, Merge},
|
tuples::{tuple_list, Merge},
|
||||||
},
|
},
|
||||||
corpus::{Corpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, QueueCorpusScheduler},
|
corpus::{Corpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, QueueCorpusScheduler},
|
||||||
@ -181,8 +181,7 @@ fn fuzz(
|
|||||||
|
|
||||||
// We need a shared map to store our state before a crash.
|
// We need a shared map to store our state before a crash.
|
||||||
// This way, we are able to continue fuzzing afterwards.
|
// This way, we are able to continue fuzzing afterwards.
|
||||||
#[cfg(target_os = "android")]
|
let _service = StdShMemService::start().expect("Failed to start ShMem service");
|
||||||
AshmemService::start().expect("Failed to start Ashmem service");
|
|
||||||
let mut shmem_provider = StdShMemProvider::new()?;
|
let mut shmem_provider = StdShMemProvider::new()?;
|
||||||
|
|
||||||
let (state, mut mgr) = match SimpleRestartingEventManager::launch(stats, &mut shmem_provider) {
|
let (state, mut mgr) = match SimpleRestartingEventManager::launch(stats, &mut shmem_provider) {
|
||||||
|
@ -19,7 +19,7 @@ use libafl::{
|
|||||||
current_nanos, current_time,
|
current_nanos, current_time,
|
||||||
os::dup2,
|
os::dup2,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider, StdShMemService},
|
||||||
tuples::{tuple_list, Merge},
|
tuples::{tuple_list, Merge},
|
||||||
},
|
},
|
||||||
corpus::{Corpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, QueueCorpusScheduler},
|
corpus::{Corpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, QueueCorpusScheduler},
|
||||||
|
@ -11,7 +11,7 @@ use libafl::{
|
|||||||
launcher::Launcher,
|
launcher::Launcher,
|
||||||
os::parse_core_bind_arg,
|
os::parse_core_bind_arg,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider, StdShMemService},
|
||||||
tuples::{tuple_list, Merge},
|
tuples::{tuple_list, Merge},
|
||||||
},
|
},
|
||||||
corpus::{
|
corpus::{
|
||||||
@ -78,8 +78,7 @@ pub fn libafl_main() {
|
|||||||
|
|
||||||
println!("Workdir: {:?}", workdir.to_string_lossy().to_string());
|
println!("Workdir: {:?}", workdir.to_string_lossy().to_string());
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
let _service = StdShMemService::start().expect("Failed to start ShMem service");
|
||||||
AshmemService::start().expect("Failed to start Ashmem service");
|
|
||||||
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||||
|
|
||||||
let stats = MultiStats::new(|s| println!("{}", s));
|
let stats = MultiStats::new(|s| println!("{}", s));
|
||||||
|
@ -13,7 +13,7 @@ use libafl::{
|
|||||||
launcher::Launcher,
|
launcher::Launcher,
|
||||||
os::parse_core_bind_arg,
|
os::parse_core_bind_arg,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider, StdShMemService},
|
||||||
tuples::{tuple_list, Merge},
|
tuples::{tuple_list, Merge},
|
||||||
},
|
},
|
||||||
corpus::{
|
corpus::{
|
||||||
@ -54,8 +54,7 @@ pub fn libafl_main() {
|
|||||||
env::current_dir().unwrap().to_string_lossy().to_string()
|
env::current_dir().unwrap().to_string_lossy().to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
let _service = StdShMemService::start().expect("Failed to start ShMem service");
|
||||||
AshmemService::start().expect("Failed to start Ashmem service");
|
|
||||||
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||||
|
|
||||||
let stats = MultiStats::new(|s| println!("{}", s));
|
let stats = MultiStats::new(|s| println!("{}", s));
|
||||||
|
@ -21,6 +21,7 @@ fxhash = "0.2.1" # yet another hash
|
|||||||
xxhash-rust = { version = "0.8.2", features = ["xxh3"] } # xxh3 hashing for rust
|
xxhash-rust = { version = "0.8.2", features = ["xxh3"] } # xxh3 hashing for rust
|
||||||
serde_json = "1.0.60"
|
serde_json = "1.0.60"
|
||||||
num_cpus = "1.0" # cpu count, for llmp example
|
num_cpus = "1.0" # cpu count, for llmp example
|
||||||
|
serial_test = "0.5"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "rand_speeds"
|
name = "rand_speeds"
|
||||||
|
@ -117,6 +117,8 @@ fn main() {
|
|||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn main() {
|
fn main() {
|
||||||
|
use libafl::bolts::shmem::StdShMemService;
|
||||||
|
|
||||||
/* The main node has a broker, and a few worker threads */
|
/* The main node has a broker, and a few worker threads */
|
||||||
|
|
||||||
let mode = std::env::args()
|
let mode = std::env::args()
|
||||||
@ -137,6 +139,9 @@ fn main() {
|
|||||||
|
|
||||||
match mode.as_str() {
|
match mode.as_str() {
|
||||||
"broker" => {
|
"broker" => {
|
||||||
|
// The shmem service is needed on some platforms like Android and MacOS
|
||||||
|
let _service = StdShMemService::start().unwrap();
|
||||||
|
|
||||||
let mut broker = llmp::LlmpBroker::new(StdShMemProvider::new().unwrap()).unwrap();
|
let mut broker = llmp::LlmpBroker::new(StdShMemProvider::new().unwrap()).unwrap();
|
||||||
broker.launch_tcp_listener_on(port).unwrap();
|
broker.launch_tcp_listener_on(port).unwrap();
|
||||||
broker.loop_forever(&mut broker_message_hook, Some(Duration::from_millis(5)))
|
broker.loop_forever(&mut broker_message_hook, Some(Duration::from_millis(5)))
|
||||||
|
@ -2650,6 +2650,8 @@ mod tests {
|
|||||||
|
|
||||||
use std::{thread::sleep, time::Duration};
|
use std::{thread::sleep, time::Duration};
|
||||||
|
|
||||||
|
use serial_test::serial;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
LlmpClient,
|
LlmpClient,
|
||||||
LlmpConnection::{self, IsBroker, IsClient},
|
LlmpConnection::{self, IsBroker, IsClient},
|
||||||
@ -2657,10 +2659,14 @@ mod tests {
|
|||||||
Tag,
|
Tag,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::bolts::shmem::{ShMemProvider, StdShMemProvider};
|
use crate::bolts::shmem::{ShMemProvider, StdShMemProvider, StdShMemService};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[serial]
|
||||||
pub fn llmp_connection() {
|
pub fn llmp_connection() {
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
let service = StdShMemService::start().unwrap();
|
||||||
|
|
||||||
let shmem_provider = StdShMemProvider::new().unwrap();
|
let shmem_provider = StdShMemProvider::new().unwrap();
|
||||||
let mut broker = match LlmpConnection::on_port(shmem_provider.clone(), 1337).unwrap() {
|
let mut broker = match LlmpConnection::on_port(shmem_provider.clone(), 1337).unwrap() {
|
||||||
IsClient { client: _ } => panic!("Could not bind to port as broker"),
|
IsClient { client: _ } => panic!("Could not bind to port as broker"),
|
||||||
|
@ -7,7 +7,7 @@ use crate::Error;
|
|||||||
use std::{env, process::Command};
|
use std::{env, process::Command};
|
||||||
|
|
||||||
#[cfg(all(unix, feature = "std"))]
|
#[cfg(all(unix, feature = "std"))]
|
||||||
pub mod ashmem_server;
|
pub mod unix_shmem_server;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub mod unix_signals;
|
pub mod unix_signals;
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
/*!
|
/*!
|
||||||
On Android, we can only share maps between processes by serializing fds over sockets.
|
On `Android`, we can only share maps between processes by serializing fds over sockets.
|
||||||
Hence, the `ashmem_server` keeps track of existing maps, creates new maps for clients,
|
On `MacOS`, we cannot rely on reference counting for Maps.
|
||||||
|
Hence, the [`unix_shmem_server`] keeps track of existing maps, creates new maps for clients,
|
||||||
and forwards them over unix domain sockets.
|
and forwards them over unix domain sockets.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::shmem::{
|
bolts::shmem::{ShMem, ShMemDescription, ShMemId, ShMemProvider},
|
||||||
unix_shmem::ashmem::{AshmemShMem, AshmemShMemProvider},
|
|
||||||
ShMem, ShMemDescription, ShMemId, ShMemProvider,
|
|
||||||
},
|
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
use core::mem::ManuallyDrop;
|
use core::mem::ManuallyDrop;
|
||||||
@ -16,9 +14,12 @@ use hashbrown::HashMap;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
|
fs,
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
|
marker::PhantomData,
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
sync::{Arc, Condvar, Mutex},
|
sync::{Arc, Condvar, Mutex},
|
||||||
|
thread::JoinHandle,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(all(feature = "std", unix))]
|
#[cfg(all(feature = "std", unix))]
|
||||||
@ -36,25 +37,36 @@ use std::{
|
|||||||
#[cfg(all(unix, feature = "std"))]
|
#[cfg(all(unix, feature = "std"))]
|
||||||
use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt};
|
use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt};
|
||||||
|
|
||||||
const ASHMEM_SERVER_NAME: &str = "@ashmem_server";
|
/// The default server name for our abstract shmem server
|
||||||
|
#[cfg(all(unix, not(any(target_os = "ios", target_os = "macos"))))]
|
||||||
|
const UNIX_SERVER_NAME: &str = "@libafl_unix_shmem_server";
|
||||||
|
/// `MacOS` server name is on disk, since `MacOS` doesn't support abtract domain sockets.
|
||||||
|
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
||||||
|
const UNIX_SERVER_NAME: &str = "./libafl_unix_shmem_server";
|
||||||
|
|
||||||
/// Hands out served shared maps, as used on Android.
|
/// Hands out served shared maps, as used on Android.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ServedShMemProvider {
|
pub struct ServedShMemProvider<SP> {
|
||||||
stream: UnixStream,
|
stream: UnixStream,
|
||||||
inner: AshmemShMemProvider,
|
inner: SP,
|
||||||
id: i32,
|
id: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`ShMem`] that got served from a [`AshmemService`] via domain sockets and can now be used in this program.
|
/// [`ShMem`] that got served from a [`AshmemService`] via domain sockets and can now be used in this program.
|
||||||
/// It works around Android's lack of "proper" shared maps.
|
/// It works around Android's lack of "proper" shared maps.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ServedShMem {
|
pub struct ServedShMem<SH>
|
||||||
inner: ManuallyDrop<AshmemShMem>,
|
where
|
||||||
|
SH: ShMem,
|
||||||
|
{
|
||||||
|
inner: ManuallyDrop<SH>,
|
||||||
server_fd: i32,
|
server_fd: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShMem for ServedShMem {
|
impl<SH> ShMem for ServedShMem<SH>
|
||||||
|
where
|
||||||
|
SH: ShMem,
|
||||||
|
{
|
||||||
fn id(&self) -> ShMemId {
|
fn id(&self) -> ShMemId {
|
||||||
let client_id = self.inner.id();
|
let client_id = self.inner.id();
|
||||||
ShMemId::from_string(&format!("{}:{}", self.server_fd, client_id.to_string()))
|
ShMemId::from_string(&format!("{}:{}", self.server_fd, client_id.to_string()))
|
||||||
@ -73,10 +85,10 @@ impl ShMem for ServedShMem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServedShMemProvider {
|
impl<SP> ServedShMemProvider<SP> {
|
||||||
/// Send a request to the server, and wait for a response
|
/// Send a request to the server, and wait for a response
|
||||||
#[allow(clippy::similar_names)] // id and fd
|
#[allow(clippy::similar_names)] // id and fd
|
||||||
fn send_receive(&mut self, request: AshmemRequest) -> Result<(i32, i32), Error> {
|
fn send_receive(&mut self, request: ServedShMemRequest) -> Result<(i32, i32), Error> {
|
||||||
let body = postcard::to_allocvec(&request)?;
|
let body = postcard::to_allocvec(&request)?;
|
||||||
|
|
||||||
let header = (body.len() as u32).to_be_bytes();
|
let header = (body.len() as u32).to_be_bytes();
|
||||||
@ -99,34 +111,43 @@ impl ServedShMemProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ServedShMemProvider {
|
impl<SP> Default for ServedShMemProvider<SP>
|
||||||
|
where
|
||||||
|
SP: ShMemProvider,
|
||||||
|
{
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new().unwrap()
|
Self::new().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for ServedShMemProvider {
|
impl<SP> Clone for ServedShMemProvider<SP>
|
||||||
|
where
|
||||||
|
SP: ShMemProvider,
|
||||||
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self::new().unwrap()
|
Self::new().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShMemProvider for ServedShMemProvider {
|
impl<SP> ShMemProvider for ServedShMemProvider<SP>
|
||||||
type Mem = ServedShMem;
|
where
|
||||||
|
SP: ShMemProvider,
|
||||||
|
{
|
||||||
|
type Mem = ServedShMem<SP::Mem>;
|
||||||
|
|
||||||
/// Connect to the server and return a new [`ServedShMemProvider`]
|
/// Connect to the server and return a new [`ServedShMemProvider`]
|
||||||
fn new() -> Result<Self, Error> {
|
fn new() -> Result<Self, Error> {
|
||||||
let mut res = Self {
|
let mut res = Self {
|
||||||
stream: UnixStream::connect_to_unix_addr(&UnixSocketAddr::new(ASHMEM_SERVER_NAME)?)?,
|
stream: UnixStream::connect_to_unix_addr(&UnixSocketAddr::new(UNIX_SERVER_NAME)?)?,
|
||||||
inner: AshmemShMemProvider::new()?,
|
inner: SP::new()?,
|
||||||
id: -1,
|
id: -1,
|
||||||
};
|
};
|
||||||
let (id, _) = res.send_receive(AshmemRequest::Hello(None))?;
|
let (id, _) = res.send_receive(ServedShMemRequest::Hello(None))?;
|
||||||
res.id = id;
|
res.id = id;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
fn new_map(&mut self, map_size: usize) -> Result<Self::Mem, crate::Error> {
|
fn new_map(&mut self, map_size: usize) -> Result<Self::Mem, crate::Error> {
|
||||||
let (server_fd, client_fd) = self.send_receive(AshmemRequest::NewMap(map_size))?;
|
let (server_fd, client_fd) = self.send_receive(ServedShMemRequest::NewMap(map_size))?;
|
||||||
|
|
||||||
Ok(ServedShMem {
|
Ok(ServedShMem {
|
||||||
inner: ManuallyDrop::new(
|
inner: ManuallyDrop::new(
|
||||||
@ -140,7 +161,7 @@ impl ShMemProvider for ServedShMemProvider {
|
|||||||
fn from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result<Self::Mem, Error> {
|
fn from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result<Self::Mem, Error> {
|
||||||
let parts = id.as_str().split(':').collect::<Vec<&str>>();
|
let parts = id.as_str().split(':').collect::<Vec<&str>>();
|
||||||
let server_id_str = parts.get(0).unwrap();
|
let server_id_str = parts.get(0).unwrap();
|
||||||
let (server_fd, client_fd) = self.send_receive(AshmemRequest::ExistingMap(
|
let (server_fd, client_fd) = self.send_receive(ServedShMemRequest::ExistingMap(
|
||||||
ShMemDescription::from_string_and_size(server_id_str, size),
|
ShMemDescription::from_string_and_size(server_id_str, size),
|
||||||
))?;
|
))?;
|
||||||
Ok(ServedShMem {
|
Ok(ServedShMem {
|
||||||
@ -156,8 +177,8 @@ impl ShMemProvider for ServedShMemProvider {
|
|||||||
if is_child {
|
if is_child {
|
||||||
// After fork, the child needs to reconnect as to not share the fds with the parent.
|
// After fork, the child needs to reconnect as to not share the fds with the parent.
|
||||||
self.stream =
|
self.stream =
|
||||||
UnixStream::connect_to_unix_addr(&UnixSocketAddr::new(ASHMEM_SERVER_NAME)?)?;
|
UnixStream::connect_to_unix_addr(&UnixSocketAddr::new(UNIX_SERVER_NAME)?)?;
|
||||||
let (id, _) = self.send_receive(AshmemRequest::Hello(Some(self.id)))?;
|
let (id, _) = self.send_receive(ServedShMemRequest::Hello(Some(self.id)))?;
|
||||||
self.id = id;
|
self.id = id;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -165,8 +186,8 @@ impl ShMemProvider for ServedShMemProvider {
|
|||||||
|
|
||||||
fn release_map(&mut self, map: &mut Self::Mem) {
|
fn release_map(&mut self, map: &mut Self::Mem) {
|
||||||
let (refcount, _) = self
|
let (refcount, _) = self
|
||||||
.send_receive(AshmemRequest::Deregister(map.server_fd))
|
.send_receive(ServedShMemRequest::Deregister(map.server_fd))
|
||||||
.expect("Could not communicate with AshMem server!");
|
.expect("Could not communicate with ServedShMem server!");
|
||||||
if refcount == 1 {
|
if refcount == 1 {
|
||||||
unsafe {
|
unsafe {
|
||||||
ManuallyDrop::drop(&mut map.inner);
|
ManuallyDrop::drop(&mut map.inner);
|
||||||
@ -177,7 +198,7 @@ impl ShMemProvider for ServedShMemProvider {
|
|||||||
|
|
||||||
/// A request sent to the [`ShMem`] server to receive a fd to a shared map
|
/// A request sent to the [`ShMem`] server to receive a fd to a shared map
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum AshmemRequest {
|
pub enum ServedShMemRequest {
|
||||||
/// Register a new map with a given size.
|
/// Register a new map with a given size.
|
||||||
NewMap(usize),
|
NewMap(usize),
|
||||||
/// Another client already has a map with this description mapped.
|
/// Another client already has a map with this description mapped.
|
||||||
@ -187,15 +208,23 @@ pub enum AshmemRequest {
|
|||||||
/// A message that tells us hello, and optionally which other client we were created from, we
|
/// A message that tells us hello, and optionally which other client we were created from, we
|
||||||
/// return a client id.
|
/// return a client id.
|
||||||
Hello(Option<i32>),
|
Hello(Option<i32>),
|
||||||
|
/// The ShMem Service should exit. This is sually sent internally on `drop`, but feel free to do whatever with it?
|
||||||
|
Exit,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct AshmemClient {
|
struct SharedShMemClient<SH>
|
||||||
|
where
|
||||||
|
SH: ShMem,
|
||||||
|
{
|
||||||
stream: UnixStream,
|
stream: UnixStream,
|
||||||
maps: HashMap<i32, Vec<Rc<RefCell<AshmemShMem>>>>,
|
maps: HashMap<i32, Vec<Rc<RefCell<SH>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AshmemClient {
|
impl<SH> SharedShMemClient<SH>
|
||||||
|
where
|
||||||
|
SH: ShMem,
|
||||||
|
{
|
||||||
fn new(stream: UnixStream) -> Self {
|
fn new(stream: UnixStream) -> Self {
|
||||||
Self {
|
Self {
|
||||||
stream,
|
stream,
|
||||||
@ -207,37 +236,134 @@ impl AshmemClient {
|
|||||||
/// The [`AshmemService`] is a service handing out [`ShMem`] pages via unix domain sockets.
|
/// The [`AshmemService`] is a service handing out [`ShMem`] pages via unix domain sockets.
|
||||||
/// It is mainly used and needed on Android.
|
/// It is mainly used and needed on Android.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AshmemService {
|
pub struct ShMemService<SP>
|
||||||
provider: AshmemShMemProvider,
|
where
|
||||||
clients: HashMap<RawFd, AshmemClient>,
|
SP: ShMemProvider,
|
||||||
all_maps: HashMap<i32, Weak<RefCell<AshmemShMem>>>,
|
{
|
||||||
|
join_handle: Option<JoinHandle<Result<(), Error>>>,
|
||||||
|
phantom: PhantomData<*const SP>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum AshmemResponse {
|
enum ServedShMemResponse<SP>
|
||||||
Mapping(Rc<RefCell<AshmemShMem>>),
|
where
|
||||||
|
SP: ShMemProvider,
|
||||||
|
{
|
||||||
|
Mapping(Rc<RefCell<SP::Mem>>),
|
||||||
Id(i32),
|
Id(i32),
|
||||||
RefCount(u32),
|
RefCount(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AshmemService {
|
impl<SP> ShMemService<SP>
|
||||||
/// Create a new [`AshMem`] service
|
where
|
||||||
|
SP: ShMemProvider,
|
||||||
|
{
|
||||||
|
/// Create a new [`ShMemService`], then listen and service incoming connections in a new thread.
|
||||||
|
pub fn start() -> Result<Self, Error> {
|
||||||
|
println!("Starting ShMemService");
|
||||||
|
|
||||||
|
#[allow(clippy::mutex_atomic)]
|
||||||
|
let syncpair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||||
|
let childsyncpair = Arc::clone(&syncpair);
|
||||||
|
let join_handle = thread::spawn(move || {
|
||||||
|
println!("Thread...");
|
||||||
|
|
||||||
|
let mut worker = match ServedShMemServiceWorker::<SP>::new() {
|
||||||
|
Ok(worker) => worker,
|
||||||
|
Err(e) => {
|
||||||
|
// Make sure the parent processes can continue
|
||||||
|
let (lock, cvar) = &*childsyncpair;
|
||||||
|
*lock.lock().unwrap() = true;
|
||||||
|
cvar.notify_one();
|
||||||
|
|
||||||
|
println!("Error creating ShMemService: {:?}", e);
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Err(e) = worker.listen(UNIX_SERVER_NAME, &childsyncpair) {
|
||||||
|
println!("Error spawning ShMemService: {:?}", e);
|
||||||
|
Err(e)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let (lock, cvar) = &*syncpair;
|
||||||
|
let mut started = lock.lock().unwrap();
|
||||||
|
while !*started {
|
||||||
|
started = cvar.wait(started).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
join_handle: Some(join_handle),
|
||||||
|
phantom: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SP> Drop for ShMemService<SP>
|
||||||
|
where
|
||||||
|
SP: ShMemProvider,
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let join_handle = self.join_handle.take();
|
||||||
|
// TODO: Guess we could use the `cvar` // Mutex here instead?
|
||||||
|
if let Some(join_handle) = join_handle {
|
||||||
|
let mut stream = match UnixStream::connect_to_unix_addr(
|
||||||
|
&UnixSocketAddr::new(UNIX_SERVER_NAME).unwrap(),
|
||||||
|
) {
|
||||||
|
Ok(stream) => stream,
|
||||||
|
Err(_) => return, // ignoring non-started server
|
||||||
|
};
|
||||||
|
|
||||||
|
let body = postcard::to_allocvec(&ServedShMemRequest::Exit).unwrap();
|
||||||
|
|
||||||
|
let header = (body.len() as u32).to_be_bytes();
|
||||||
|
let mut message = header.to_vec();
|
||||||
|
message.extend(body);
|
||||||
|
|
||||||
|
stream
|
||||||
|
.write_all(&message)
|
||||||
|
.expect("Failed to send bye-message to ShMemService");
|
||||||
|
join_handle
|
||||||
|
.join()
|
||||||
|
.expect("Failed to join ShMemService thread!")
|
||||||
|
.expect("Error in ShMemService thread!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The struct for the worker, handling incoming requests for [`ShMem`].
|
||||||
|
struct ServedShMemServiceWorker<SP>
|
||||||
|
where
|
||||||
|
SP: ShMemProvider,
|
||||||
|
{
|
||||||
|
provider: SP,
|
||||||
|
clients: HashMap<RawFd, SharedShMemClient<SP::Mem>>,
|
||||||
|
all_maps: HashMap<i32, Weak<RefCell<SP::Mem>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SP> ServedShMemServiceWorker<SP>
|
||||||
|
where
|
||||||
|
SP: ShMemProvider,
|
||||||
|
{
|
||||||
|
/// Create a new [`ShMemService`]
|
||||||
fn new() -> Result<Self, Error> {
|
fn new() -> Result<Self, Error> {
|
||||||
Ok(AshmemService {
|
Ok(Self {
|
||||||
provider: AshmemShMemProvider::new()?,
|
provider: SP::new()?,
|
||||||
clients: HashMap::new(),
|
clients: HashMap::new(),
|
||||||
all_maps: HashMap::new(),
|
all_maps: HashMap::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read and handle the client request, send the answer over unix fd.
|
/// Read and handle the client request, send the answer over unix fd.
|
||||||
fn handle_request(&mut self, client_id: RawFd) -> Result<AshmemResponse, Error> {
|
fn handle_request(&mut self, client_id: RawFd) -> Result<ServedShMemResponse<SP>, Error> {
|
||||||
let request = self.read_request(client_id)?;
|
let request = self.read_request(client_id)?;
|
||||||
|
|
||||||
//println!("got ashmem client: {}, request:{:?}", client_id, request);
|
//println!("got ashmem client: {}, request:{:?}", client_id, request);
|
||||||
// Handle the client request
|
// Handle the client request
|
||||||
let response = match request {
|
let response = match request {
|
||||||
AshmemRequest::Hello(other_id) => {
|
ServedShMemRequest::Hello(other_id) => {
|
||||||
if let Some(other_id) = other_id {
|
if let Some(other_id) = other_id {
|
||||||
if other_id != client_id {
|
if other_id != client_id {
|
||||||
// remove temporarily
|
// remove temporarily
|
||||||
@ -249,21 +375,21 @@ impl AshmemService {
|
|||||||
self.clients.insert(other_id, other_client.unwrap());
|
self.clients.insert(other_id, other_client.unwrap());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(AshmemResponse::Id(client_id))
|
Ok(ServedShMemResponse::Id(client_id))
|
||||||
}
|
}
|
||||||
AshmemRequest::NewMap(map_size) => {
|
ServedShMemRequest::NewMap(map_size) => {
|
||||||
let new_map = self.provider.new_map(map_size)?;
|
let new_map = self.provider.new_map(map_size)?;
|
||||||
let description = new_map.description();
|
let description = new_map.description();
|
||||||
let new_rc = Rc::new(RefCell::new(new_map));
|
let new_rc = Rc::new(RefCell::new(new_map));
|
||||||
self.all_maps
|
self.all_maps
|
||||||
.insert(description.id.into(), Rc::downgrade(&new_rc));
|
.insert(description.id.into(), Rc::downgrade(&new_rc));
|
||||||
Ok(AshmemResponse::Mapping(new_rc))
|
Ok(ServedShMemResponse::Mapping(new_rc))
|
||||||
}
|
}
|
||||||
AshmemRequest::ExistingMap(description) => {
|
ServedShMemRequest::ExistingMap(description) => {
|
||||||
let client = self.clients.get_mut(&client_id).unwrap();
|
let client = self.clients.get_mut(&client_id).unwrap();
|
||||||
let description_id: i32 = description.id.into();
|
let description_id: i32 = description.id.into();
|
||||||
if client.maps.contains_key(&description_id) {
|
if client.maps.contains_key(&description_id) {
|
||||||
Ok(AshmemResponse::Mapping(
|
Ok(ServedShMemResponse::Mapping(
|
||||||
client
|
client
|
||||||
.maps
|
.maps
|
||||||
.get_mut(&description_id)
|
.get_mut(&description_id)
|
||||||
@ -275,7 +401,7 @@ impl AshmemService {
|
|||||||
.clone(),
|
.clone(),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(AshmemResponse::Mapping(
|
Ok(ServedShMemResponse::Mapping(
|
||||||
self.all_maps
|
self.all_maps
|
||||||
.get_mut(&description_id)
|
.get_mut(&description_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -285,24 +411,29 @@ impl AshmemService {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AshmemRequest::Deregister(map_id) => {
|
ServedShMemRequest::Deregister(map_id) => {
|
||||||
let client = self.clients.get_mut(&client_id).unwrap();
|
let client = self.clients.get_mut(&client_id).unwrap();
|
||||||
let maps = client.maps.entry(map_id).or_default();
|
let maps = client.maps.entry(map_id).or_default();
|
||||||
if maps.is_empty() {
|
if maps.is_empty() {
|
||||||
Ok(AshmemResponse::RefCount(0u32))
|
Ok(ServedShMemResponse::RefCount(0u32))
|
||||||
} else {
|
} else {
|
||||||
Ok(AshmemResponse::RefCount(
|
Ok(ServedShMemResponse::RefCount(
|
||||||
Rc::strong_count(&maps.pop().unwrap()) as u32,
|
Rc::strong_count(&maps.pop().unwrap()) as u32,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ServedShMemRequest::Exit => {
|
||||||
|
println!("ShMemService - Exiting");
|
||||||
|
// stopping the server
|
||||||
|
return Err(Error::ShuttingDown);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
//println!("send ashmem client: {}, response: {:?}", client_id, &response);
|
//println!("send ashmem client: {}, response: {:?}", client_id, &response);
|
||||||
|
|
||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_request(&mut self, client_id: RawFd) -> Result<AshmemRequest, Error> {
|
fn read_request(&mut self, client_id: RawFd) -> Result<ServedShMemRequest, Error> {
|
||||||
let client = self.clients.get_mut(&client_id).unwrap();
|
let client = self.clients.get_mut(&client_id).unwrap();
|
||||||
|
|
||||||
// Always receive one be u32 of size, then the command.
|
// Always receive one be u32 of size, then the command.
|
||||||
@ -315,7 +446,7 @@ impl AshmemService {
|
|||||||
.stream
|
.stream
|
||||||
.read_exact(&mut bytes)
|
.read_exact(&mut bytes)
|
||||||
.expect("Failed to read message body");
|
.expect("Failed to read message body");
|
||||||
let request: AshmemRequest = postcard::from_bytes(&bytes)?;
|
let request: ServedShMemRequest = postcard::from_bytes(&bytes)?;
|
||||||
|
|
||||||
Ok(request)
|
Ok(request)
|
||||||
}
|
}
|
||||||
@ -323,7 +454,7 @@ impl AshmemService {
|
|||||||
let response = self.handle_request(client_id)?;
|
let response = self.handle_request(client_id)?;
|
||||||
|
|
||||||
match response {
|
match response {
|
||||||
AshmemResponse::Mapping(mapping) => {
|
ServedShMemResponse::Mapping(mapping) => {
|
||||||
let id = mapping.borrow().id();
|
let id = mapping.borrow().id();
|
||||||
let server_fd: i32 = id.to_string().parse().unwrap();
|
let server_fd: i32 = id.to_string().parse().unwrap();
|
||||||
let client = self.clients.get_mut(&client_id).unwrap();
|
let client = self.clients.get_mut(&client_id).unwrap();
|
||||||
@ -332,11 +463,11 @@ impl AshmemService {
|
|||||||
.send_fds(id.to_string().as_bytes(), &[server_fd])?;
|
.send_fds(id.to_string().as_bytes(), &[server_fd])?;
|
||||||
client.maps.entry(server_fd).or_default().push(mapping);
|
client.maps.entry(server_fd).or_default().push(mapping);
|
||||||
}
|
}
|
||||||
AshmemResponse::Id(id) => {
|
ServedShMemResponse::Id(id) => {
|
||||||
let client = self.clients.get_mut(&client_id).unwrap();
|
let client = self.clients.get_mut(&client_id).unwrap();
|
||||||
client.stream.send_fds(id.to_string().as_bytes(), &[])?;
|
client.stream.send_fds(id.to_string().as_bytes(), &[])?;
|
||||||
}
|
}
|
||||||
AshmemResponse::RefCount(refcount) => {
|
ServedShMemResponse::RefCount(refcount) => {
|
||||||
let client = self.clients.get_mut(&client_id).unwrap();
|
let client = self.clients.get_mut(&client_id).unwrap();
|
||||||
client
|
client
|
||||||
.stream
|
.stream
|
||||||
@ -346,23 +477,6 @@ impl AshmemService {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new [`AshmemService`], then listen and service incoming connections in a new thread.
|
|
||||||
pub fn start() -> Result<thread::JoinHandle<Result<(), Error>>, Error> {
|
|
||||||
#[allow(clippy::mutex_atomic)]
|
|
||||||
let syncpair = Arc::new((Mutex::new(false), Condvar::new()));
|
|
||||||
let childsyncpair = Arc::clone(&syncpair);
|
|
||||||
let join_handle =
|
|
||||||
thread::spawn(move || Self::new()?.listen(ASHMEM_SERVER_NAME, &childsyncpair));
|
|
||||||
|
|
||||||
let (lock, cvar) = &*syncpair;
|
|
||||||
let mut started = lock.lock().unwrap();
|
|
||||||
while !*started {
|
|
||||||
started = cvar.wait(started).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(join_handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Listen on a filename (or abstract name) for new connections and serve them. This function
|
/// Listen on a filename (or abstract name) for new connections and serve them. This function
|
||||||
/// should not return.
|
/// should not return.
|
||||||
fn listen(
|
fn listen(
|
||||||
@ -378,10 +492,13 @@ impl AshmemService {
|
|||||||
let (lock, cvar) = &**syncpair;
|
let (lock, cvar) = &**syncpair;
|
||||||
*lock.lock().unwrap() = true;
|
*lock.lock().unwrap() = true;
|
||||||
cvar.notify_one();
|
cvar.notify_one();
|
||||||
|
|
||||||
|
println!("Error in ShMem Worker");
|
||||||
return Err(Error::Unknown(
|
return Err(Error::Unknown(
|
||||||
"The server appears to already be running. We are probably a client".to_string(),
|
"The server appears to already be running. We are probably a client".to_string(),
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut poll_fds: Vec<PollFd> = vec![PollFd::new(
|
let mut poll_fds: Vec<PollFd> = vec![PollFd::new(
|
||||||
listener.as_raw_fd(),
|
listener.as_raw_fd(),
|
||||||
PollFlags::POLLIN | PollFlags::POLLRDNORM | PollFlags::POLLRDBAND,
|
PollFlags::POLLIN | PollFlags::POLLRDNORM | PollFlags::POLLRDBAND,
|
||||||
@ -418,7 +535,7 @@ impl AshmemService {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
let (stream, addr) = match listener.accept_unix_addr() {
|
let (stream, _addr) = match listener.accept_unix_addr() {
|
||||||
Ok(stream_val) => stream_val,
|
Ok(stream_val) => stream_val,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error accepting client: {:?}", e);
|
println!("Error accepting client: {:?}", e);
|
||||||
@ -426,17 +543,21 @@ impl AshmemService {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("Recieved connection from {:?}", addr);
|
// println!("Recieved connection from {:?}", addr);
|
||||||
let pollfd = PollFd::new(
|
let pollfd = PollFd::new(
|
||||||
stream.as_raw_fd(),
|
stream.as_raw_fd(),
|
||||||
PollFlags::POLLIN | PollFlags::POLLRDNORM | PollFlags::POLLRDBAND,
|
PollFlags::POLLIN | PollFlags::POLLRDNORM | PollFlags::POLLRDBAND,
|
||||||
);
|
);
|
||||||
poll_fds.push(pollfd);
|
poll_fds.push(pollfd);
|
||||||
let client = AshmemClient::new(stream);
|
let client = SharedShMemClient::new(stream);
|
||||||
let client_id = client.stream.as_raw_fd();
|
let client_id = client.stream.as_raw_fd();
|
||||||
self.clients.insert(client_id, client);
|
self.clients.insert(client_id, client);
|
||||||
match self.handle_client(client_id) {
|
match self.handle_client(client_id) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
|
Err(Error::ShuttingDown) => {
|
||||||
|
println!("Shutting down");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
dbg!("Ignoring failed read from client", e);
|
dbg!("Ignoring failed read from client", e);
|
||||||
}
|
}
|
||||||
@ -449,3 +570,14 @@ impl AshmemService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<SP> Drop for ServedShMemServiceWorker<SP>
|
||||||
|
where
|
||||||
|
SP: ShMemProvider,
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// try to remove the file from fs, and ignore errors.
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
drop(fs::remove_file(&UNIX_SERVER_NAME));
|
||||||
|
}
|
||||||
|
}
|
@ -7,38 +7,58 @@ use core::{
|
|||||||
fmt::{self, Debug, Display},
|
fmt::{self, Debug, Display},
|
||||||
mem::ManuallyDrop,
|
mem::ManuallyDrop,
|
||||||
};
|
};
|
||||||
|
#[cfg(all(feature = "std", unix, not(target_os = "android")))]
|
||||||
|
pub use unix_shmem::{MmapShMem, MmapShMemProvider};
|
||||||
|
|
||||||
#[cfg(all(feature = "std", unix))]
|
#[cfg(all(feature = "std", unix))]
|
||||||
pub use unix_shmem::{UnixShMem, UnixShMemProvider};
|
pub use unix_shmem::{UnixShMem, UnixShMemProvider};
|
||||||
/// The default [`ShMemProvider`] for this os.
|
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
#[cfg(all(feature = "std", unix))]
|
#[cfg(all(feature = "std", unix))]
|
||||||
pub type OsShMemProvider = UnixShMemProvider;
|
pub use crate::bolts::os::unix_shmem_server::{ServedShMemProvider, ShMemService};
|
||||||
/// The default [`ShMem`] for this os.
|
|
||||||
#[cfg(all(feature = "std", unix))]
|
|
||||||
pub type OsShMem = UnixShMem;
|
|
||||||
|
|
||||||
#[cfg(all(windows, feature = "std"))]
|
#[cfg(all(windows, feature = "std"))]
|
||||||
pub use win32_shmem::{Win32ShMem, Win32ShMemProvider};
|
pub use win32_shmem::{Win32ShMem, Win32ShMemProvider};
|
||||||
#[cfg(all(windows, feature = "std"))]
|
#[cfg(all(windows, feature = "std"))]
|
||||||
pub type OsShMemProvider = Win32ShMemProvider;
|
pub type StdShMemProvider = Win32ShMemProvider;
|
||||||
#[cfg(all(windows, feature = "std"))]
|
#[cfg(all(windows, feature = "std"))]
|
||||||
pub type OsShMem = Win32ShMem;
|
pub type StdShMem = Win32ShMem;
|
||||||
|
|
||||||
use crate::Error;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "android", feature = "std"))]
|
#[cfg(all(target_os = "android", feature = "std"))]
|
||||||
use crate::bolts::os::ashmem_server::ServedShMemProvider;
|
pub type StdShMemProvider = RcShMemProvider<ServedShMemProvider<AshmemShMemProvider>>;
|
||||||
#[cfg(all(target_os = "android", feature = "std"))]
|
#[cfg(all(target_os = "android", feature = "std"))]
|
||||||
pub type StdShMemProvider = RcShMemProvider<ServedShMemProvider>;
|
pub type StdShMem = RcShMem<ServedShMem<AshmemShMem>>;
|
||||||
#[cfg(all(target_os = "android", feature = "std"))]
|
#[cfg(all(target_os = "android", feature = "std"))]
|
||||||
pub type StdShMem = RcShMem<ServedShMemProvider>;
|
pub type StdShMemService = ShMemService<AshmemShMemProvider>;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "std", any(target_os = "ios", target_os = "macos")))]
|
||||||
|
pub type StdShMemProvider = RcShMemProvider<ServedShMemProvider<MmapShMemProvider>>;
|
||||||
|
#[cfg(all(feature = "std", any(target_os = "ios", target_os = "macos")))]
|
||||||
|
pub type StdShMem = RcShMem<ServedShMem<MmapShMem>>;
|
||||||
|
#[cfg(all(feature = "std", any(target_os = "ios", target_os = "macos")))]
|
||||||
|
pub type StdShMemService = ShMemService<MmapShMemProvider>;
|
||||||
|
|
||||||
/// The default [`ShMemProvider`] for this os.
|
/// The default [`ShMemProvider`] for this os.
|
||||||
#[cfg(all(feature = "std", not(target_os = "android")))]
|
#[cfg(all(
|
||||||
pub type StdShMemProvider = OsShMemProvider;
|
feature = "std",
|
||||||
/// The default [`ShMem`] for this os.
|
unix,
|
||||||
#[cfg(all(feature = "std", not(target_os = "android")))]
|
not(any(target_os = "android", target_os = "ios", target_os = "macos"))
|
||||||
pub type StdShMem = OsShMem;
|
))]
|
||||||
|
pub type StdShMemProvider = UnixShMemProvider;
|
||||||
|
/// The default [`ShMemProvider`] for this os.
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "std",
|
||||||
|
unix,
|
||||||
|
not(any(target_os = "android", target_os = "ios", target_os = "macos"))
|
||||||
|
))]
|
||||||
|
pub type StdShMem = UnixShMem;
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
not(any(target_os = "android", target_os = "macos", target_os = "ios")),
|
||||||
|
not(feature = "std")
|
||||||
|
))]
|
||||||
|
pub type StdShMemService = DummyShMemService;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@ -47,6 +67,12 @@ use std::{
|
|||||||
env,
|
env,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(all(
|
||||||
|
unix,
|
||||||
|
feature = "std",
|
||||||
|
any(target_os = "ios", target_os = "macos", target_os = "android")
|
||||||
|
))]
|
||||||
|
use super::os::unix_shmem_server::ServedShMem;
|
||||||
#[cfg(all(unix, feature = "std"))]
|
#[cfg(all(unix, feature = "std"))]
|
||||||
use crate::bolts::os::pipes::Pipe;
|
use crate::bolts::os::pipes::Pipe;
|
||||||
#[cfg(all(unix, feature = "std"))]
|
#[cfg(all(unix, feature = "std"))]
|
||||||
@ -436,14 +462,13 @@ where
|
|||||||
/// A Unix sharedmem implementation.
|
/// A Unix sharedmem implementation.
|
||||||
///
|
///
|
||||||
/// On Android, this is partially reused to wrap [`unix_shmem::ashmem::AshmemShMem`],
|
/// On Android, this is partially reused to wrap [`unix_shmem::ashmem::AshmemShMem`],
|
||||||
/// Although for an [`unix_shmem::ashmem::AshmemShMemProvider`] using a unix domain socket
|
/// Although for an [`unix_shmem::ashmem::ServedShMemProvider`] using a unix domain socket
|
||||||
/// Is needed on top.
|
/// Is needed on top.
|
||||||
#[cfg(all(unix, feature = "std"))]
|
#[cfg(all(unix, feature = "std"))]
|
||||||
pub mod unix_shmem {
|
pub mod unix_shmem {
|
||||||
|
|
||||||
/// Shared memory provider for Android, allocating and forwarding maps over unix domain sockets.
|
/// Shared memory provider for Android, allocating and forwarding maps over unix domain sockets.
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
pub type UnixShMemProvider = ashmem::AshmemShMemProvider;
|
pub type UnixShMemProvider = ashmem::ServedShMemProvider;
|
||||||
/// Shared memory for Android
|
/// Shared memory for Android
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
pub type UnixShMem = ashmem::AshmemShMem;
|
pub type UnixShMem = ashmem::AshmemShMem;
|
||||||
@ -454,10 +479,22 @@ pub mod unix_shmem {
|
|||||||
#[cfg(not(target_os = "android"))]
|
#[cfg(not(target_os = "android"))]
|
||||||
pub type UnixShMem = ashmem::AshmemShMem;
|
pub type UnixShMem = ashmem::AshmemShMem;
|
||||||
|
|
||||||
|
/// Mmap [`ShMem`] for Unix
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
|
pub use default::MmapShMem;
|
||||||
|
/// Mmap [`ShMemProvider`] for Unix
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
|
pub use default::MmapShMemProvider;
|
||||||
|
|
||||||
#[cfg(all(unix, feature = "std", not(target_os = "android")))]
|
#[cfg(all(unix, feature = "std", not(target_os = "android")))]
|
||||||
mod default {
|
mod default {
|
||||||
use core::{ptr, slice};
|
|
||||||
use libc::{c_int, c_long, c_uchar, c_uint, c_ulong, c_ushort, c_void};
|
use core::{convert::TryInto, ptr, slice};
|
||||||
|
use libc::{
|
||||||
|
c_int, c_long, c_uchar, c_uint, c_ulong, c_ushort, c_void, close, ftruncate, mmap,
|
||||||
|
munmap, perror, shm_open, shm_unlink,
|
||||||
|
};
|
||||||
|
use std::{io::Write, process, ptr::null_mut};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::shmem::{ShMem, ShMemId, ShMemProvider},
|
bolts::shmem::{ShMem, ShMemId, ShMemProvider},
|
||||||
@ -502,6 +539,197 @@ pub mod unix_shmem {
|
|||||||
fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void;
|
fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAX_MMAP_FILENAME_LEN: usize = 256;
|
||||||
|
|
||||||
|
/// Mmap-based The sharedmap impl for unix using [`shm_open`] and [`mmap`].
|
||||||
|
/// Default on `MacOS` and `iOS`, where we need a central point to unmap
|
||||||
|
/// shared mem segments for dubious Mach kernel reasons.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MmapShMem {
|
||||||
|
/// The path of this shared memory segment.
|
||||||
|
/// None in case we didn't [`shm_open`] this ourselves, but someone sent us the FD.
|
||||||
|
filename_path: Option<[u8; MAX_MMAP_FILENAME_LEN]>,
|
||||||
|
/// The size of this map
|
||||||
|
map_size: usize,
|
||||||
|
/// The map ptr
|
||||||
|
map: *mut u8,
|
||||||
|
/// The shmem id, containing the file descriptor and size, to send over the wire
|
||||||
|
id: ShMemId,
|
||||||
|
/// The file descriptor of the shmem
|
||||||
|
shm_fd: c_int,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MmapShMem {
|
||||||
|
pub fn new(map_size: usize, shmem_ctr: usize) -> 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(),
|
||||||
|
shmem_ctr
|
||||||
|
)?;
|
||||||
|
|
||||||
|
/* create the shared memory segment as if it was a file */
|
||||||
|
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 {:?}",
|
||||||
|
shmem_ctr
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* configure the size of the shared memory segment */
|
||||||
|
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 {:?}",
|
||||||
|
shmem_ctr
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* map the shared memory segment to the address space of the process */
|
||||||
|
let map = mmap(
|
||||||
|
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 {:?}",
|
||||||
|
shmem_ctr
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
filename_path: Some(filename_path),
|
||||||
|
map: map as *mut u8,
|
||||||
|
map_size,
|
||||||
|
shm_fd,
|
||||||
|
id: ShMemId::from_string(&format!("{}", shm_fd)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_id_and_size(id: ShMemId, map_size: usize) -> Result<Self, Error> {
|
||||||
|
unsafe {
|
||||||
|
let shm_fd: i32 = id.to_string().parse().unwrap();
|
||||||
|
|
||||||
|
/* map the shared memory segment to the address space of the process */
|
||||||
|
let map = mmap(
|
||||||
|
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)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [`ShMemProvider`] which uses `shmget`/`shmat`/`shmctl` to provide shared memory mappings.
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MmapShMemProvider {
|
||||||
|
current_map_id: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for MmapShMemProvider {}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
impl Default for MmapShMemProvider {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement [`ShMemProvider`] for [`UnixShMemProvider`].
|
||||||
|
#[cfg(unix)]
|
||||||
|
impl ShMemProvider for MmapShMemProvider {
|
||||||
|
type Mem = MmapShMem;
|
||||||
|
|
||||||
|
fn new() -> Result<Self, Error> {
|
||||||
|
Ok(Self { current_map_id: 0 })
|
||||||
|
}
|
||||||
|
fn new_map(&mut self, map_size: usize) -> Result<Self::Mem, Error> {
|
||||||
|
self.current_map_id += 1;
|
||||||
|
MmapShMem::new(map_size, self.current_map_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result<Self::Mem, Error> {
|
||||||
|
MmapShMem::from_id_and_size(id, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShMem for MmapShMem {
|
||||||
|
fn id(&self) -> ShMemId {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.map_size
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map(&self) -> &[u8] {
|
||||||
|
unsafe { slice::from_raw_parts(self.map, self.map_size) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe { slice::from_raw_parts_mut(self.map, self.map_size) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for MmapShMem {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
if self.map.is_null() {
|
||||||
|
panic!("Map should never be null for MmapShMem (on Drop)");
|
||||||
|
}
|
||||||
|
|
||||||
|
munmap(self.map as *mut _, self.map_size);
|
||||||
|
self.map = ptr::null_mut();
|
||||||
|
|
||||||
|
if self.shm_fd == -1 {
|
||||||
|
panic!("FD should never be -1 for MmapShMem (on Drop)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// None in case we didn't [`shm_open`] this ourselves, but someone sent us the FD.
|
||||||
|
if let Some(filename_path) = self.filename_path {
|
||||||
|
shm_unlink(filename_path.as_ptr() as *const _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The default sharedmap impl for unix using shmctl & shmget
|
/// The default sharedmap impl for unix using shmctl & shmget
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CommonUnixShMem {
|
pub struct CommonUnixShMem {
|
||||||
@ -514,7 +742,11 @@ pub mod unix_shmem {
|
|||||||
/// Create a new shared memory mapping, using shmget/shmat
|
/// Create a new shared memory mapping, using shmget/shmat
|
||||||
pub fn new(map_size: usize) -> Result<Self, Error> {
|
pub fn new(map_size: usize) -> Result<Self, Error> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let os_id = shmget(0, map_size as c_ulong, 0o1000 | 0o2000 | 0o600);
|
let os_id = shmget(
|
||||||
|
libc::IPC_PRIVATE,
|
||||||
|
map_size as c_ulong,
|
||||||
|
libc::IPC_CREAT | libc::IPC_EXCL | libc::SHM_R | libc::SHM_W,
|
||||||
|
);
|
||||||
|
|
||||||
if os_id < 0_i32 {
|
if os_id < 0_i32 {
|
||||||
return Err(Error::Unknown(format!("Failed to allocate a shared mapping of size {} - check OS limits (i.e shmall, shmmax)", map_size)));
|
return Err(Error::Unknown(format!("Failed to allocate a shared mapping of size {} - check OS limits (i.e shmall, shmmax)", map_size)));
|
||||||
@ -523,7 +755,7 @@ pub mod unix_shmem {
|
|||||||
let map = shmat(os_id, ptr::null(), 0) as *mut c_uchar;
|
let map = shmat(os_id, ptr::null(), 0) as *mut c_uchar;
|
||||||
|
|
||||||
if map as c_int == -1 || map.is_null() {
|
if map as c_int == -1 || map.is_null() {
|
||||||
shmctl(os_id, 0, ptr::null_mut());
|
shmctl(os_id, libc::IPC_RMID, ptr::null_mut());
|
||||||
return Err(Error::Unknown(
|
return Err(Error::Unknown(
|
||||||
"Failed to map the shared mapping".to_string(),
|
"Failed to map the shared mapping".to_string(),
|
||||||
));
|
));
|
||||||
@ -543,7 +775,7 @@ pub mod unix_shmem {
|
|||||||
let id_int: i32 = id.into();
|
let id_int: i32 = id.into();
|
||||||
let map = shmat(id_int, ptr::null(), 0) as *mut c_uchar;
|
let map = shmat(id_int, ptr::null(), 0) as *mut c_uchar;
|
||||||
|
|
||||||
if map == usize::MAX as *mut c_void as *mut c_uchar || map.is_null() {
|
if map.is_null() || map == null_mut::<c_uchar>().wrapping_sub(1) {
|
||||||
return Err(Error::Unknown(
|
return Err(Error::Unknown(
|
||||||
"Failed to map the shared mapping".to_string(),
|
"Failed to map the shared mapping".to_string(),
|
||||||
));
|
));
|
||||||
@ -579,7 +811,7 @@ pub mod unix_shmem {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let id_int: i32 = self.id.into();
|
let id_int: i32 = self.id.into();
|
||||||
shmctl(id_int, 0, ptr::null_mut());
|
shmctl(id_int, libc::IPC_RMID, ptr::null_mut());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -999,12 +1231,28 @@ pub mod win32_shmem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A `ShMemService` dummy, that does nothing on start.
|
||||||
|
/// Drop in for targets that don't need a server for ref counting and page creation.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DummyShMemService;
|
||||||
|
|
||||||
|
impl DummyShMemService {
|
||||||
|
/// Create a new [`DummyShMemService`] that does nothing.
|
||||||
|
/// Useful only to have the same API for [`StdShMemService`] on Operating Systems that don't need it.
|
||||||
|
#[inline]
|
||||||
|
pub fn start() -> Result<Self, Error> {
|
||||||
|
Ok(Self {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
/// A cursor around [`ShMem`] that immitates [`std::io::Cursor`]. Notably, this implements [`Write`] for [`ShMem`] in std environments.
|
/// A cursor around [`ShMem`] that immitates [`std::io::Cursor`]. Notably, this implements [`Write`] for [`ShMem`] in std environments.
|
||||||
pub struct ShMemCursor<T: ShMem> {
|
pub struct ShMemCursor<T: ShMem> {
|
||||||
inner: T,
|
inner: T,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl<T: ShMem> ShMemCursor<T> {
|
impl<T: ShMem> ShMemCursor<T> {
|
||||||
pub fn new(shmem: T) -> Self {
|
pub fn new(shmem: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -1083,3 +1331,22 @@ impl<T: ShMem> std::io::Seek for ShMemCursor<T> {
|
|||||||
Ok(effective_new_pos)
|
Ok(effective_new_pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serial_test::serial;
|
||||||
|
|
||||||
|
use crate::bolts::shmem::{ShMem, ShMemProvider, StdShMemProvider, StdShMemService};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_shmem_service() {
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
let service = StdShMemService::start().unwrap();
|
||||||
|
let mut provider = StdShMemProvider::new().unwrap();
|
||||||
|
let mut map = provider.new_map(1024).unwrap();
|
||||||
|
map.map_mut()[0] = 1;
|
||||||
|
assert!(map.map()[0] == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -111,7 +111,8 @@ where
|
|||||||
if size_of::<StateShMemContent>() + serialized.len() > self.shmem.len() {
|
if size_of::<StateShMemContent>() + serialized.len() > self.shmem.len() {
|
||||||
// generate a filename
|
// generate a filename
|
||||||
let mut hasher = AHasher::new_with_keys(0, 0);
|
let mut hasher = AHasher::new_with_keys(0, 0);
|
||||||
hasher.write(&serialized[serialized.len() - 1024..]);
|
// Using the last few k as randomness for a filename, hoping it's unique.
|
||||||
|
hasher.write(&serialized[serialized.len().saturating_sub(4096)..]);
|
||||||
|
|
||||||
let filename = format!("{:016x}.libafl_state", hasher.finish());
|
let filename = format!("{:016x}.libafl_state", hasher.finish());
|
||||||
let tmpfile = temp_dir().join(&filename);
|
let tmpfile = temp_dir().join(&filename);
|
||||||
@ -238,15 +239,20 @@ where
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
use serial_test::serial;
|
||||||
|
|
||||||
use crate::bolts::{
|
use crate::bolts::{
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider, StdShMemService},
|
||||||
staterestore::StateRestorer,
|
staterestore::StateRestorer,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[serial]
|
||||||
fn test_state_restore() {
|
fn test_state_restore() {
|
||||||
const TESTMAP_SIZE: usize = 1024;
|
const TESTMAP_SIZE: usize = 1024;
|
||||||
|
|
||||||
|
let _service = StdShMemService::start().unwrap();
|
||||||
|
|
||||||
let mut shmem_provider = StdShMemProvider::new().unwrap();
|
let mut shmem_provider = StdShMemProvider::new().unwrap();
|
||||||
let shmem = shmem_provider.new_map(TESTMAP_SIZE).unwrap();
|
let shmem = shmem_provider.new_map(TESTMAP_SIZE).unwrap();
|
||||||
let mut state_restorer = StateRestorer::<StdShMemProvider>::new(shmem);
|
let mut state_restorer = StateRestorer::<StdShMemProvider>::new(shmem);
|
||||||
|
@ -15,7 +15,7 @@ use std::net::{SocketAddr, ToSocketAddrs};
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use crate::bolts::{
|
use crate::bolts::{
|
||||||
llmp::{LlmpClient, LlmpConnection},
|
llmp::{LlmpClient, LlmpConnection},
|
||||||
shmem::StdShMemProvider,
|
shmem::{StdShMemProvider, StdShMemService},
|
||||||
staterestore::StateRestorer,
|
staterestore::StateRestorer,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ use crate::bolts::os::startable_self;
|
|||||||
use crate::bolts::os::{fork, ForkResult};
|
use crate::bolts::os::{fork, ForkResult};
|
||||||
|
|
||||||
#[cfg(all(target_os = "android", feature = "std"))]
|
#[cfg(all(target_os = "android", feature = "std"))]
|
||||||
use crate::bolts::os::ashmem_server::AshmemService;
|
use crate::bolts::os::unix_shmem_server::ShMemService;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
@ -689,8 +689,7 @@ where
|
|||||||
OT: ObserversTuple<I, S> + serde::de::DeserializeOwned,
|
OT: ObserversTuple<I, S> + serde::de::DeserializeOwned,
|
||||||
S: DeserializeOwned,
|
S: DeserializeOwned,
|
||||||
{
|
{
|
||||||
#[cfg(target_os = "android")]
|
let _service = StdShMemService::start().expect("Error starting ShMem Service");
|
||||||
AshmemService::start().expect("Error starting Ashmem Service");
|
|
||||||
|
|
||||||
RestartingMgr::builder()
|
RestartingMgr::builder()
|
||||||
.shmem_provider(StdShMemProvider::new()?)
|
.shmem_provider(StdShMemProvider::new()?)
|
||||||
@ -929,11 +928,13 @@ where
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use serial_test::serial;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::{
|
bolts::{
|
||||||
llmp::{LlmpClient, LlmpSharedMap},
|
llmp::{LlmpClient, LlmpSharedMap},
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider, StdShMemService},
|
||||||
staterestore::StateRestorer,
|
staterestore::StateRestorer,
|
||||||
tuples::tuple_list,
|
tuples::tuple_list,
|
||||||
},
|
},
|
||||||
@ -949,7 +950,10 @@ mod tests {
|
|||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[serial]
|
||||||
fn test_mgr_state_restore() {
|
fn test_mgr_state_restore() {
|
||||||
|
let _service = StdShMemService::start().unwrap();
|
||||||
|
|
||||||
let rand = StdRand::with_seed(0);
|
let rand = StdRand::with_seed(0);
|
||||||
|
|
||||||
let mut corpus = InMemoryCorpus::<BytesInput>::new();
|
let mut corpus = InMemoryCorpus::<BytesInput>::new();
|
||||||
|
@ -3,6 +3,7 @@ use core::marker::PhantomData;
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::{process::Child, time::Duration};
|
use std::{process::Child, time::Duration};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
use crate::{
|
use crate::{
|
||||||
executors::{Executor, ExitKind, HasObservers},
|
executors::{Executor, ExitKind, HasObservers},
|
||||||
inputs::Input,
|
inputs::Input,
|
||||||
|
@ -614,9 +614,11 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use serial_test::serial;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::{
|
bolts::{
|
||||||
shmem::{ShMem, ShMemProvider, StdShMemProvider},
|
shmem::{ShMem, ShMemProvider, StdShMemProvider, StdShMemService},
|
||||||
tuples::tuple_list,
|
tuples::tuple_list,
|
||||||
},
|
},
|
||||||
executors::ForkserverExecutor,
|
executors::ForkserverExecutor,
|
||||||
@ -625,11 +627,14 @@ mod tests {
|
|||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
#[test]
|
#[test]
|
||||||
|
#[serial]
|
||||||
fn test_forkserver() {
|
fn test_forkserver() {
|
||||||
const MAP_SIZE: usize = 65536;
|
const MAP_SIZE: usize = 65536;
|
||||||
let bin = "echo";
|
let bin = "echo";
|
||||||
let args = vec![String::from("@@")];
|
let args = vec![String::from("@@")];
|
||||||
|
|
||||||
|
let _service = StdShMemService::start().unwrap();
|
||||||
|
|
||||||
let mut shmem = StdShMemProvider::new()
|
let mut shmem = StdShMemProvider::new()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.new_map(MAP_SIZE as usize)
|
.new_map(MAP_SIZE as usize)
|
||||||
|
@ -19,7 +19,9 @@ pub use shadow::ShadowExecutor;
|
|||||||
pub mod with_observers;
|
pub mod with_observers;
|
||||||
pub use with_observers::WithObservers;
|
pub use with_observers::WithObservers;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
pub mod command;
|
pub mod command;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
pub use command::CommandExecutor;
|
pub use command::CommandExecutor;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -107,7 +107,12 @@ impl<'a> FridaHelper<'a> for FridaInstrumentationHelper<'a> {
|
|||||||
self.asan_runtime.register_thread();
|
self.asan_runtime.register_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
|
fn pre_exec<I: Input + HasTargetBytes>(&mut self, _input: &I) {}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
fn pre_exec<I: Input + HasTargetBytes>(&mut self, input: &I) {
|
fn pre_exec<I: Input + HasTargetBytes>(&mut self, input: &I) {
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
let target_bytes = input.target_bytes();
|
let target_bytes = input.target_bytes();
|
||||||
let slice = target_bytes.as_slice();
|
let slice = target_bytes.as_slice();
|
||||||
//println!("target_bytes: {:#x}: {:02x?}", slice.as_ptr() as usize, slice);
|
//println!("target_bytes: {:#x}: {:02x?}", slice.as_ptr() as usize, slice);
|
||||||
|
@ -71,7 +71,7 @@ impl<'a, H> InMemoryBytesCoverageSugar<'a, H>
|
|||||||
where
|
where
|
||||||
H: FnMut(&[u8]),
|
H: FnMut(&[u8]),
|
||||||
{
|
{
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines, clippy::similar_names)]
|
||||||
pub fn run(&mut self) {
|
pub fn run(&mut self) {
|
||||||
let conf = self
|
let conf = self
|
||||||
.configuration
|
.configuration
|
||||||
|
@ -7,7 +7,7 @@ use libafl::{
|
|||||||
current_nanos,
|
current_nanos,
|
||||||
launcher::Launcher,
|
launcher::Launcher,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider, StdShMemService},
|
||||||
tuples::{tuple_list, Merge},
|
tuples::{tuple_list, Merge},
|
||||||
},
|
},
|
||||||
corpus::{
|
corpus::{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
echo "Warning: this script is not a proper fix to do LLMP fuzzing." \
|
echo "Warning: this script is not a proper fix to do LLMP fuzzing." \
|
||||||
"Instead, run `afl-persistent-config` with SIP disabled."
|
'Instead, run `afl-persistent-config` with SIP disabled.'
|
||||||
|
|
||||||
sudo sysctl -w kern.sysv.shmmax=524288000
|
sudo sysctl -w kern.sysv.shmmax=524288000
|
||||||
sudo sysctl -w kern.sysv.shmmin=1
|
sudo sysctl -w kern.sysv.shmmin=1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user