Repository navigation
Fix #1047: bound what SessionStart keeps from stdin, not just how it reads it - #1049
Open
CNSeniorious000 wants to merge 3 commits into
Open
CNSeniorious000 wants to merge 3 commits into
CNSeniorious000 wants to merge 3 commits into
Conversation
…din, not just how it reads it
…tighten the comment
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.
Closes #1047.
What
session-start-hook.shcrops each side of the stdin join to 4 KB, so the payload its extractors see cannot exceed 8 KB whatever the host sends — and the four extractions over it (cwd,session_id,transcript_path,source) stay linear-size.Why
_stdin_json_string_intoopens with${_sjsi_raw#*\"$_sjsi_key\"}, and a leading*makes bash retry the matcher at every offset — quadratic. Measured on bash 5.2.15: 0.28 s at 12.5 KB, 0.95 s at 25 KB, 3.55 s at 50 KB, 15.2 s at 100 KB, against 2-6 ms for thecaseone line below it on the same strings. Dropping the leading*is not an option:docs/releasing.mdalready records that both${x#*P}and${x%%P*}are quadratic on a miss.The loop's own
HOOK_STDIN="$HOOK_STDIN$_line"is the second quadratic, for many-line input rather than one huge line (6 MB across 5,000 lines: 63-91 s, against 0.4-0.7 s cropped). That is why the bound belongs in the read loop rather than only in the extractor: one 18-byte crop closes both.The
#1040comment this sits under already says why the line loop itself is fine ("a few hundred bytes: milliseconds even at that rate"). That is a claim about a payload the host chooses, and nothing enforced it. Full numbers, and the 27 orphaned processes this was found on, are in #1047.Red, then green
Red — with
scripts/session-start-hook.shreverted tomain(tests, docs and fragment left in place), on Linux / bash 5.2.15, the same transcript-shaped payload the third test uses (1 MB over 64 lines):and on macOS / bash 3.2, the deterministic test:
Green — this branch:
The positive control is green in BOTH runs on purpose: a bound that cropped everything would pass the first assertion too.
The three tests
..._a_16_kb_stdin_is_cropped_before_the_hook_publishes_ithooks.d/listener reports how many bytes the hook published inREMEMBER_HOOK_STDIN_FILE..._a_real_payload_arrives_byte_for_byte..._a_transcript_sized_stdin_does_not_hang_the_hookTwo fixture constraints came out of measuring bash 3.2, and both are in the module docstring:
read -r -t 1returns EMPTY — not a partial line — once a payload needs more than its second to fetch (observed: a 16 KB single line arrives, a 64 KB one and an 8-line 131 KB one do not). A larger fixture would leave the hook with nothing to crop and the test green for the wrong reason.The listener is named
99-record-stdin.sh, not50-git-restore.sh: a lone bundled listener is dispatched deferred (& disown, #660) and the published file is deleted on the way out, so a deferred copy would race that cleanup. An unknown script keeps the foreground dispatch, which is why nothing here polls.Budget
The comment block costs nothing (the build strips comments); the code change is the +18.
Not done here
/simplifyfindings that did not fit: an earlybreakonce the accumulator reaches the bound, orread -n, would cut the pathological loop from ~894 ms to ~4 ms — but costs 44-45 compiled bytes against a 19-byte margin, and would stop draining stdin (EPIPE for the writer). Left as a deliberate non-change.${raw#*...}operator lives inpost-tool-hook.sh,session-end-hook.shanduser-prompt-hook.sh, whose payloadslib-stdin.shalso reads unbounded. Those are not affected by this fix and this PR does not touch them; noted on session-start-hook: a transcript-sized stdin makes field extraction quadratic — the hook spins for hours and ignores SIGTERM #1047 instead.Observed, and reasoned
hooks.d/listener (documented indocs/hooks.md). Everything under 4 KB is byte-identical tomain, which the second test pins.