Skip to content

run, exec: fix -i hanging when stdin reaches EOF during task creation#5042

Merged
ktock merged 1 commit into
containerd:mainfrom
AkihiroSuda:fix-5029
Jul 7, 2026
Merged

run, exec: fix -i hanging when stdin reaches EOF during task creation#5042
ktock merged 1 commit into
containerd:mainfrom
AkihiroSuda:fix-5029

Conversation

@AkihiroSuda

Copy link
Copy Markdown
Member

nerdctl (run|exec) -i (without -t) propagates the EOF of its own stdin to the container by calling CloseIO: the shim deliberately keeps its own write end of the stdin FIFO open (so that a detached client does not propagate EOF), and only CloseIO makes the shim close it.

The io copy goroutines are started by the cio.Creator, inside container.NewTask (respectively task.Exec), before the task (process) is registered in containerd:

  • in taskutil.NewTask, when the stdin was short enough to reach EOF during the creation, the container.Task API lookup in the closer failed with "no such task", and the error was discarded at the debug level;
  • in container.Exec, the closer was only installed after task.Exec returned, and a closer firing before that was a silent no-op.

In both cases the CloseIO was lost forever: the container never received EOF on its stdin, and eg: nerdctl run --rm -i IMAGE cat hung until killed.

Make the closer block on a channel that receives the task (process) handle when the creation returns, so that the CloseIO is always delivered.

The race is easily reproducible under load; in the CI it is the cause of the TestRunStdin flakiness, which became a consistent failure on the slow almalinux-8 guests after the tests moved out of Docker.

Fix #5029

Assisted-by: Claude Fable 5 noreply@anthropic.com

`nerdctl (run|exec) -i` (without `-t`) propagates the EOF of its own
stdin to the container by calling CloseIO: the shim deliberately keeps
its own write end of the stdin FIFO open (so that a detached client
does not propagate EOF), and only CloseIO makes the shim close it.

The io copy goroutines are started by the cio.Creator, inside
container.NewTask (respectively task.Exec), before the task (process)
is registered in containerd:
- in taskutil.NewTask, when the stdin was short enough to reach EOF
  during the creation, the container.Task API lookup in the closer
  failed with "no such task", and the error was discarded at the debug
  level;
- in container.Exec, the closer was only installed after task.Exec
  returned, and a closer firing before that was a silent no-op.

In both cases the CloseIO was lost forever: the container never
received EOF on its stdin, and eg: `nerdctl run --rm -i IMAGE cat`
hung until killed.

Make the closer block on a channel that receives the task (process)
handle when the creation returns, so that the CloseIO is always
delivered.

The race is easily reproducible under load; in the CI it is the cause
of the TestRunStdin flakiness, which became a consistent failure on
the slow almalinux-8 guests after the tests moved out of Docker.

Fix issue 5029

Assisted-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
@AkihiroSuda AkihiroSuda added this to the v2.4.0 milestone Jul 4, 2026
@AkihiroSuda AkihiroSuda requested a review from ktock July 7, 2026 00:58
@ktock ktock merged commit e269302 into containerd:main Jul 7, 2026
108 of 116 checks passed
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.

Flaky test: TestRunStdin

2 participants