mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
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>
(cherry picked from commit cb31d62f1c)
Signed-off-by: lifubang <lifubang@acmcoder.com>
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
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
|
||||
}
|
||||
@@ -367,11 +367,14 @@ func (p *setnsProcess) startWithCgroupFD() error {
|
||||
defer fd.Close()
|
||||
}
|
||||
|
||||
cmdCopy := cloneCmd(p.cmd)
|
||||
err = p.startWithCPUAffinity()
|
||||
if err != nil && p.cmd.SysProcAttr.UseCgroupFD {
|
||||
logrus.Debugf("exec with CLONE_INTO_CGROUP failed: %v; retrying without", err)
|
||||
// SysProcAttr.CgroupFD is never used when UseCgroupFD is unset.
|
||||
p.cmd.SysProcAttr.UseCgroupFD = false
|
||||
cmdCopy.SysProcAttr.UseCgroupFD = false
|
||||
// Must not reuse exec.Cmd.
|
||||
p.cmd = cmdCopy
|
||||
err = p.startWithCPUAffinity()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user