From 2dafe5254aca7a8da23788a90ab8e16d9317bac8 Mon Sep 17 00:00:00 2001 From: ls-ggg <335814617@qq.com> Date: Fri, 29 Mar 2024 18:12:08 +0800 Subject: [PATCH] gilibcontainer:clean cached rlimit nofile in go runtime As reported in issue #4195, the new version of go runtime will cache rlimit-nofile. before executing exec, the rlimit-nofile of the process will be restored with the cache. in runc, this will cause the rlimit-nofile set by the parent process for the container to become invalid. this can be solved by clearing the cache. Signed-off-by: ls-ggg <335814617@qq.com> (cherry picked from commit f9f8abf3102e47f2fc3aa45bcd134e957da7de66) Signed-off-by: lifubang --- libcontainer/init_linux.go | 9 +++++++++ libcontainer/setns_init_linux.go | 7 +++++++ libcontainer/standard_init_linux.go | 15 ++++++++++----- libcontainer/system/linux.go | 16 ++++++++++++++++ 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go index bd89b253d5a..9c0f70d03c1 100644 --- a/libcontainer/init_linux.go +++ b/libcontainer/init_linux.go @@ -649,6 +649,15 @@ func setupRoute(config *configs.Config) error { return nil } +func containsRlimit(limits []configs.Rlimit, resource int) bool { + for _, rlimit := range limits { + if rlimit.Type == resource { + return true + } + } + return false +} + func setupRlimits(limits []configs.Rlimit, pid int) error { for _, rlimit := range limits { if err := unix.Prlimit(pid, rlimit.Type, &unix.Rlimit{Max: rlimit.Hard, Cur: rlimit.Soft}, nil); err != nil { diff --git a/libcontainer/setns_init_linux.go b/libcontainer/setns_init_linux.go index 33f4a13b647..9738d043020 100644 --- a/libcontainer/setns_init_linux.go +++ b/libcontainer/setns_init_linux.go @@ -49,6 +49,13 @@ func (l *linuxSetnsInit) Init() error { } } } + + // Set RLIMIT_NOFILE again to clean the cache in go runtime + // The problem originates from https://github.com/golang/go/commit/f5eef58e4381259cbd84b3f2074c79607fb5c821 + if containsRlimit(l.config.Rlimits, unix.RLIMIT_NOFILE) { + system.ClearRlimitNofileCache() + } + if l.config.CreateConsole { if err := setupConsole(l.consoleSocket, l.config, false); err != nil { return err diff --git a/libcontainer/standard_init_linux.go b/libcontainer/standard_init_linux.go index 4447032cf59..3b952777fbb 100644 --- a/libcontainer/standard_init_linux.go +++ b/libcontainer/standard_init_linux.go @@ -6,17 +6,16 @@ import ( "os" "os/exec" - "github.com/opencontainers/runtime-spec/specs-go" - "github.com/opencontainers/selinux/go-selinux" - "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" - "github.com/opencontainers/runc/libcontainer/apparmor" "github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/keys" "github.com/opencontainers/runc/libcontainer/seccomp" "github.com/opencontainers/runc/libcontainer/system" "github.com/opencontainers/runc/libcontainer/utils" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/selinux/go-selinux" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" ) type linuxStandardInit struct { @@ -77,6 +76,12 @@ func (l *linuxStandardInit) Init() error { } } + // Set RLIMIT_NOFILE again to clean the cache in go runtime + // The problem originates from https://github.com/golang/go/commit/f5eef58e4381259cbd84b3f2074c79607fb5c821 + if containsRlimit(l.config.Rlimits, unix.RLIMIT_NOFILE) { + system.ClearRlimitNofileCache() + } + if err := setupNetwork(l.config); err != nil { return err } diff --git a/libcontainer/system/linux.go b/libcontainer/system/linux.go index 0b3b4da33d7..2f6931c6284 100644 --- a/libcontainer/system/linux.go +++ b/libcontainer/system/linux.go @@ -9,6 +9,7 @@ import ( "os" "os/exec" "strconv" + "sync/atomic" "syscall" "unsafe" @@ -16,6 +17,21 @@ import ( "golang.org/x/sys/unix" ) +//go:linkname syscallOrigRlimitNofile syscall.origRlimitNofile + +// As reported in issue #4195, the new version of go runtime will +// cache rlimit-nofile. before executing exec, the rlimit-nofile +// of the process will be restored with the cache. in runc, this will +// cause the rlimit-nofile set by the parent process for the container +// to become invalid. this can be solved by clearing the cache. But +// unfortunately, gostdlib doesn't privede such method, so we need to +// link to the private var origRlimitNofile in package syscall. +var syscallOrigRlimitNofile atomic.Pointer[syscall.Rlimit] + +func ClearRlimitNofileCache() { + syscallOrigRlimitNofile.Store(nil) +} + type ParentDeathSignal int func (p ParentDeathSignal) Restore() error {