Repository navigation
fix(fspy): keep exec arguments and environment out of libc malloc - #893
Merged
wan9chi merged 2 commits intoOct 26, 2026
Merged
Conversation
fspy benchmarklinuxmacoswindows |
The exec interposers copied the program, arguments and environment into std `BString`s and `Vec`s (`RawExec::to_exec`), resolved and rewrote them there (PATH search, shebang, preload variables), and copied them back out (`RawExec::from_exec`), all through libc malloc. Exec may run in the child of fork(), where malloc's lock may be held by a thread that no longer exists. Make `Exec`, `Shebang` and the env helpers generic over an allocator. The preload builds the `Exec` in the interception's bump, and every string or array derived from it stays there; the supervisor uses `Global`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wan9chi
force-pushed
the
fix/fspy-exec-strings-no-alloc
branch
from
October 26, 2026 07:37
3432e9f to
ad889c1
Compare
`which`, `RawExec::from_exec` and `Client::handle_exec` took callbacks only to keep memory alive while it was used: `which` built candidate paths in a stack buffer from `alloca`, and `from_exec` kept owned strings alive for nested closures, so `handle_exec` could only hand its result to one. With the strings in the interception's bump, return them instead. `which` builds candidates in one reused buffer in the allocator and returns the match, which drops `stackalloc`. `from_exec` leaks the C strings and pointer arrays into the `Exec`'s allocator and returns the `RawExec`, and `handle_exec` borrows the caller's allocator and returns the arguments to forward with the pre-exec step. The interposers call the real functions directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Motivation
The exec interposers copied the program, arguments and environment into std
BStrings andVecs (RawExec::to_exec), resolved and rewrote them there (PATH search, shebang, preload variables), and copied them back out (RawExec::from_exec), all through libcmalloc. Exec may run in the child offork(), where malloc's lock may be held by a thread that no longer exists. Only the final pointer arrays used the interception's bump.Change
Exec,Shebang,ensure_envandappend_path_envinfspy_shared_unixare generic over an allocator; strings areallocator_api2::vec::Vec<u8, A>, andexec::to_vec_incopies bytes into one. (<[u8]>::to_vec_inas a method tripsunstable_name_collisionson nightly.)Execin the interception's bump, so every string derived from it stays there. The supervisor'sget_exec/set_execuseGlobal.whichreturns the found path in the allocator, building each candidate in one reused buffer instead of anallocabuffer handed to a callback;stackallocis no longer used.RawExec::from_execleaks the C strings and pointer arrays into theExec's allocator and returns theRawExec, instead of nesting closures to keep owned strings alive.Client::handle_execborrows the caller's allocator and returns the arguments to forward with the pre-exec step, so the exec andposix_spawninterposers call the real function directly.std::path::absolutestill allocates on this path; the PR stacked on this one replaces it.🤖 Generated with Claude Code