Skip to content

cmd/containerd-shim-runhcs-v1: prevent zombie shims by adding timeout to stdin read#2824

Open
HarshalPatel1972 wants to merge 1 commit into
microsoft:mainfrom
HarshalPatel1972:fix-zombie-shim
Open

cmd/containerd-shim-runhcs-v1: prevent zombie shims by adding timeout to stdin read#2824
HarshalPatel1972 wants to merge 1 commit into
microsoft:mainfrom
HarshalPatel1972:fix-zombie-shim

Conversation

@HarshalPatel1972

Copy link
Copy Markdown

cmd/containerd-shim-runhcs-v1: prevent zombie shims by adding timeout to stdin read

When the containerd daemon restarts or exits unexpectedly on Windows, the
named pipe write handles may be inherited or fail to immediately broadcast
an EOF. Because io.ReadAll does not natively respect context.Context,
the runhcs shim blocks indefinitely waiting for data, leaking the process
and blocking network ports.

This introduces a background monitor with a 5-second timeout that explicitly
closes os.Stdin if the read takes too long. This forcefully aborts the blocking
I/O read, allowing the shim to detect the daemon disconnection and
terminate gracefully.

Signed-off-by: Harshal Patel hp842484@gmail.com

… to stdin read

When the containerd daemon restarts or exits unexpectedly on Windows, the
named pipe write handles may be inherited or fail to immediately broadcast
an EOF. Because io.ReadAll does not natively respect context.Context,
the runhcs shim blocks indefinitely waiting for data, leaking the process
and blocking network ports.

This introduces a background monitor with a 5-second timeout that explicitly
closes os.Stdin if the read takes too long. This forcefully aborts the blocking
I/O read, allowing the shim to detect the daemon disconnection and
terminate gracefully.

Signed-off-by: Harshal Patel <hp842484@gmail.com>
@HarshalPatel1972
HarshalPatel1972 requested a review from a team as a code owner July 13, 2026 20:44
@rawahars

Copy link
Copy Markdown
Contributor

Thanks for this, but I think the change is based on a misdiagnosis and should not merge.

Shim options aren't a live stream; containerd marshals them into a fixed buffer at process start and pipes them in — containerd/pkg/shim/util.go Command():

cmd.Stdin = bytes.NewReader(d) // d = proto.Marshal(opts)

os/exec creates an anonymous pipe, hands the shim the read end, and a goroutine inside containerd writes the buffer and closes the write end, delivering EOF. So:

  • It's an anonymous pipe, not a named pipe.
  • The write end is held only by containerd. If the daemon restarts/crashes, the OS closes it → the read side gets EOF immediatelyio.ReadAll returns. A daemon exit unblocks the read; it can't hang forever.
  • The shim only ever holds the read end — no write handle to inherit/leak.

The change is also risky: force-closing os.Stdin mid-ReadAll races with a legitimately slow write (large payload / slow daemon / loaded host). Past 5s it returns a partial buffer or "file already closed" → proto.Unmarshal fails → shim fails to start. New failure mode on a working path, and 5s maps to no containerd contract (startup is already bounded by containerd's exec.CommandContext).

Real zombie-shim cases are on the serving side (ttrpc shutdown, log-pipe Accept, or not exiting after task delete), not the startup read.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants