Skip to content

Non-NULL Stdin handle appears to be required for HcsProcess to launch console process in WCOW container #2831

Description

@mcayland-ntx

Setup

OS: Windows 11 with latest updates, and Windows containers installed with containerd as per https://learn.microsoft.com/en-us/virtualization/windowscontainers/quick-start/set-up-environment?tabs=containerd but with the following version updates:

  • containerd version v2.2.0
  • nerdctl version v2.3.4 (from git)
  • hcsshim version spec: v1.3.0 (from git)

Description

I've been investigating why running a WCOW container using nerdctl run -d -t mcr.microsoft.com/windows/server:ltsc2025 terminates almost immediately with exit code 3221225786 (0xc000013a) STATUS_CONTROL_C_EXIT as reported at containerd/nerdctl#4899, and the best conclusion I have reached so far is that the underlying HcsProcess requires a valid non-NULL Stdin handle to avoid this immediate termination.

Analysis

Looking at the original issue reported with nerdctl above, with the latest git versions of nerdctl and hcsshim I see the following behaviour:

C:\Users\mark>nerdctl run -d -t mcr.microsoft.com/windows/nanoserver:ltsc2025
369992f8d96f29ab0412dc800b3eff24c8a3a29596bbf5ba3629450f2dd709fc

C:\Users\mark>nerdctl ps -a
CONTAINER ID    IMAGE                                            COMMAND                     CREATED          STATUS                               PORTS    NAMES
369992f8d96f    mcr.microsoft.com/windows/nanoserver:ltsc2025    "c:\\windows\\system32…"    5 seconds ago    Exited (3221225786) 2 seconds ago             nanoserver-36999

For comparison we can see that starting the same container in interactive mode works fine:

C:\Users\mark>nerdctl run --rm -it mcr.microsoft.com/windows/nanoserver:ltsc2025

Microsoft Windows [Version 10.0.26100.32860]
(c) Microsoft Corporation. All rights reserved.

C:\Windows\System32>echo "Hello world"
"Hello world"

C:\Windows\System32>exit
time="2026-07-20T09:52:06+01:00" level=warning msg="failed to remove hosts file for container \"163f0d294c723af096f2965253e16e226a7be00a3a4d8d1cdc93bc152776f251\"" error="hosts-store error\nnot found\nGetFileAttributesEx C:\\ProgramData\\nerdctl\\052055e3\\etchosts\\default\\163f0d294c723af096f2965253e16e226a7be00a3a4d8d1cdc93bc152776f251: The system cannot find the file specified."

C:\Users\mark>

I added various debug statements around nerdctl/hcsshim/containerd and the only obvious difference I could see was that in the detached terminal case the HcsProcess Stdin was set to NULL compared with the interactive terminal.

To test this theory I made the following simple change to hcsshim to see what would happen to the working interactive terminal if I hardcoded the HcsProcess Stdin to NULL:

diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go
index 82f859d5..8b8aa20c 100644
--- a/internal/cmd/cmd.go
+++ b/internal/cmd/cmd.go
@@ -180,7 +180,7 @@ func (c *Cmd) Start() error {
                        User:             c.Spec.User.Username,
                        WorkingDirectory: c.Spec.Cwd,
                        EmulateConsole:   c.Spec.Terminal,
-                       CreateStdInPipe:  c.Stdin != nil,
+                       CreateStdInPipe:  false,
                        CreateStdOutPipe: c.Stdout != nil,
                        CreateStdErrPipe: c.Stderr != nil,
                }

With this change we can see that the container that was previously working in interactive mode now fails in exactly the same, even to the point where the Ctrl-C character is visible in the terminal itself:

C:\Users\mark>nerdctl run -it mcr.microsoft.com/windows/nanoserver:ltsc2025
^C
C:\Users\mark>nerdctl ps -a
CONTAINER ID    IMAGE                                            COMMAND                     CREATED               STATUS                                    PORTS    NAMES
a141fb818e6e    mcr.microsoft.com/windows/nanoserver:ltsc2025    "c:\\windows\\system32…"    About a minute ago    Exited (3221225786) About a minute ago             nanoserver-a141f

So why is it necessary to provide a non-NULL handle to the HcsProcess Stdin only in the detached terminal case? The answer to this is because when nerdctl creates a detached terminal, it uses a new upstream BinaryIO which only creates pipes for Stdout/Stderr and not Stdin as per https://github.com/microsoft/hcsshim/blob/main/internal/cmd/io_binary.go#L33.

At this point my thoughts were that any HcsProcess must have a non-zero Stdin handle, however with the above patch applied to hcsshim I see something interesting: if I run a command such as nerdctl run --rm -it mcr.microsoft.com/windows/nanoserver:ltsc2025 cmd.exe /? enough times in a loop from cmd.exe then eventually it will succeed after perhaps 20-30 tries.

This suggests that it may not necessarily be the NULL Stdin handle that is directly responsible for the failure, but somehow hcsshim accidentally depends upon a functional Stdin in order to detect a successful WCOW container startup and process HcsProcess launch.

Summary

There is a currently an issue when launching detached containers from nerdctl using nerdctl run -d -t mcr.microsoft.com/windows/nanoserver:ltsc2025 causing the process to fail immediately with exit code 3221225786 (0xc000013a) STATUS_CONTROL_C_EXIT. This is linked to the presence of a valid non-NULL Stdin handle for the container HcsProcess which is likely to have one of two causes:

  • Console applications launched as a HcsProcess MUST have a valid non-NULL Stdin handle

This would require someone with Windows internal knowledge to confirm the expectations and behaviour of console applications such as cmd.exe when presented with a NULL Stdin handle. If all console applications MUST have a valid non-NULL Stdin handle then hcsshim should always create the Stdin pipe for a HcsProcess (even if it is never connected) which will resolve the issue.

  • Hcsshim inadvertently depends upon a valid Stdin handle to detect a successful HcsProcess launch

There may be logic in hcsshim that assumes a valid Stdin handle is always present to detect a successful HcsProcess launch: if a valid Stdin handle is not present then a racy logic condition in hcsshim may attempt to detect this and cause the HcsProcess to terminate immediately. For a detached container launched with nerdctl with BinaryIO, a Stdin pipe is never created for the HcsProcess causing the failure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions