Repository navigation
fix(process): kill the whole process group when exec times out - #454
Open
TanishGudise wants to merge 1 commit into
Open
TanishGudise wants to merge 1 commit into
TanishGudise wants to merge 1 commit into
Conversation
POST /process/exec killed only the direct child on timeout, then waited on cmd.Wait, which also waits for stdout and stderr to close. Children of the command inherit those pipes, so `sh -c "sleep 8; true"` with timeout_sec=1 returned after 8 seconds. Run the command in its own process group and kill the group on timeout with the existing signalChildProcessGroup helper, as the Browser REPL and Playwright executors already do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Checklist
Problem
On timeout,
POST /process/execkilled only the direct child and then waited oncmd.Wait(), which also waits for stdout and stderr to close. Children of the command inherit those pipes, sosh -c "sleep 8; true"withtimeout_sec: 1returned after 8 s, and the children kept running (#452).Changes (
server/cmd/api/api/process.go, +9/−1)Setpgid, set on theSysProcAttrthatbuildCmdmay already have filled in foras_user/as_root).signalChildProcessGroup(cmd, killSignal), as the Browser REPL and Playwright executors already do.I didn't use
configureChildProcessCmd, because it replacesSysProcAttr(which would drop theas_user/as_rootcredential) and also setsPdeathsig, which exec doesn't need for this fix. Spawn is unchanged.Scope and limits:
setsid, daemons) aren't in the group, so they aren't killed and can still hold the response open if they keep stdout or stderr. One way to bound that iscmd.WaitDelay, but it also changes calls that don't time out, so I left it out.kernel-images-api.confdoesn't setstopasgroup, and I didn't find anything in the image that signals the API server's group.Tests
TestProcessExec_TimeoutKillsDescendants(Linux only, like the REPL process-group tests) runssh -c 'sleep 30 & echo $! > "$1"; wait'withtimeout_sec: 1. It checks that the call returns within 10 s and that the backgroundsleepis gone.Over HTTP, against
kernel-images-apibuilt for linux/arm64 on an internal Docker network,sh -c "sleep 8; true"withtimeout_sec: 1returns500 {"message":"process timed out"}after 8.01 s on main and after 1.01 s with this PR.go vet ./...andgo test -race $(go list ./... | grep -v /e2e$)on Linux give the same results as main. The only failure on both isTestSetFilteredFailsWhenItCannotPersist, which can't pass as root in my container. I didn't runmake test-runtime(the Node daemon is unchanged) or the e2e suite: the published images are amd64-only and my Docker VM is arm64.The 500 status for a timeout is unchanged here. It's part of #453.
🤖 Generated with Claude Code
Note
Medium Risk
Changes synchronous process execution and signal behavior on timeout/cancel; low blast radius but affects resource cleanup and timing for all exec calls (which now run in isolated process groups).
Overview
POST /process/execnow tears down the whole process group on timeout or request cancellation, instead of killing only the direct child and blocking until grandchildren release inherited stdout/stderr.Exec starts the command with
Setpgid(preserving existingSysProcAttrforas_user/as_root). When the deadline or client context ends, it callssignalChildProcessGroup—the same helper used by the Browser REPL—rather thanProcess.Kill().A Linux-only test runs a shell that backgrounds a long
sleepandwaits; it asserts the API returns within ~1s and the background PID is gone.Reviewed by Cursor Bugbot for commit 32be48a. Bugbot is set up for automated code reviews on this repo. Configure here.