fix: rewrite and resign SIP executables on execve/posix_spawn #3
Closed
not-matthias wants to merge 4 commits into
Closed
fix: rewrite and resign SIP executables on execve/posix_spawn #3not-matthias wants to merge 4 commits into
not-matthias wants to merge 4 commits into
Conversation
1424a61 to
9e6e169
Compare
Use `xcrun --show-sdk-path` instead of a hardcoded Xcode SDK path so the build works regardless of where the toolchain is installed, and set CARGO_TARGET_ARM64E_APPLE_DARWIN_LINKER=clang so the -Zbuild-std arm64e target links correctly.
Add a `log` backend that formats records into a fixed stack buffer (there is no allocator in a preload) and writes them to stderr, gated behind the SAMPLY_PRELOAD_DEBUG env var. Wire `logging::init()` into the preload constructor and trace the samply handoff lifecycle.
macOS strips every DYLD_* env var when the kernel execs an Apple platform
binary in a SIP-protected path (/usr/bin/env, /bin/sh, the coreutils, ...),
which unloads this preload from that process and its whole descendant tree
(the classic `env -> bash -> uv -> python` blind spot).
Interpose the execve/posix_spawn{,p} family: when the target is such a SIP
binary, transparently exec an ad-hoc re-signed copy from a writable cache
instead. The copy is not a platform binary, so dyld keeps DYLD_*, the
preload re-arms in the child, and the var keeps propagating down the tree.
Shebang interpreter chains (pnpm/pip via #!/usr/bin/env) are handled by
redirecting the interpreter and rebuilding argv. samply seeds the shared
cache dir via the SAMPLY_SIP_REDIRECT_DIR env var.
Rebuild the bundled fat dylib and per-arch binaries to include the SIP exec-redirect feature and the debug logger.
f0fe523 to
20b3819
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds a macOS SIP “exec redirect” mechanism to keep DYLD_INSERT_LIBRARIES propagation intact across SIP-protected platform-binary exec/spawn boundaries by copying + ad-hoc re-signing target binaries into a writable cache, then exec’ing the cached copies.
Changes:
- Plumb a new
SAMPLY_SIP_REDIRECT_DIRenv var from the mac task launcher to enable SIP redirect caching. - Add a new
sip_redirectpreload module that interposesexecve/posix_spawn{,p}and redirects SIP binaries + SIP-interpreter shebang scripts to re-signed cached copies. - Add preload debug logging + an integration smoke test script, and update preload build tooling/deps.
Reviewed changes
Copilot reviewed 10 out of 16 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
samply/src/mac/process_launcher.rs |
Sets up SAMPLY_SIP_REDIRECT_DIR for child processes to enable redirect caching. |
samply-mac-preload/tests/preload_smoke.sh |
Adds end-to-end smoke tests for exec/posix_spawn redirect + setuid policy. |
samply-mac-preload/src/sip_redirect/sys.rs |
New low-level libc helpers (env/argv/copy + codesign spawn). |
samply-mac-preload/src/sip_redirect/redirect.rs |
Implements redirect policy (SIP binary detection, shebang parsing, cache build). |
samply-mac-preload/src/sip_redirect/mod.rs |
Adds the actual interposes for execve / posix_spawn{,p}. |
samply-mac-preload/src/sip_redirect/buffers.rs |
Stack buffers for redirected path building + rebuilt argv storage. |
samply-mac-preload/src/logging.rs |
Adds optional stderr logging for preload debugging. |
samply-mac-preload/src/lib.rs |
Wires in logging + sip_redirect module and adds InterposeEntry::new. |
samply-mac-preload/Cargo.toml |
Adds heapless, itoa, and log dependencies for the new modules. |
samply-mac-preload/Cargo.lock |
Updates lockfile for new dependencies / format version bump. |
samply-mac-preload/build.sh |
Makes SDK discovery dynamic and adjusts arm64e linker env. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+43
to
+55
| /// `<dst>.<pid>.tmp` for an atomic build. | ||
| pub(super) fn set_tmp_path(&mut self, dst: &CPathBuffer) -> bool { | ||
| let pid = unsafe { libc::getpid() }; | ||
| let mut itoa_buf = itoa::Buffer::new(); | ||
| let pid_str = itoa_buf.format(pid as u32); | ||
|
|
||
| self.bytes.clear(); | ||
| self.bytes.extend_from_slice(dst.as_bytes()).is_ok() | ||
| && self.bytes.push(b'.').is_ok() | ||
| && self.bytes.extend_from_slice(pid_str.as_bytes()).is_ok() | ||
| && self.bytes.extend_from_slice(b".tmp").is_ok() | ||
| && self.bytes.push(0).is_ok() | ||
| } |
Comment on lines
+96
to
+110
| // Skip "#!" and leading whitespace | ||
| let mut start = 2; | ||
| while start < line.len() && line[start] == b' ' { | ||
| start += 1; | ||
| } | ||
| if start >= line.len() { | ||
| return false; | ||
| } | ||
|
|
||
| // Find end of interpreter path | ||
| let interp_end = line[start..] | ||
| .iter() | ||
| .position(|&b| b == b' ') | ||
| .map(|p| start + p) | ||
| .unwrap_or(line.len()); |
Comment on lines
+144
to
+151
| if interp_end < line.len() { | ||
| let arg_bytes = &line[interp_end + 1..]; | ||
| // Trim at the next whitespace (only a single interpreter arg is supported). | ||
| let arg_end = arg_bytes | ||
| .iter() | ||
| .position(|&b| b == b' ') | ||
| .unwrap_or(arg_bytes.len()); | ||
| let arg = &arg_bytes[..arg_end]; |
Comment on lines
+105
to
+108
| let devnull = c"/dev/null".as_ptr(); | ||
| libc::posix_spawn_file_actions_addopen(&mut fa, 1, devnull, libc::O_WRONLY, 0); | ||
| libc::posix_spawn_file_actions_addopen(&mut fa, 2, devnull, libc::O_WRONLY, 0); | ||
|
|
Comment on lines
+54
to
+57
| node_is_preloaded() { | ||
| [[ -n "$NODE" ]] || return 1 | ||
| [[ "$("$NODE" -e "process.stdout.write(process.env.DYLD_INSERT_LIBRARIES?'y':'n')" 2>/dev/null)" == "y" ]] | ||
| } |
Member
Author
|
Superseded by #4 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.