Shell-alias runner deadlocks on large output and reports signal-killed aliases as success
Context: API Makeathon participant. Found while reviewing the alias subprocess runner in src/main.rs.
Two independent bugs in how the alias runner spawns and reaps its child process.
1. Deadlock on large output
src/main.rs:226-234 pipes both the child's stdout and stderr, then calls wait() (line 232) before either stream is drained (lines 234-245). Once either OS pipe buffer fills, the child blocks writing while the parent blocks in wait() waiting for the child to exit. Neither side makes progress.
2. Signal-killed alias reported as exit 0
src/main.rs:232-247 converts ExitStatus::code() with unwrap_or(0). On Unix, a process terminated by a signal has code() == None, so abnormal termination is converted to exit code 0. That value becomes Zoo's own exit status (src/main.rs:203).
Concrete failure
A shell alias that emits more than one pipe buffer of stdout or stderr never completes (it hangs at wait()). A shell alias killed by a signal (kill -TERM $$, an OOM kill, a segfault) is reported as a clean success, so scripts continue as though it worked.
Verify
Deadlock: define an alias that emits data beyond pipe capacity; the command blocks at wait(), while a control implementation using wait_with_output() or concurrent draining completes and preserves the output. Signal: define an alias running kill -TERM $$, invoke it, and inspect Zoo's exit status (currently 0).
Suggested fix
Use wait_with_output() (or drain the pipes on separate threads before waiting), and map signal termination to a nonzero exit (e.g. 128 + signal) instead of unwrap_or(0).
Environment
Zoo CLI v0.2.184 (33534cd). Reviewed against the current main of KittyCAD/cli.
Shell-alias runner deadlocks on large output and reports signal-killed aliases as success
Context: API Makeathon participant. Found while reviewing the alias subprocess runner in
src/main.rs.Two independent bugs in how the alias runner spawns and reaps its child process.
1. Deadlock on large output
src/main.rs:226-234pipes both the child's stdout and stderr, then callswait()(line 232) before either stream is drained (lines 234-245). Once either OS pipe buffer fills, the child blocks writing while the parent blocks inwait()waiting for the child to exit. Neither side makes progress.2. Signal-killed alias reported as exit 0
src/main.rs:232-247convertsExitStatus::code()withunwrap_or(0). On Unix, a process terminated by a signal hascode() == None, so abnormal termination is converted to exit code 0. That value becomes Zoo's own exit status (src/main.rs:203).Concrete failure
A shell alias that emits more than one pipe buffer of stdout or stderr never completes (it hangs at
wait()). A shell alias killed by a signal (kill -TERM $$, an OOM kill, a segfault) is reported as a clean success, so scripts continue as though it worked.Verify
Deadlock: define an alias that emits data beyond pipe capacity; the command blocks at
wait(), while a control implementation usingwait_with_output()or concurrent draining completes and preserves the output. Signal: define an alias runningkill -TERM $$, invoke it, and inspect Zoo's exit status (currently 0).Suggested fix
Use
wait_with_output()(or drain the pipes on separate threads before waiting), and map signal termination to a nonzero exit (e.g. 128 + signal) instead ofunwrap_or(0).Environment
Zoo CLI v0.2.184 (33534cd). Reviewed against the current
mainof KittyCAD/cli.