Skip to content

Commit b6633f4

Browse files
committed
cgroup: plug leaks of /sys/fs/cgroup handle
We auto-close this file descriptor in the final exec step, but it's probably a good idea to not possibly leak the file descriptor to "runc init" (we've had issues like this in the past) especially since it is a directory handle from the host mount namespace. In practice, on runc 1.1 this does leak to "runc init" but on main the handle has a low enough file descriptor that it gets clobbered by the ForkExec of "runc init". OPEN_TREE_CLONE would let us protect this handle even further, but the performance impact of creating an anonymous mount namespace is probably not worth it. Also, switch to using an *os.File for the handle so if it goes out of scope during setup (i.e. an error occurs during setup) it will get cleaned up by the GC. Fixes: GHSA-xr7r-f8xq-vfvv CVE-2024-21626 Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
1 parent 284ba30 commit b6633f4

File tree

1 file changed

+16
-15
lines changed

1 file changed

+16
-15
lines changed

libcontainer/cgroups/file.go

+16-15
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,16 @@ var (
7777
// TestMode is set to true by unit tests that need "fake" cgroupfs.
7878
TestMode bool
7979

80-
cgroupFd int = -1
81-
prepOnce sync.Once
82-
prepErr error
83-
resolveFlags uint64
80+
cgroupRootHandle *os.File
81+
prepOnce sync.Once
82+
prepErr error
83+
resolveFlags uint64
8484
)
8585

8686
func prepareOpenat2() error {
8787
prepOnce.Do(func() {
8888
fd, err := unix.Openat2(-1, cgroupfsDir, &unix.OpenHow{
89-
Flags: unix.O_DIRECTORY | unix.O_PATH,
89+
Flags: unix.O_DIRECTORY | unix.O_PATH | unix.O_CLOEXEC,
9090
})
9191
if err != nil {
9292
prepErr = &os.PathError{Op: "openat2", Path: cgroupfsDir, Err: err}
@@ -97,15 +97,16 @@ func prepareOpenat2() error {
9797
}
9898
return
9999
}
100+
file := os.NewFile(uintptr(fd), cgroupfsDir)
101+
100102
var st unix.Statfs_t
101-
if err = unix.Fstatfs(fd, &st); err != nil {
103+
if err := unix.Fstatfs(int(file.Fd()), &st); err != nil {
102104
prepErr = &os.PathError{Op: "statfs", Path: cgroupfsDir, Err: err}
103105
logrus.Warnf("falling back to securejoin: %s", prepErr)
104106
return
105107
}
106108

107-
cgroupFd = fd
108-
109+
cgroupRootHandle = file
109110
resolveFlags = unix.RESOLVE_BENEATH | unix.RESOLVE_NO_MAGICLINKS
110111
if st.Type == unix.CGROUP2_SUPER_MAGIC {
111112
// cgroupv2 has a single mountpoint and no "cpu,cpuacct" symlinks
@@ -132,28 +133,28 @@ func openFile(dir, file string, flags int) (*os.File, error) {
132133
return openFallback(path, flags, mode)
133134
}
134135

135-
fd, err := unix.Openat2(cgroupFd, relPath,
136+
fd, err := unix.Openat2(int(cgroupRootHandle.Fd()), relPath,
136137
&unix.OpenHow{
137138
Resolve: resolveFlags,
138139
Flags: uint64(flags) | unix.O_CLOEXEC,
139140
Mode: uint64(mode),
140141
})
141142
if err != nil {
142143
err = &os.PathError{Op: "openat2", Path: path, Err: err}
143-
// Check if cgroupFd is still opened to cgroupfsDir
144+
// Check if cgroupRootHandle is still opened to cgroupfsDir
144145
// (happens when this package is incorrectly used
145146
// across the chroot/pivot_root/mntns boundary, or
146147
// when /sys/fs/cgroup is remounted).
147148
//
148149
// TODO: if such usage will ever be common, amend this
149-
// to reopen cgroupFd and retry openat2.
150-
fdStr := strconv.Itoa(cgroupFd)
150+
// to reopen cgroupRootHandle and retry openat2.
151+
fdStr := strconv.Itoa(int(cgroupRootHandle.Fd()))
151152
fdDest, _ := os.Readlink("/proc/self/fd/" + fdStr)
152153
if fdDest != cgroupfsDir {
153-
// Wrap the error so it is clear that cgroupFd
154+
// Wrap the error so it is clear that cgroupRootHandle
154155
// is opened to an unexpected/wrong directory.
155-
err = fmt.Errorf("cgroupFd %s unexpectedly opened to %s != %s: %w",
156-
fdStr, fdDest, cgroupfsDir, err)
156+
err = fmt.Errorf("cgroupRootHandle %d unexpectedly opened to %s != %s: %w",
157+
cgroupRootHandle.Fd(), fdDest, cgroupfsDir, err)
157158
}
158159
return nil, err
159160
}

0 commit comments

Comments
 (0)