Skip to content

Commit ddc70a3

Browse files
committed
fix an error caused by fd reuse race when starting runc init
There is a race situation when we are opening a file, if there is a small fd was closed at that time, maybe it will be reused by safeExe. Because of Go stdlib fds shuffling bug, if the fd of safeExe is too small, go stdlib will dup3 it to another fd, or dup3 a other fd to this fd, then it will cause the fd type cmd.Path refers to a random path, and it can lead to an error "permission denied" when starting the process. Please see opencontainers#4294 and <golang/go#61751>. So we should not use the original fd of safeExe, but use the fd after shuffled by Go stdlib. Because Go stdlib will guarantee this fd refers to the correct file. Signed-off-by: lfbzhm <[email protected]>
1 parent ca8ca3c commit ddc70a3

File tree

1 file changed

+27
-13
lines changed

1 file changed

+27
-13
lines changed

libcontainer/container_linux.go

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,33 @@ func (c *Container) newParentProcess(p *Process) (parentProcess, error) {
560560
if err != nil {
561561
return nil, fmt.Errorf("unable to create safe /proc/self/exe clone for runc init: %w", err)
562562
}
563-
exePath = "/proc/self/fd/" + strconv.Itoa(int(safeExe.Fd()))
563+
564+
// TODO: After https://go-review.googlesource.com/c/go/+/515799 included
565+
// in go versions supported by us, we can remove this logic and change
566+
// it back to `exePath = "/proc/self/fd/" + strconv.Itoa(safeExe.Fd())`.
567+
568+
// Due to a Go stdlib bug, we need to add safeExe to the set of
569+
// ExtraFiles otherwise it is possible for the stdlib to clobber the fd
570+
// during forkAndExecInChild1 and replace it with some other file that
571+
// might be malicious. This is less than ideal (because the descriptor
572+
// will be non-O_CLOEXEC) however we have protections in "runc init" to
573+
// stop us from leaking extra file descriptors.
574+
//
575+
// See <https://github.com/golang/go/issues/61751>.
576+
p.ExtraFiles = append(p.ExtraFiles, safeExe)
577+
578+
// There is a race situation when we are opening a file, if there is a
579+
// small fd was closed at that time, maybe it will be reused by safeExe.
580+
// Because of Go stdlib fds shuffling bug, if the fd of safeExe is too
581+
// small, go stdlib will dup3 it to another fd, or dup3 a other fd to this
582+
// fd, then it will cause the fd type cmd.Path refers to a random path,
583+
// and it can lead to an error "permission denied" when starting the process.
584+
// Please see #4294.
585+
// So we should not use the original fd of safeExe, but use the fd after
586+
// shuffled by Go stdlib. Because Go stdlib will guarantee this fd refers to
587+
// the correct file.
588+
exePath = "/proc/self/fd/" + strconv.Itoa(stdioFdCount+len(p.ExtraFiles)-1)
589+
564590
p.clonedExes = append(p.clonedExes, safeExe)
565591
logrus.Debug("runc-dmz: using /proc/self/exe clone") // used for tests
566592
}
@@ -618,18 +644,6 @@ func (c *Container) newParentProcess(p *Process) (parentProcess, error) {
618644
)
619645
}
620646

621-
if safeExe != nil {
622-
// Due to a Go stdlib bug, we need to add safeExe to the set of
623-
// ExtraFiles otherwise it is possible for the stdlib to clobber the fd
624-
// during forkAndExecInChild1 and replace it with some other file that
625-
// might be malicious. This is less than ideal (because the descriptor
626-
// will be non-O_CLOEXEC) however we have protections in "runc init" to
627-
// stop us from leaking extra file descriptors.
628-
//
629-
// See <https://github.com/golang/go/issues/61751>.
630-
cmd.ExtraFiles = append(cmd.ExtraFiles, safeExe)
631-
}
632-
633647
// NOTE: when running a container with no PID namespace and the parent
634648
// process spawning the container is PID1 the pdeathsig is being
635649
// delivered to the container's init process by the kernel for some

0 commit comments

Comments
 (0)