Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/fspy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ fspy_seccomp_unotify = { workspace = true, features = ["supervisor"] }
nix = { workspace = true, features = ["uio"] }
tokio = { workspace = true, features = ["bytes"] }

[target.'cfg(all(unix, not(target_env = "musl")))'.dependencies]
[target.'cfg(all(unix, all(not(target_env = "musl"), not(target_os = "android"))))'.dependencies]
fspy_preload_unix = { workspace = true }

[target.'cfg(unix)'.dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/fspy/src/artifact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ macro_rules! artifact {
pub use artifact;

impl Artifact {
#[cfg(not(target_os = "linux"))]
#[cfg(all(not(target_os = "android"), not(target_os = "linux")))]
pub const fn new(name: &'static str, content: &'static [u8], hash: &'static str) -> Self {
Self { name, content, hash }
}
Expand Down
4 changes: 2 additions & 2 deletions crates/fspy/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#![cfg_attr(target_os = "windows", feature(windows_process_extensions_main_thread_handle))]
#![feature(once_cell_try)]
#![cfg_attr(not(target_os = "android"), feature(once_cell_try))]

// Persist the injected DLL/shared library somewhere in the filesystem.
// Not needed on musl (seccomp-only tracking).
#[cfg(not(target_env = "musl"))]
#[cfg(all(not(target_os = "android"), not(target_env = "musl")))]
mod artifact;

pub mod error;
Expand Down
16 changes: 9 additions & 7 deletions crates/fspy/src/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::{io, path::Path};

#[cfg(target_os = "linux")]
use fspy_seccomp_unotify::supervisor::supervise;
#[cfg(not(target_env = "musl"))]
#[cfg(all(not(target_os = "android"), not(target_env = "musl")))]
use fspy_shared::ipc::NativeStr;
use fspy_shared::ipc::{PathAccess, channel::channel};
#[cfg(target_os = "macos")]
Expand Down Expand Up @@ -36,20 +36,22 @@ pub struct SpyImpl {
#[cfg(target_os = "macos")]
artifacts: Artifacts,

#[cfg(not(target_env = "musl"))]
#[cfg(all(not(target_os = "android"), not(target_env = "musl")))]
preload_path: Box<NativeStr>,
}

#[cfg(not(target_env = "musl"))]
#[cfg(all(not(target_os = "android"), not(target_env = "musl")))]
const PRELOAD_CDYLIB_BINARY: &[u8] = include_bytes!(env!("CARGO_CDYLIB_FILE_FSPY_PRELOAD_UNIX"));

impl SpyImpl {
/// Initialize the fs access spy by writing the preload library on disk.
///
/// On musl targets, we don't build a preload library —
/// only seccomp-based tracking is used.
pub fn init_in(#[cfg_attr(target_env = "musl", allow(unused))] dir: &Path) -> io::Result<Self> {
#[cfg(not(target_env = "musl"))]
pub fn init_in(
#[cfg_attr(any(target_os = "android", target_env = "musl"), allow(unused))] dir: &Path,
) -> io::Result<Self> {
#[cfg(all(not(target_os = "android"), not(target_env = "musl")))]
let preload_path = {
use const_format::formatcp;
use xxhash_rust::const_xxh3::xxh3_128;
Expand All @@ -67,7 +69,7 @@ impl SpyImpl {
};

Ok(Self {
#[cfg(not(target_env = "musl"))]
#[cfg(all(not(target_os = "android"), not(target_env = "musl")))]
preload_path,
#[cfg(target_os = "macos")]
artifacts: {
Expand Down Expand Up @@ -98,7 +100,7 @@ impl SpyImpl {
#[cfg(target_os = "macos")]
artifacts: self.artifacts.clone(),

#[cfg(not(target_env = "musl"))]
#[cfg(all(not(target_os = "android"), not(target_env = "musl")))]
preload_path: self.preload_path.clone(),

#[cfg(target_os = "linux")]
Expand Down
13 changes: 8 additions & 5 deletions crates/fspy_preload_unix/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
// On musl targets, fspy_preload_unix is not needed since we can track accesses via seccomp-only.
// Compile as an empty crate to avoid build failures from missing libc symbols.
#![cfg_attr(not(target_env = "musl"), feature(c_variadic))]
#![cfg_attr(not(target_os = "android"), not(target_env = "musl"), feature(c_variadic))]

#[cfg(all(unix, not(target_env = "musl")))]
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
mod client;
#[cfg(all(unix, not(target_env = "musl")))]

#[cfg(all(target_os = "linux", not(target_env = "musl")))]
mod interceptions;
#[cfg(all(unix, not(target_env = "musl")))]

#[cfg(all(target_os = "linux", not(target_env = "musl")))]
mod libc;
#[cfg(all(unix, not(target_env = "musl")))]

#[cfg(all(target_os = "linux", not(target_env = "musl")))]
mod macros;
2 changes: 1 addition & 1 deletion crates/fspy_seccomp_unotify/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2024"
publish = false

[target.'cfg(target_os = "linux")'.dependencies]
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies]
bincode = { workspace = true }
libc = { workspace = true }
nix = { workspace = true, features = ["process", "fs", "poll", "socket", "uio"] }
Expand Down
2 changes: 1 addition & 1 deletion crates/fspy_seccomp_unotify/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![cfg(target_os = "linux")]
#![cfg(any(target_os = "android", target_os = "linux"))]

#[cfg(any(feature = "supervisor", feature = "target"))]
mod bindings;
Expand Down
13 changes: 13 additions & 0 deletions crates/fspy_seccomp_unotify/src/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ use std::{
};

use libc::sock_filter;
#[cfg(target_os = "android")]
use libc::{PR_SET_NO_NEW_PRIVS, prctl};
#[cfg(not(target_os = "android"))]
use nix::sys::prctl::set_no_new_privs;
use passfd::FdPassingExt;

Expand All @@ -19,7 +22,17 @@ use crate::{bindings::install_unotify_filter, payload::SeccompPayload};
/// Returns an error if setting no-new-privs fails, the filter cannot be installed,
/// or the IPC socket communication fails.
pub fn install_target(payload: &SeccompPayload) -> nix::Result<()> {
#[cfg(not(target_os = "android"))]
set_no_new_privs()?;

#[cfg(target_os = "android")]
{
let ret = unsafe { prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) };
if ret != 0 {
return Err(nix::Error::last());
}
}

let sock_filters =
payload.filter.0.iter().copied().map(sock_filter::from).collect::<Vec<sock_filter>>();
let notify_fd = install_unotify_filter(&sock_filters)?;
Expand Down
8 changes: 7 additions & 1 deletion crates/fspy_shared/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@ bincode = { workspace = true }
bitflags = { workspace = true }
bstr = { workspace = true }
bytemuck = { workspace = true, features = ["must_cast", "derive"] }
shared_memory = { workspace = true, features = ["logging"] }
thiserror = { workspace = true }
tracing = { workspace = true }
uuid = { workspace = true, features = ["v4"] }

[target.'cfg(not(target_os = "android"))'.dependencies]
shared_memory = { workspace = true, features = ["logging"] }

[target.'cfg(target_os = "android")'.dependencies]
memmap2 = "0.9.10"
memfd = "0.6"

[target.'cfg(target_os = "windows")'.dependencies]
bytemuck = { workspace = true }
os_str_bytes = { workspace = true }
Expand Down
29 changes: 29 additions & 0 deletions crates/fspy_shared/src/ipc/channel/backend/memfd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use std::{fs::File, io, os::unix::io::AsRawFd, sync::Arc};

use memfd::MemfdOptions;
use memmap2::{MmapMut, MmapOptions};

pub struct Shm {
pub mmap: MmapMut,
pub fd_path: Arc<str>,
}

pub fn create(size: usize) -> io::Result<Shm> {
let memfd = MemfdOptions::default().create("ipc").map_err(io::Error::other)?;
memfd.as_file().set_len(size as u64)?;

let fd = memfd.as_file().as_raw_fd();
let fd_path = format!("/proc/self/fd/{fd}");

let mmap = unsafe { MmapOptions::new().len(size).map_mut(memfd.as_file())? };

Ok(Shm { mmap, fd_path: fd_path.into() })
}

pub fn open(shm_id: &str, size: usize) -> io::Result<memmap2::MmapMut> {
let file = File::open(shm_id)?;

let mmap = unsafe { memmap2::MmapOptions::new().len(size).map_mut(&file)? };

Ok(mmap)
}
10 changes: 10 additions & 0 deletions crates/fspy_shared/src/ipc/channel/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#[cfg(target_os = "android")]
mod memfd;

#[cfg(not(target_os = "android"))]
mod shmem;

#[cfg(target_os = "android")]
pub use memfd::*;
#[cfg(not(target_os = "android"))]
pub use shmem::*;
20 changes: 20 additions & 0 deletions crates/fspy_shared/src/ipc/channel/backend/shmem.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use std::io;

use shared_memory::{Shmem, ShmemConf};

pub struct Shm {
pub shm: Shmem,
pub os_id: String,
}

pub fn create(size: usize) -> io::Result<Shm> {
let shm = ShmemConf::new().size(size).create().map_err(io::Error::other)?;

let os_id = shm.get_os_id().to_string();

Ok(Shm { shm, os_id })
}

pub fn open(os_id: &str, size: usize) -> io::Result<Shmem> {
ShmemConf::new().size(size).os_id(os_id).open().map_err(io::Error::other)
}
Loading