diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go index 7e1298c4d16..e0a65d5cc8f 100644 --- a/libcontainer/init_linux.go +++ b/libcontainer/init_linux.go @@ -12,6 +12,7 @@ import ( "runtime/debug" "strconv" "strings" + "syscall" "github.com/containerd/console" "github.com/moby/sys/user" @@ -225,9 +226,7 @@ func containerInit(t initType, config *initConfig, pipe *syncSocket, consoleSock // Clean the RLIMIT_NOFILE cache in go runtime. // Issue: https://github.com/opencontainers/runc/issues/4195 - if containsRlimit(config.Rlimits, unix.RLIMIT_NOFILE) { - system.ClearRlimitNofileCache() - } + maybeClearRlimitNofileCache(config.Rlimits) switch t { case initSetns: @@ -655,13 +654,16 @@ func setupRoute(config *configs.Config) error { return nil } -func containsRlimit(limits []configs.Rlimit, resource int) bool { +func maybeClearRlimitNofileCache(limits []configs.Rlimit) { for _, rlimit := range limits { - if rlimit.Type == resource { - return true + if rlimit.Type == syscall.RLIMIT_NOFILE { + system.ClearRlimitNofileCache(&syscall.Rlimit{ + Cur: rlimit.Soft, + Max: rlimit.Hard, + }) + return } } - return false } func setupRlimits(limits []configs.Rlimit, pid int) error { diff --git a/libcontainer/system/linux.go b/libcontainer/system/linux.go index 4d5565b25f4..368364baf04 100644 --- a/libcontainer/system/linux.go +++ b/libcontainer/system/linux.go @@ -8,7 +8,6 @@ import ( "io" "os" "strconv" - "sync/atomic" "syscall" "unsafe" @@ -16,21 +15,6 @@ import ( "golang.org/x/sys/unix" ) -//go:linkname syscallOrigRlimitNofile syscall.origRlimitNofile -var syscallOrigRlimitNofile atomic.Pointer[syscall.Rlimit] - -// ClearRlimitNofileCache is to clear go runtime's nofile rlimit cache. -func ClearRlimitNofileCache() { - // As reported in issue #4195, the new version of go runtime(since 1.19) - // will cache rlimit-nofile. Before executing execve, the rlimit-nofile - // of the process will be restored with the cache. In runc, this will - // cause the rlimit-nofile setting by the parent process for the container - // to become invalid. It can be solved by clearing this cache. But - // unfortunately, go stdlib doesn't provide such function, so we need to - // link to the private var `origRlimitNofile` in package syscall to hack. - syscallOrigRlimitNofile.Store(nil) -} - type ParentDeathSignal int func (p ParentDeathSignal) Restore() error { diff --git a/libcontainer/system/rlimit_linux.go b/libcontainer/system/rlimit_linux.go new file mode 100644 index 00000000000..4595fa82aa1 --- /dev/null +++ b/libcontainer/system/rlimit_linux.go @@ -0,0 +1,15 @@ +//go:build go1.23 + +package system + +import ( + "syscall" +) + +// ClearRlimitNofileCache clears go runtime's nofile rlimit cache. The argument +// is process RLIMIT_NOFILE values. Relies on go.dev/cl/588076. +func ClearRlimitNofileCache(lim *syscall.Rlimit) { + // Ignore the return values since we only need to clean the cache, + // the limit is going to be set via unix.Prlimit elsewhere. + _ = syscall.Setrlimit(syscall.RLIMIT_NOFILE, lim) +} diff --git a/libcontainer/system/rlimit_linux_go122.go b/libcontainer/system/rlimit_linux_go122.go new file mode 100644 index 00000000000..865d1802214 --- /dev/null +++ b/libcontainer/system/rlimit_linux_go122.go @@ -0,0 +1,27 @@ +//go:build !go1.23 + +// TODO: remove this file once go 1.22 is no longer supported. + +package system + +import ( + "sync/atomic" + "syscall" + _ "unsafe" // Needed for go:linkname to work. +) + +//go:linkname syscallOrigRlimitNofile syscall.origRlimitNofile +var syscallOrigRlimitNofile atomic.Pointer[syscall.Rlimit] + +// ClearRlimitNofileCache clears go runtime's nofile rlimit cache. +// The argument is process RLIMIT_NOFILE values. +func ClearRlimitNofileCache(_ *syscall.Rlimit) { + // As reported in issue #4195, the new version of go runtime(since 1.19) + // will cache rlimit-nofile. Before executing execve, the rlimit-nofile + // of the process will be restored with the cache. In runc, this will + // cause the rlimit-nofile setting by the parent process for the container + // to become invalid. It can be solved by clearing this cache. But + // unfortunately, go stdlib doesn't provide such function, so we need to + // link to the private var `origRlimitNofile` in package syscall to hack. + syscallOrigRlimitNofile.Store(nil) +}