Repository navigation
fix(state): drain, cap and time-bound a state hook's output - #11
Merged
Merged
Conversation
aeddi
force-pushed
the
state-hook-drains-stdout
branch
from
November 4, 2026 11:30
317f131 to
ef380d7
Compare
aeddi
marked this pull request as ready for review
November 4, 2026 11:33
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.
Summary
A state hook's output is now read while the hook runs, capped at 1 MiB, and bounded by the
hook's own timeout. Three problems are fixed:
before reading its stdout, so a hook that filled the pipe (64 KiB on Linux with 4 KiB pages,
and on macOS) blocked on the write, was killed at the timeout, and was reported as
"timed out" instead of read.
hook grow the signer's memory without bound. The reader stops one byte past 1 MiB, and an
oversized hook is reported as such. The limit is checked before the exit status, because a
hook cut off at the limit dies of SIGPIPE and its status alone would not say why.
of stdout to close it, however long that took. It now shares the hook's deadline.
timeout_secs's doc now gives its real default, 1 second rather than 5, and says it boundsclosing stdout too.
Safety
The state hook supplies the starting height for double-sign protection. Every hook error,
including the two new ones (output over the limit, output held open past the deadline), goes
through the same
fail_closedpolicy: withtruethe signer refuses to start, withfalseit logs the error and starts from the state file.
One behaviour does change. A process holding the hook's stdout open used to block startup
indefinitely, so neither policy ever applied; it now fails at the deadline and takes the
policy like any other error.
Double-sign protection is never weakened: an error never touches the state, and a successful
read is still parsed by
serde_json, which rejects trailing data, and checked against thesame sanity limit.
Changes
d0228d4thread::Builder, so failing to start it is an error, not a panic —8b15ed17d4fc3dc5b4201,a4a9ef7timeout_secs's real default —a91971c2168377Testing
buffer was reported as a timeout; a 2 MiB writer was accepted;
echo json; sleep 5 &witha 1 s timeout returned
Okafter 5.19 s.reading_stops_one_byte_past_the_limit; checking the limitafter the exit status fails
output_past_the_limit_is_an_errorwith "status None";draining only after the hook exits, and an unbounded receive, are each killed too.
close-on-exec in two steps, so a child spawned on another test thread could inherit a test's
pipe and hold its output open. Production runs on Linux, where
pipe2(O_CLOEXEC)isatomic, and spawns hooks one at a time. Stress runs: about 1 failure in 30 before, none in
140 after.
cargo clippy --all-features, the command CI runs,is clean with
-Dwarnings, and so is this file's test module (--lib --profile test).