Skip to content

fix(fspy): record file accesses made inside signal handlers - #687

Merged
wan9chi merged 1 commit into
mainfrom
agent/fspy-drop-reentry-guard
Aug 18, 2026
Merged

wan9chi merged 1 commit into
mainfrom
agent/fspy-drop-reentry-guard

Conversation

@wan9chi

@wan9chi wan9chi commented Aug 18, 2026

Copy link
Copy Markdown
Member

Motivation

The HANDLING_OPEN thread-local guard (#540) suppressed same-thread re-entry into handle_open because, at the time, resolving and reporting an access called interposable libc symbols — getcwd, readlink, fcntl through nix, plus CString::new(format!(...)) — and on Linux an LD_PRELOAD library's own PLT calls resolve to its own exported interposers, recursing until the traced process overflowed its stack.

That vector no longer exists, on either platform for its own reason:

  • Linux: the handler's entire call graph is now raw syscalls — path resolution through fspy_nostd's rustix/linux_raw wrappers, fd formatting through itoa, allocation through the mmap-backed pooled bump, reporting through atomics on the shared mapping. Nothing binds through the PLT.
  • macOS: the handler still calls libSystem symbols, but interposition is dyld's __interpose section, and dyld never applies interposing tuples to the image that provides them — the same exemption every original() forward has always relied on.

Dropping the guard also fixes a real gap: it silently discarded legitimate accesses made by signal handlers that interrupt an in-flight interception — exactly the case the signal-safe allocator work exists to support — and it removes a std TLS dependency from the hot path, which the std-free preload roadmap has to shed anyway.

A comment on handle_open now records the invariant this rests on: the handler must stay free of bindable libc calls on Linux, and the macOS argument holds only under __interpose-style interposition.

🤖 Generated with Claude Code

The HANDLING_OPEN thread-local guard (#540) suppressed same-thread
re-entry into handle_open because resolving and reporting an access
then called interposable libc symbols, and on Linux an LD_PRELOAD
library's own PLT calls resolve to its own exported interposers,
recursing until the traced process overflowed its stack.

That vector is gone on both platforms: on Linux the handler's whole
call graph is raw syscalls (rustix/linux_raw resolution, itoa fd
formatting, mmap-backed pooled bump, shm atomics), so nothing binds
through the PLT; on macOS the handler still calls libSystem, but dyld
never applies __interpose tuples to the image that provides them — the
exemption every original() forward already relies on.

Dropping the guard fixes a real gap — it silently discarded accesses
made by signal handlers that interrupt an in-flight interception,
exactly the case the signal-safe allocator work supports — and removes
a std TLS dependency from the hot path. A comment on handle_open
records the invariant: the handler must stay free of bindable libc
calls on Linux, and the macOS argument holds only under
__interpose-style interposition.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

fspy benchmark

linux

dynamic/launch             change  -0.51%  [-11.42% .. +11.42%]  overhead  +281.88%
dynamic/access             change  +0.37%  [-13.83% .. +14.77%]  overhead   +10.58%
dynamic/access-relative    change  -0.60%  [-22.09% .. +23.24%]  overhead   +47.19%
dynamic/access-contended   change  +0.47%  [-14.56% ..  +9.60%]  overhead   +12.80%
static/launch              change  +0.71%  [ -9.17% .. +14.56%]  overhead  +697.50%
static/access              change  +4.46%  [-11.36% .. +19.53%]  overhead  +875.67%
static/access-relative     change  -0.02%  [-17.95% .. +11.36%]  overhead +1518.30%
static/access-contended    change  -0.99%  [-11.35% ..  +8.45%]  overhead +2080.50%

macos

dynamic/launch             change  -0.14%  [ -6.25% ..  +5.37%]  overhead  +244.62%
dynamic/access             change  +0.00%  [ -4.42% ..  +5.16%]  overhead    +1.79%
dynamic/access-relative    change  +1.48%  [-14.30% .. +16.90%]  overhead  +267.58%
dynamic/access-contended   change  +0.74%  [-43.17% .. +13.87%]  overhead    +1.08%

windows

dynamic/launch             change  +2.93%  [ -9.57% .. +15.85%]  overhead   +26.09%
dynamic/access             change  -1.95%  [-26.63% .. +10.24%]  overhead    +1.19%
dynamic/access-relative    change  +2.20%  [-11.53% .. +21.68%]  overhead    +3.81%
dynamic/access-contended   change  +1.38%  [-13.99% .. +25.34%]  overhead    +3.67%

@wan9chi
wan9chi marked this pull request as ready for review August 18, 2026 16:57
@wan9chi
wan9chi merged commit e947db2 into main Aug 18, 2026
19 checks passed
@wan9chi
wan9chi deleted the agent/fspy-drop-reentry-guard branch August 18, 2026 16:57
fengmk2 added a commit to voidzero-dev/vite-plus that referenced this pull request Sep 22, 2026
…2780)

Vite+ now uses Vitest 5, and `vp migrate` helps Vitest 4 projects adopt
the new APIs and defaults. Standalone installs and upgrades now require
verified npm provenance for release binaries.

### Breaking Changes

#### Vitest 5

`vp test` and the public `vite-plus/test*` exports now use
`vitest@5.0.1`
([#2551](#2551)), by
@fengmk2.

| Area | Old | New |
| --- | --- | --- |
| Test runner | `vitest@4.1.11` | `vitest@5.0.1` |
| CLI Node.js range | `^20.19.0 \|\| ^22.18.0 \|\| >=24.11.0` |
`^22.18.0 \|\| ^24.11.0 \|\| >=26.0.0` |
| `vite-plus/test/runners` and `vite-plus/test/suite` | Vite+
compatibility exports | Use supported APIs from `vite-plus/test`; review
unsupported runner and expect plugins |
| `vite-plus/test/browser-webdriverio` | Bundled export | Use the
community `@vitest/browser-webdriverio` package |

Run `vp migrate` from the workspace root before you install the new
dependencies. The migrator updates supported config, source, benchmark,
command, and import changes. It reports manual work as `BLOCK` or
`REVIEW` items. See the [Vitest 5 migration
guide](https://viteplus.dev/guide/vitest-v5) for the full process.
Projects can stay on the prior release until their runtimes and tests
are ready.

#### `vp staged` runtime requirements

`vp staged` now uses `lint-staged@17.5.1`
([#2754](#2754)), by
@fengmk2.

| Requirement | Old | New |
| --- | --- | --- |
| Node.js for `vp staged` | The Vite+ CLI runtime range | `^22.22.1 \|\|
^24.11.0 \|\| >=26.0.0` |
| Git | No separate documented minimum | `>=2.32.0` |

Update Node.js and Git on developer machines and CI runners that execute
`vp staged` or its pre-commit hook. Other workflows do not use these
extra requirements.

### Highlights

- Standalone installers, `vp upgrade`, and `vp-setup.exe` now reject
release binaries without supported SLSA provenance
([#2440](#2440)), by
@kazupon.
- Installers now show progress and the exact shell activation command.
Download progress preserves earlier terminal output
([#2744](#2744),
[#2741](#2741)), by
@fengmk2.
- System-first runtime and package-manager shims now use a fallback
directory at the end of `PATH`. Setup restores missing package-manager
preferences
([#2758](#2758),
[#2763](#2763)), by
@liangmiQwQ and @fengmk2.
- `vp run` now finishes when background processes remain. Large file
traces run without caching instead of killing the task
([#2767](#2767),
[vite-task#675](voidzero-dev/vite-task#675)), by
@wan9chi.

### Features

- `vp add` now supports shared install options such as `--offline`,
`--frozen-lockfile`, and `--lockfile-only`
([#2722](#2722)), by
@jong-kyung.
- `vp pm patch` and `vp pm patch-commit` now use the native commands in
npm 12 and later
([#2736](#2736)), by
@jong-kyung.
- `vp rebuild` now supports Yarn Berry and forwards package names and
extra arguments
([#2761](#2761)), by
@jong-kyung.
- The bundled tools update `oxlint` `1.83.0` -> `1.85.0`,
`oxlint-tsgolint` `7.0.2001` -> `7.0.2002`, and `oxfmt` `0.68.0` ->
`0.70.0` ([#2745](#2745),
[#2773](#2773),
[#2778](#2778)), by
@voidzero-guard[bot]. These versions can flag or format code that passed
before. Run `vp fmt` after upgrading if CI runs `vp check`.

### Fixes & Enhancements

- `oxlint` and `oxfmt` no longer expose bin wrappers that conflict with
upstream packages. Editors must use `vp lint --lsp`, `vp fmt --lsp`, or
`vp fmt --stdin-filepath`
([#2672](#2672)), by
@fengmk2.
- `vp create vite:generator` now replaces catalog references for package
managers that do not support catalogs
([#2720](#2720)), by
@SaKaNa-Y.
- Unpinned npm projects now use the npm version bundled with the
selected Node.js runtime. The same policy works during migration
([#2742](#2742),
[#2748](#2748)), by
@liangmiQwQ.
- The CLI now loads its local versions module through a file URL,
including on Windows paths
([#2749](#2749)), by
@YanChenBai.
- Package-manager commands now use pnpm when the project has no detected
package manager
([#2750](#2750)), by
@liangmiQwQ.
- `vp migrate` now removes unused `@oxlint/plugins` dependencies after
it rewrites plugin imports
([#2751](#2751)), by
@fengmk2.
- `vp update --no-save` now warns that Yarn Classic and Yarn Berry do
not support the option
([#2762](#2762)), by
@jong-kyung.
- `vp migrate` now explains its `tsdown@0.23` compatibility settings and
links to removal guidance
([#2769](#2769)), by
@fengmk2.
- Environment setup now installs and diagnoses the official `pn` and
`pnx` aliases for pnpm
([#2770](#2770)), by
@iruoy.
- Cached tasks now receive GitHub Actions OIDC variables, so npm Trusted
Publishing works through `vp run`
([vite-task#691](voidzero-dev/vite-task#691)),
by @naokihaba.
- Automatic task input tracking now records file access from signal
handlers
([vite-task#687](voidzero-dev/vite-task#687)),
by @wan9chi.

### Refactor

- `vp lint`, `vp fmt`, and `vp check` now use native config discovery.
Package commands keep matching workspace-root settings, while explicit
config flags take precedence
([#2731](#2731)), by
@fengmk2.

### Chore

- Generated workflows and `vp migrate` now use `setup-vp@v1.21.1`,
including its installation fixes
([#2760](#2760),
[#2772](#2772)), by
@renovate[bot] and @fengmk2.

### Bundled Versions

| Tool | Version | Source |
| --- | --- | --- |
| vite | `8.3.0` |
[`434e8e9`](vitejs/vite@434e8e9)
|
| rolldown | `1.2.9` |
[`5b4746e`](rolldown/rolldown@5b4746e)
|
| tsdown | `0.23.0` | [npm](https://npmx.dev/package/tsdown/v/0.23.0) |
| vitest | `5.0.1` | [npm](https://npmx.dev/package/vitest/v/5.0.1) |
| oxlint | `1.85.0` | [npm](https://npmx.dev/package/oxlint/v/1.85.0) |
| oxlint-tsgolint | `7.0.2002` |
[npm](https://npmx.dev/package/oxlint-tsgolint/v/7.0.2002) |
| oxfmt | `0.70.0` | [npm](https://npmx.dev/package/oxfmt/v/0.70.0) |

### Upgrade

```bash
vp upgrade
```

### New Contributors

@YanChenBai, @iruoy

**Full Changelog**:
v0.3.3...v1.0.0-rc.0

---

Merging this PR will trigger the release workflow.

---------

Co-authored-by: voidzero-guard[bot] <278573678+voidzero-guard[bot]@users.noreply.github.com>
Co-authored-by: MK (fengmk2) <fengmk2@gmail.com>
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