From 3ce4036efbf2dc0e8c74a8144ff68b0013a598e4 Mon Sep 17 00:00:00 2001 From: Harshal Patel Date: Tue, 14 Jul 2026 02:07:02 +0530 Subject: [PATCH] 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 --- cmd/containerd-shim-runhcs-v1/serve.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cmd/containerd-shim-runhcs-v1/serve.go b/cmd/containerd-shim-runhcs-v1/serve.go index d7e236ac6e..df20319c69 100644 --- a/cmd/containerd-shim-runhcs-v1/serve.go +++ b/cmd/containerd-shim-runhcs-v1/serve.go @@ -274,7 +274,24 @@ func trapClosedConnErr(err error) error { // readOptions reads in bytes from the reader and converts it to a shim options // struct. If no data is available from the reader, returns (nil, nil). func readOptions(r io.Reader) (*runhcsopts.Options, error) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + readDone := make(chan struct{}) + go func() { + select { + case <-ctx.Done(): + // Forcefully break the blocking io.ReadAll if the daemon disconnects + if c, ok := r.(io.Closer); ok { + c.Close() + } + case <-readDone: + // Read completed successfully, exit the monitor + } + }() + d, err := io.ReadAll(r) + close(readDone) if err != nil { return nil, errors.Wrap(err, "failed to read input") }