Skip to content

Commit 0afad3a

Browse files
committed
fix a fd resue race when starting runc init
In opencontainers#3987(0e9a335), we may use a memfd to copy run to start runc init, due to a Go stdlib bug, we need to add safeExe to the set of ExtraFiles otherwise it is possible for the stdlib to clobber the fd during forkAndExecInChild1 and replace it with some other file that might be malicious. This is less than ideal (because the descriptor will be non-O_CLOEXEC) however we have protections in "runc init" to stop us from leaking extra file descriptors. See <golang/go#61751>. There is a race situation when we are opening this memfd, if the fd 5 or 6 was closed at that time, maybe it will be reused by memfd. But because of we have added safeExe to the set of ExtraFiles, if the fd of safeExe is than stdio fds count + ExtraFiles count, go stdlib will dup3 it to another fd, then it will cause the original fd closed. (opencontainers#4294) Signed-off-by: lfbzhm <[email protected]>
1 parent d82235c commit 0afad3a

File tree

2 files changed

+26
-0
lines changed

2 files changed

+26
-0
lines changed

libcontainer/container_linux.go

+15
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,21 @@ func (c *Container) newParentProcess(p *Process) (parentProcess, error) {
628628
//
629629
// See <https://github.com/golang/go/issues/61751>.
630630
cmd.ExtraFiles = append(cmd.ExtraFiles, safeExe)
631+
632+
// But because of we have added safeExe to the set of ExtraFiles, if the
633+
// fd of safeExe is too small, go stdlib will dup3 it to another fd, then
634+
// it will cause the original fd closed. (#4294)
635+
if int(safeExe.Fd()) <= stdioFdCount+len(cmd.ExtraFiles) {
636+
maxFd, err := utils.GetMaxFds()
637+
if err != nil {
638+
return nil, fmt.Errorf("unable to get the max opened fd of current process: %w", err)
639+
}
640+
maxFd = maxFd + 1
641+
if err := unix.Dup3(int(safeExe.Fd()), maxFd, unix.O_CLOEXEC); err != nil {
642+
return nil, fmt.Errorf("unable to dup3 the fd from %d to %d: %w", safeExe.Fd(), maxFd, err)
643+
}
644+
cmd.Path = "/proc/self/fd/" + strconv.Itoa(maxFd)
645+
}
631646
}
632647

633648
// NOTE: when running a container with no PID namespace and the parent

libcontainer/utils/utils_unix.go

+11
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,17 @@ func fdRangeFrom(minFd int, fn fdFunc) error {
9797
return nil
9898
}
9999

100+
// GetMaxFds returns the max opened fd of current process
101+
func GetMaxFds() (int, error) {
102+
maxFd := -1
103+
err := fdRangeFrom(-1, func(fd int) {
104+
if fd > maxFd {
105+
maxFd = fd
106+
}
107+
})
108+
return maxFd, err
109+
}
110+
100111
// CloseExecFrom sets the O_CLOEXEC flag on all file descriptors greater or
101112
// equal to minFd in the current process.
102113
func CloseExecFrom(minFd int) error {

0 commit comments

Comments
 (0)