What
exec passes a timeout down to attach_run and nothing else:
src/backends/lxc/common/src/state_aware.rs:592-606
let timeout = if request.script_timeout == 0 { None }
else { Some(Duration::from_millis(..)) };
let exit_code = container
.attach_run(&request.script_code, .., timeout)
When that timeout fires, the attached process is torn down. Nothing in this
path reaps processes the script spawned inside the container. Because the
state-aware container is persistent -- it survives exec and lives until
deprovision -- those descendants keep running in it, and a later exec in the
same sandbox shares a container with them.
Why it matters
A timeout is the caller's statement that the work must not continue. Here it
ends the caller's view of the work while the work itself carries on, holding
CPU, memory, file handles, and network inside a container the caller believes is
idle. It also makes timeouts non-deterministic for anything that follows in the
same sandbox.
Fix
Reap the process tree on timeout rather than just the attached process -- kill
the container's PID namespace descendants, or stop and restart the container as
part of timeout handling. The right shape is worth designing rather than
patching, which is why this is an issue and not a change in PR #849.
Provenance -- reported twice
Verified still live: the code path above is unchanged from the version
Gudge (@MGudgin) reviewed. The descendant-survival mechanism is his, and it is
consistent with the code, which contains no reaping step at all.
Filing it so the second report does not share the fate of the first.
What
execpasses a timeout down toattach_runand nothing else:src/backends/lxc/common/src/state_aware.rs:592-606When that timeout fires, the attached process is torn down. Nothing in this
path reaps processes the script spawned inside the container. Because the
state-aware container is persistent -- it survives
execand lives untildeprovision-- those descendants keep running in it, and a laterexecin thesame sandbox shares a container with them.
Why it matters
A timeout is the caller's statement that the work must not continue. Here it
ends the caller's view of the work while the work itself carries on, holding
CPU, memory, file handles, and network inside a container the caller believes is
idle. It also makes timeouts non-deterministic for anything that follows in the
same sandbox.
Fix
Reap the process tree on timeout rather than just the attached process -- kill
the container's PID namespace descendants, or stop and restart the container as
part of timeout handling. The right shape is worth designing rather than
patching, which is why this is an issue and not a change in PR #849.
Provenance -- reported twice
state_aware.rs:597. PR [LXC] State-aware lifecycle management #633 was closed withits 87 review comments unaddressed, so the report was never resolved.
state_aware.rs:603-- the same finding on the samecode, which had simply moved by six lines.
Verified still live: the code path above is unchanged from the version
Gudge (@MGudgin) reviewed. The descendant-survival mechanism is his, and it is
consistent with the code, which contains no reaping step at all.
Filing it so the second report does not share the fate of the first.