Skip to content

refactor(fspy-shared): make the payload a borrowed view - #671

Draft
wan9chi wants to merge 1 commit into
agent/fspy-alloc-generic-channelfrom
agent/fspy-channel-conf-view
Draft

refactor(fspy-shared): make the payload a borrowed view#671
wan9chi wants to merge 1 commit into
agent/fspy-alloc-generic-channelfrom
agent/fspy-channel-conf-view

Conversation

@wan9chi

@wan9chi wan9chi commented Aug 14, 2026

Copy link
Copy Markdown
Member

Motivation

The end goal is a fully std-free preload, and the payload types were shaped against it: the channel configuration and every payload path crossed process boundaries as Box<IpcStr>, copied out of the deserialization buffer through the global allocator — on the attach path, under the loader lock on Windows.

Nothing needs to own payload fields. This makes Payload and EncodedPayload strictly borrowed views over storage their producer owns — the model the Windows preload already had with its 'static Detours page:

  • ChannelConf<'a> borrows its path in C-string form — the new IpcCStr, an IpcStr that keeps its NUL terminator. channel() returns only the Receiver, Receiver::conf() borrows the keeper's C string, and a unix sender attaches by borrowing the path straight from the conf, allocating nothing; Windows still re-aligns the wide path through a caller-provided allocator. IpcStr sheds the APIs whose last users this replaces.
  • decode_payload_from_env leaks its allocations into whichever allocator the caller passes — the allocator's lifetime bounds the payload's. The supervisor lends its session paths per spawn instead of cloning boxes.
  • The preload ctor owns the attach storage — one page-backed bump (fspy_nostd_alloc::page_bump()), held in ManuallyDrop and never dropped — and lends it to from_env, which is now safe code end to end: decode leaks the payload into the bump, and the sender borrows its path from the decoded bytes, so no temporary allocation, no scope, and no lifetime laundering exist in the attach at all. The ctor's Client::assume_process_lifetime is the single unsafe step, reasoning against the bump the ctor itself owns. One mapping serves the whole attach (a second only if the payload outgrows the chunk), no global allocator, and no borrows into the mutable process environment — env memory is not stable storage, so the value is copied out deliberately. seccomp_payload stays owned until fspy_seccomp_unotify grows borrowed types.
  • The Windows preload deserializes its payload zero-copy from the static page and forwards those original bytes to grandchildren instead of re-serializing per spawn.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown

fspy benchmark

linux

dynamic/launch             change  +0.14%  [ -8.47% ..  +8.31%]  overhead  +296.71%
dynamic/access             change  -0.49%  [-11.74% .. +12.96%]  overhead    +6.98%
dynamic/access-relative    change  -0.65%  [-26.56% ..  +9.06%]  overhead   +44.87%
dynamic/access-contended   change  -0.54%  [-10.90% .. +13.05%]  overhead    +4.99%
static/launch              change  -1.85%  [-10.25% .. +10.43%]  overhead  +653.73%
static/access              change  +0.31%  [-10.36% ..  +8.46%]  overhead  +810.24%
static/access-relative     change  -1.03%  [ -5.01% ..  +6.09%]  overhead +1239.62%
static/access-contended    change  +0.18%  [ -9.51% ..  +9.09%]  overhead +2143.61%

macos

dynamic/launch             change  -0.06%  [ -3.22% ..  +3.57%]  overhead  +214.34%
dynamic/access             change  +0.72%  [ -2.94% ..  +4.73%]  overhead    +7.48%
dynamic/access-relative    change  -0.11%  [ -1.92% ..  +1.66%]  overhead  +270.68%
dynamic/access-contended   change  -0.28%  [ -6.53% ..  +3.81%]  overhead    +5.57%

windows

dynamic/launch             change  -0.91%  [ -5.90% ..  +2.89%]  overhead   +25.99%
dynamic/access             change  +0.00%  [ -0.56% ..  +0.74%]  overhead    +0.94%
dynamic/access-relative    change  +0.00%  [ -0.55% ..  +0.55%]  overhead    +1.13%
dynamic/access-contended   change  +0.18%  [ -0.88% ..  +1.62%]  overhead    +1.64%

@wan9chi
wan9chi force-pushed the agent/fspy-channel-conf-view branch from ddd04dd to cd1446d Compare August 14, 2026 04:21
@wan9chi wan9chi changed the title refactor(fspy-shared): make ChannelConf a borrowed view refactor(fspy-shared): make the payload a borrowed view Aug 14, 2026
@wan9chi
wan9chi force-pushed the agent/fspy-channel-conf-view branch 7 times, most recently from eb1efd6 to fde03cd Compare August 14, 2026 07:46
@wan9chi
wan9chi changed the base branch from main to agent/fspy-alloc-generic-channel August 14, 2026 07:47
@wan9chi
wan9chi force-pushed the agent/fspy-channel-conf-view branch 4 times, most recently from 4fa2c1a to d44239e Compare August 18, 2026 03:43
@wan9chi
wan9chi force-pushed the agent/fspy-channel-conf-view branch from d44239e to e8a57be Compare August 18, 2026 09:13
@wan9chi
wan9chi force-pushed the agent/fspy-channel-conf-view branch 3 times, most recently from 9f03d6f to e7d0ba3 Compare August 18, 2026 10:06
The payload and its channel configuration are now views over storage
their producer owns, the model the Windows preload already had with its
static Detours page:

- ChannelConf borrows its path in C-string form (the new IpcCStr, an
  IpcStr that keeps its NUL terminator): channel() returns only the
  Receiver, Receiver::conf() borrows the keeper's C string, and a unix
  sender attaches by borrowing the path straight from the conf — no
  allocation at all. Windows still re-aligns the wide path through a
  caller-provided allocator. IpcStr sheds the APIs whose last users
  this replaces (from_os_c_str, to_os_c_string_in, to_boxed).
- The unix Payload and EncodedPayload borrow every path and the encoded
  string. The supervisor lends its session paths per spawn instead of
  cloning boxes. seccomp_payload stays owned until fspy_seccomp_unotify
  grows borrowed types.
- decode_payload_from_env leaks its allocations into the allocator the
  caller lends, whose borrow bounds the payload.
- The preload ctor owns the attach storage — one page-backed bump from
  fspy_nostd_alloc::page_bump(), held in ManuallyDrop and never
  dropped — and lends it to from_env, which is safe code end to end:
  decode leaks the payload into the bump and the sender borrows its
  path from the decoded bytes. The ctor's
  Client::assume_process_lifetime is the attach's single unsafe step,
  reasoning against the bump the ctor itself owns. One mapping serves
  the whole attach unless the payload outgrows the chunk, nothing
  comes from the global allocator, and nothing borrows the mutable
  process environment.
- The Windows preload deserializes its payload zero-copy from the
  static page and forwards those original bytes to children instead of
  re-serializing per spawn.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@wan9chi
wan9chi force-pushed the agent/fspy-channel-conf-view branch from e7d0ba3 to 94b517a Compare August 18, 2026 10:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant