1
0
mirror of https://github.com/opencontainers/runc.git synced 2026-02-06 03:45:41 +01:00
Files
runc/libcontainer/cmd_clone.go
Kir Kolyshkin cb31d62f1c Fix exec vs Go 1.26
Since [PR 4812], runc exec tries to use clone3 syscall with
CLONE_INTO_CGROUP, falling back to the old method if it is not
supported.

One issue with that approach is, a

> Cmd cannot be reused after calling its [Cmd.Start], [Cmd.Run],
> [Cmd.Output], or [Cmd.CombinedOutput] methods.

(from https://pkg.go.dev/os/exec#Cmd).

This is enforced since Go 1.26, see [CL 728642], and so runc exec
actually fails in specific scenarios (go1.26 and no CLONE_INTO_CGROUP
support).

The easiest workaround is to pre-copy the p.cmd structure (copy = *cmd).
From the [CL 734200] it looks like it is an acceptable way, but it might
break in the future as it also copies the private fields, so let's do a
proper field-by-field copy. If the upstream will add cmd.Clone method,
we will switch to it.

Also, we can probably be fine with a post-copy (once the first Start has
failed), but let's be conservative here and do a pre-copy.

[PR 4812]: https://github.com/opencontainers/runc/pull/4812
[CL 728642]: https://go.dev/cl/728642
[CL 734200]: https://go.dev/cl/734200

Reported-by: Efim Verzakov <efimverzakov@gmail.com>
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2026-01-29 13:49:34 -08:00

38 lines
1013 B
Go

package libcontainer
import "os/exec"
// cloneCmd creates a copy of exec.Cmd. It is needed because cmd.Start
// must only be used once, and go1.26 actually enforces that (see
// https://go-review.googlesource.com/c/go/+/728642). The implementation
// is similar to
//
// cmd = *c
// return &cmd
//
// except it does not copy private fields, or fields populated
// after the call to cmd.Start.
//
// NOTE if Go will add exec.Cmd.Clone, we should switch to it.
func cloneCmd(c *exec.Cmd) *exec.Cmd {
cmd := &exec.Cmd{
Path: c.Path,
Args: c.Args,
Env: c.Env,
Dir: c.Dir,
Stdin: c.Stdin,
Stdout: c.Stdout,
Stderr: c.Stderr,
ExtraFiles: c.ExtraFiles,
SysProcAttr: c.SysProcAttr,
// Don't copy Process, ProcessState, Err since
// these fields are populated after the start.
// Technically, we do not use Cancel or WaitDelay,
// but they are here for the sake of completeness.
Cancel: c.Cancel,
WaitDelay: c.WaitDelay,
}
return cmd
}