From ea99c274d16564af8d4f04a9aec30d740ac83f6d Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Tue, 9 Feb 2021 19:33:13 -0800 Subject: [PATCH] libct/int: add TestFdLeaks This is a very simple test that checks that container.Run do not leak opened file descriptors. In fact it does, so we have to add two exclusions: 1. /sys/fs/cgroup is opened once per lifetime in prepareOpenat2(), provided that cgroupv2 is used and openat2 is available. This works as intended ("it's not a bug, it's a feature"). 2. ebpf program fd is leaked every time we call setDevices() for cgroupv2 (iow, every container.Run or container.Set leaks 1 fd). This needs to be fixed, thus FIXME. Signed-off-by: Kir Kolyshkin --- libcontainer/integration/exec_test.go | 61 +++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/libcontainer/integration/exec_test.go b/libcontainer/integration/exec_test.go index 783af0efb34..1cb1c88c421 100644 --- a/libcontainer/integration/exec_test.go +++ b/libcontainer/integration/exec_test.go @@ -1996,3 +1996,64 @@ func TestCGROUPHost(t *testing.T) { t.Fatalf("cgroup link not equal to host link %q %q", actual, l) } } + +func TestFdLeaks(t *testing.T) { + if testing.Short() { + return + } + + rootfs, err := newRootfs() + ok(t, err) + defer remove(rootfs) + + pfd, err := os.Open("/proc/self/fd") + ok(t, err) + defer pfd.Close() + fds0, err := pfd.Readdirnames(0) + ok(t, err) + _, err = pfd.Seek(0, 0) + ok(t, err) + + config := newTemplateConfig(&tParam{rootfs: rootfs}) + buffers, exitCode, err := runContainer(config, "", "true") + ok(t, err) + + if exitCode != 0 { + t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) + } + + fds1, err := pfd.Readdirnames(0) + ok(t, err) + + if len(fds1) == len(fds0) { + return + } + // Show the extra opened files. + + excludedPaths := []string{ + "/sys/fs/cgroup", // opened once, see prepareOpenat2 + "anon_inode:bpf-prog", // FIXME: see https://github.com/opencontainers/runc/issues/2366#issuecomment-776411392 + } + + count := 0 +next_fd: + for _, fd1 := range fds1 { + for _, fd0 := range fds0 { + if fd0 == fd1 { + continue next_fd + } + } + dst, _ := os.Readlink("/proc/self/fd/" + fd1) + for _, ex := range excludedPaths { + if ex == dst { + continue next_fd + } + } + + count++ + t.Logf("extra fd %s -> %s", fd1, dst) + } + if count > 0 { + t.Fatalf("found %d extra fds after container.Run", count) + } +}