Skip to content

Commit ffc11bc

Browse files
authored
Merge pull request #5034 from lifubang/ci-detect-fdleak-try-best-1.4
[1.4] fix fd leaks and detect them as comprehensively as possible
2 parents 9a05ab7 + e675743 commit ffc11bc

File tree

14 files changed

+222
-64
lines changed

14 files changed

+222
-64
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require (
66
github.com/checkpoint-restore/go-criu/v7 v7.2.0
77
github.com/containerd/console v1.0.5
88
github.com/coreos/go-systemd/v22 v22.6.0
9-
github.com/cyphar/filepath-securejoin v0.6.0
9+
github.com/cyphar/filepath-securejoin v0.6.1
1010
github.com/docker/go-units v0.5.0
1111
github.com/godbus/dbus/v5 v5.1.0
1212
github.com/moby/sys/capability v0.4.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5z
1111
github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU=
1212
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
1313
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
14-
github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is=
15-
github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
14+
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
15+
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
1616
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1717
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1818
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

libcontainer/rootfs_linux.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,12 @@ func (m *mountEntry) createOpenMountpoint(rootfs string) (Err error) {
589589

590590
func mountToRootfs(c *mountConfig, m mountEntry) error {
591591
rootfs := c.root
592+
defer func() {
593+
if m.dstFile != nil {
594+
_ = m.dstFile.Close()
595+
m.dstFile = nil
596+
}
597+
}()
592598

593599
// procfs and sysfs are special because we need to ensure they are actually
594600
// mounted on a specific path in a container without any funny business.
@@ -629,12 +635,6 @@ func mountToRootfs(c *mountConfig, m mountEntry) error {
629635
if err := m.createOpenMountpoint(rootfs); err != nil {
630636
return fmt.Errorf("create mountpoint for %s mount: %w", m.Destination, err)
631637
}
632-
defer func() {
633-
if m.dstFile != nil {
634-
_ = m.dstFile.Close()
635-
m.dstFile = nil
636-
}
637-
}()
638638

639639
switch m.Device {
640640
case "mqueue":
@@ -995,6 +995,8 @@ func createDeviceNode(rootfs string, node *devices.Device, bind bool) error {
995995
if err != nil {
996996
return fmt.Errorf("mkdir parent of device inode %q: %w", node.Path, err)
997997
}
998+
defer destDir.Close()
999+
9981000
if bind {
9991001
return bindMountDeviceNode(destDir, destName, node)
10001002
}

tests/integration/create.bats

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,70 @@ function teardown() {
1010
teardown_bundle
1111
}
1212

13+
# is_allowed_fdtarget checks whether the target of a file descriptor symlink
14+
# conforms to the allowed whitelist.
15+
#
16+
# This whitelist reflects the set of file descriptors that runc legitimately
17+
# opens during container lifecycle operations (e.g., exec, create, and run).
18+
# If runc's internal behavior changes (e.g., new FD types are introduced),
19+
# this function MUST be updated accordingly to avoid false positives.
20+
#
21+
is_allowed_fdtarget() {
22+
local target="$1"
23+
{
24+
# pty devices for stdio
25+
grep -Ex "/dev/pts/[0-9]+" <<<"$target" ||
26+
# eventfd, eventpoll, signalfd, etc.
27+
grep -Ex "anon_inode:\[.+\]" <<<"$target" ||
28+
# procfs handle cache (pathrs-lite / libpathrs)
29+
grep -Ex "/(proc)?" <<<"$target" ||
30+
# anonymous sockets used for IPC
31+
grep -Ex "socket:\[[0-9]+\]" <<<"$target" ||
32+
# anonymous pipes used for I/O forwarding
33+
grep -Ex "pipe:\[[0-9]+\]" <<<"$target" ||
34+
# "runc start" synchronisation barrier FIFO
35+
grep -Ex ".*/exec\.fifo" <<<"$target" ||
36+
# temporary internal fd used in exec.fifo FIFO reopen (pathrs-lite / libpathrs)
37+
grep -Ex "(/proc)?/1/task/1/fd" <<<"$target" ||
38+
# overlayfs binary reference (CVE-2019-5736)
39+
grep -Ex "/runc" <<<"$target" ||
40+
# memfd cloned binary (CVE-2019-5736)
41+
grep -Fx "/memfd:runc_cloned:/proc/self/exe (deleted)" <<<"$target"
42+
} >/dev/null
43+
return "$?"
44+
}
45+
46+
@test "runc create[detect fd leak as comprehensively as possible]" {
47+
runc create --console-socket "$CONSOLE_SOCKET" test_busybox
48+
[ "$status" -eq 0 ]
49+
50+
testcontainer test_busybox created
51+
52+
pid=$(__runc state test_busybox | jq '.pid')
53+
violation_found=0
54+
55+
while IFS= read -rd '' link; do
56+
fd_name=$(basename "$link")
57+
# Skip . and ..
58+
if [[ "$fd_name" == "." || "$fd_name" == ".." ]]; then
59+
continue
60+
fi
61+
62+
# Resolve symlink target (use readlink)
63+
target=$(readlink "$link" 2>/dev/null)
64+
if [[ -z "$target" ]]; then
65+
echo "Warning: Cannot read target of $link"
66+
continue
67+
fi
68+
69+
if ! is_allowed_fdtarget "$target"; then
70+
echo "Violation: FD $fd_name -> '$target'"
71+
violation_found=1
72+
fi
73+
done < <(find "/proc/$pid/fd" -type l -print0)
74+
[ "$violation_found" -eq 0 ]
75+
}
76+
1377
@test "runc create" {
1478
runc create --console-socket "$CONSOLE_SOCKET" test_busybox
1579
[ "$status" -eq 0 ]

tests/integration/seccomp.bats

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,16 @@ function flags_value() {
185185
[[ "$output" == *"error running startContainer hook"* ]]
186186
[[ "$output" == *"bad system call"* ]]
187187
}
188+
189+
@test "runc run [seccomp] (verify syscall compatibility after seccomp enforcement)" {
190+
update_config ' .process.args = ["true"]
191+
| .process.noNewPrivileges = false
192+
| .linux.seccomp = {
193+
"defaultAction":"SCMP_ACT_ALLOW",
194+
"architectures":["SCMP_ARCH_X86","SCMP_ARCH_X32","SCMP_ARCH_X86_64","SCMP_ARCH_AARCH64","SCMP_ARCH_ARM"],
195+
"syscalls":[{"names":["close_range", "fsopen", "fsconfig", "fspick", "openat2", "open_tree", "move_mount", "mount_setattr"], "action":"SCMP_ACT_ERRNO", "errnoRet": 38}]
196+
}'
197+
198+
runc run test_busybox
199+
[ "$status" -eq 0 ]
200+
}

vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md

Lines changed: 40 additions & 48 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/cyphar/filepath-securejoin/VERSION

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/openat2_linux.go

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_atomic_go119.go

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_atomic_unsupported.go

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)