Skip to content

fix(core): cap exec_capture buffer to prevent OOM on unbounded linter output - #3333

Open
FlorianBruniaux wants to merge 1 commit into
rtk-ai:developfrom
FlorianBruniaux:fix/exec-capture-oom-cap
Open

FlorianBruniaux wants to merge 1 commit into
rtk-ai:developfrom
FlorianBruniaux:fix/exec-capture-oom-cap

Conversation

@FlorianBruniaux

Copy link
Copy Markdown
Contributor

Problem

pnpm lint gets hook-rewritten to rtk lint (see the classification
table in src/discover/registry.rs). Without arguments, rtk lint
defaults to eslint and scans . (the whole repo) with -f json
(src/cmds/js/lint_cmd.rs::run). On a large repo with many violations,
that JSON payload can reach hundreds of MB.

It gets captured via core::stream::exec_capture(), which used
Command::output() with no size limit, unlike run_streaming(), which
already caps captured output at RAW_CAP (10 MiB). Result: the rtk
process itself can OOM before any filter runs.

exec_capture is shared by 15 other filter modules (golangci, pip,
prisma, vitest, playwright, dotnet, git, gt, container, ...). This wasn't
lint-specific, it was a gap in the shared capture primitive.

Fix

exec_capture/exec_capture_stdin now read each pipe (stdout/stderr)
capped at RAW_CAP (10 MiB), still draining (and discarding) anything
past the cap so the child process never blocks writing to a full pipe.
Behavior is unchanged below 10 MiB; above it, output is bounded instead
of crashing the process.

Testing

  • cargo fmt --all && cargo clippy --all-targets: clean
  • cargo test --all: full suite green except 2 pre-existing, unrelated
    failures in hooks::rewrite_cmd (permission-config-dependent, verified
    to fail identically on develop without this patch)
  • New regression test test_exec_capture_raw_cap_at_10mb: proves the cap
    holds and the child process exits cleanly (no pipe deadlock)

Test plan

  • CI green (fmt, clippy, test)
  • Confirm the 2 pre-existing rewrite_cmd failures are also present on
    develop HEAD without this change

… output

pnpm lint gets hook-rewritten to `rtk lint`, which defaults to
`eslint -f json .` over the whole repo. exec_capture used
Command::output() with no size limit, unlike run_streaming's
RAW_CAP-bounded capture. A large repo's JSON output could OOM the
rtk process itself. Shared by 15 other filter modules (golangci,
pip, prisma, vitest, playwright, dotnet, git...), not lint-specific.

Signed-off-by: Florian BRUNIAUX <florian@bruniaux.com>
@bharvey42

Copy link
Copy Markdown

Carried in a local fork build since 2026-08-08 (Windows 11, x86_64-pc-windows-gnu, daily use) with no regressions observed across the filter modules that share exec_capture.

Heads-up for the author: as of develop @ aa40853 this branch no longer merges cleanly — git merge-tree reports conflicts against current mainline, so it probably needs a rebase before a reviewer can act on it. Three PRs merged into develop today (#3798, #3749, #3470).

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.

2 participants