Skip to content
Merged
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
12 changes: 12 additions & 0 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ wincode = "0.6.0"
bindgen = "0.72.1"
bitflags = "2.10.0"
brush-parser = "0.4.0"
bstr = { version = "1.12.0", default-features = false, features = ["alloc", "std"] }
bstr = { version = "1.12.0", default-features = false }
bump-scope = { version = "2", default-features = false, features = ["allocator-api2-02"] }
bumpalo = { version = "3.17.0", features = ["collections"] }
bytemuck = { version = "1.23.0", features = ["extern_crate_alloc", "must_cast"] }
Expand Down
2 changes: 1 addition & 1 deletion crates/fspy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ publish = false

[dependencies]
wincode = { workspace = true }
bstr = { workspace = true, default-features = false }
bstr = { workspace = true, features = ["alloc", "std"] }
bumpalo = { workspace = true }
derive_more = { workspace = true, features = ["debug"] }
materialized_artifact = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion crates/fspy_preload_unix/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ crate-type = ["cdylib"]
allocator-api2 = { workspace = true, features = ["alloc"] }
anyhow = { workspace = true }
wincode = { workspace = true }
bstr = { workspace = true, default-features = false }
bstr = { workspace = true, features = ["alloc", "std"] }
ctor = { workspace = true }
fspy_shared = { workspace = true }
fspy_shared_unix = { workspace = true }
Expand Down
9 changes: 6 additions & 3 deletions crates/fspy_preload_unix/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ impl Client {
reason = "preload library intentionally uses stderr for error reporting"
)]
#[cfg(not(test))]
fn from_env() -> Self {
fn from_env(envs: impl Iterator<Item = sigsafe::env::Entry>) -> Self {
use fspy_shared_unix::payload::decode_payload_from_env;

let encoded_payload = decode_payload_from_env().unwrap();
let encoded_payload = decode_payload_from_env(envs).unwrap();

let ipc_sender = match encoded_payload.payload.ipc_channel_conf.sender() {
Ok(sender) => Some(sender),
Expand Down Expand Up @@ -155,5 +155,8 @@ pub unsafe fn handle_open(path: impl ToAbsolutePath, mode: impl ToAccessMode) {
#[cfg(not(test))]
#[ctor::ctor(unsafe)]
fn init_client() {
CLIENT.set(Client::from_env()).unwrap();
// SAFETY: the ctor only reads the process environment while constructing
// the client and does not retain borrowed environment views.
let current = unsafe { sigsafe::env::current() }.unwrap();
CLIENT.set(Client::from_env(current.envs())).unwrap();
}
2 changes: 1 addition & 1 deletion crates/fspy_shared/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ publish = false
wincode = { workspace = true, features = ["derive"] }
bitflags = { workspace = true }
bumpalo = { workspace = true }
bstr = { workspace = true }
bstr = { workspace = true, features = ["alloc", "std"] }
bytemuck = { workspace = true, features = ["must_cast", "derive"] }
fspy_shm = { workspace = true }
native_str = { workspace = true }
Expand Down
3 changes: 2 additions & 1 deletion crates/fspy_shared_unix/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ publish = false
anyhow = { workspace = true }
base64 = { workspace = true }
wincode = { workspace = true, features = ["derive"] }
bstr = { workspace = true }
bstr = { workspace = true, features = ["alloc", "std"] }
fspy_shared = { workspace = true }
nix = { workspace = true, features = ["fs"] }
sigsafe = { workspace = true }
stackalloc = { workspace = true }

[dev-dependencies]
Expand Down
25 changes: 15 additions & 10 deletions crates/fspy_shared_unix/src/payload.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::os::unix::ffi::OsStringExt;

use base64::{Engine as _, prelude::BASE64_STANDARD_NO_PAD};
use bstr::BString;
#[cfg(not(target_env = "musl"))]
Expand Down Expand Up @@ -53,19 +51,26 @@ pub fn encode_payload(payload: Payload) -> EncodedPayload {
EncodedPayload { payload, encoded_string: encoded_string.into() }
}

/// Decodes the fspy payload from the environment variable
/// Decodes the fspy payload from an iterator over environment entries.
///
/// # Errors
///
/// Returns an error if:
/// - The environment variable is not found
/// - The base64 decoding fails
/// - The deserialization fails
pub fn decode_payload_from_env() -> anyhow::Result<EncodedPayload> {
let Some(encoded_string) = std::env::var_os(PAYLOAD_ENV_NAME) else {
/// Returns an error if the payload environment variable is missing, base64
/// decoding fails, or deserialization fails.
pub fn decode_payload_from_env(
mut envs: impl Iterator<Item = sigsafe::env::Entry>,
) -> anyhow::Result<EncodedPayload> {
let Some(encoded_string) = envs.find_map(|(name, value)| {
if AsRef::<[u8]>::as_ref(name) == PAYLOAD_ENV_NAME.as_bytes() {
value.map(|value| BString::from(value.as_bytes()))
} else {
None
}
}) else {
anyhow::bail!("Environment variable '{PAYLOAD_ENV_NAME}' not found");
};
decode_payload(encoded_string.into_vec().into())

decode_payload(encoded_string)
}

fn decode_payload(encoded_string: BString) -> anyhow::Result<EncodedPayload> {
Expand Down
7 changes: 7 additions & 0 deletions crates/sigsafe/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ publish = false
[lib]
doctest = false

[dependencies]
# Only the borrowed `BStr` type is used. Keeping every feature disabled makes
# the environment iterator usable without `alloc` or `std`.
bstr = { workspace = true }

[target.'cfg(unix)'.dependencies]
rustix = { workspace = true, features = ["fs"] }

Expand All @@ -21,6 +26,8 @@ rustix = { workspace = true, features = ["param"] }
# The compile-time backend check in lib.rs needs a `linux_raw`-gated rustix
# item to reference; `runtime` is the module that has one.
[target.'cfg(target_os = "linux")'.dependencies]
# Parsing remains allocation-free and no-std; `atoi` enables `std` by default.
atoi = { version = "3.1.0", default-features = false }
rustix = { workspace = true, features = ["runtime"] }
syscalls = { workspace = true }

Expand Down
1 change: 1 addition & 0 deletions crates/sigsafe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ rustix can be built with a libc backend instead of raw syscalls, and anything in
Functions whose rustix implementation already meets the rules are re-exposed as-is; being listed in a module here is what marks a call as allowed, and the backend check above is what keeps that true.

- `mm` — anonymous memory mappings: `mmap_anonymous`, `munmap`.
- `env` — allocation-free iteration over process arguments and environment entries.
- `fs` — caller-buffer filesystem operations: `getcwd`, plus macOS `fcntl_getpath`.
- `param` — `page_size`.

Expand Down
Loading
Loading