Skip to content

Commit 76d1b9f

Browse files
committed
libct/system: ClearRlimitNofileCache for go 1.23
Go 1.23 tightens access to internal symbols, and even puts runc into "hall of shame" for using an internal symbol (recently added by commit da68c8e). So, while not impossible, it becomes harder to access those internal symbols, and it is a bad idea in general. Since Go 1.23 includes https://go.dev/cl/588076, we can clean the internal rlimit cache by setting the RLIMIT_NOFILE for ourselves, essentially disabling the rlimit cache. Once Go 1.22 is no longer supported, we will remove the go:linkname hack. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
1 parent ba4b52d commit 76d1b9f

4 files changed

Lines changed: 52 additions & 23 deletions

File tree

libcontainer/init_linux.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"runtime/debug"
1313
"strconv"
1414
"strings"
15+
"syscall"
1516

1617
"github.com/containerd/console"
1718
"github.com/moby/sys/user"
@@ -225,9 +226,7 @@ func containerInit(t initType, config *initConfig, pipe *syncSocket, consoleSock
225226

226227
// Clean the RLIMIT_NOFILE cache in go runtime.
227228
// Issue: https://github.com/opencontainers/runc/issues/4195
228-
if containsRlimit(config.Rlimits, unix.RLIMIT_NOFILE) {
229-
system.ClearRlimitNofileCache()
230-
}
229+
maybeClearRlimitNofileCache(config.Rlimits)
231230

232231
switch t {
233232
case initSetns:
@@ -655,13 +654,17 @@ func setupRoute(config *configs.Config) error {
655654
return nil
656655
}
657656

658-
func containsRlimit(limits []configs.Rlimit, resource int) bool {
657+
func maybeClearRlimitNofileCache(limits []configs.Rlimit) {
658+
const res = unix.RLIMIT_NOFILE
659659
for _, rlimit := range limits {
660-
if rlimit.Type == resource {
661-
return true
660+
if rlimit.Type == res {
661+
system.ClearRlimitNofileCache(&syscall.Rlimit{
662+
Cur: rlimit.Soft,
663+
Max: rlimit.Hard,
664+
})
665+
return
662666
}
663667
}
664-
return false
665668
}
666669

667670
func setupRlimits(limits []configs.Rlimit, pid int) error {

libcontainer/system/linux.go

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,13 @@ import (
88
"io"
99
"os"
1010
"strconv"
11-
"sync/atomic"
1211
"syscall"
1312
"unsafe"
1413

1514
"github.com/sirupsen/logrus"
1615
"golang.org/x/sys/unix"
1716
)
1817

19-
//go:linkname syscallOrigRlimitNofile syscall.origRlimitNofile
20-
var syscallOrigRlimitNofile atomic.Pointer[syscall.Rlimit]
21-
22-
// ClearRlimitNofileCache is to clear go runtime's nofile rlimit cache.
23-
func ClearRlimitNofileCache() {
24-
// As reported in issue #4195, the new version of go runtime(since 1.19)
25-
// will cache rlimit-nofile. Before executing execve, the rlimit-nofile
26-
// of the process will be restored with the cache. In runc, this will
27-
// cause the rlimit-nofile setting by the parent process for the container
28-
// to become invalid. It can be solved by clearing this cache. But
29-
// unfortunately, go stdlib doesn't provide such function, so we need to
30-
// link to the private var `origRlimitNofile` in package syscall to hack.
31-
syscallOrigRlimitNofile.Store(nil)
32-
}
33-
3418
type ParentDeathSignal int
3519

3620
func (p ParentDeathSignal) Restore() error {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//go:build go1.23
2+
3+
package system
4+
5+
import (
6+
"syscall"
7+
)
8+
9+
// ClearRlimitNofileCache clears go runtime's nofile rlimit cache. The argument
10+
// is process RLIMIT_NOFILE values. Relies on go.dev/cl/588076.
11+
func ClearRlimitNofileCache(lim *syscall.Rlimit) {
12+
// Ignore the return values since we only need to clean the cache,
13+
// the limit is going to be set via unix.Prlimit elsewhere.
14+
_ = syscall.Setrlimit(syscall.RLIMIT_NOFILE, lim)
15+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//go:build !go1.23
2+
3+
// TODO: remove this file once go 1.22 is no longer supported.
4+
5+
package system
6+
7+
import (
8+
"sync/atomic"
9+
"syscall"
10+
_ "unsafe" // Needed for go:linkname to work.
11+
)
12+
13+
//go:linkname syscallOrigRlimitNofile syscall.origRlimitNofile
14+
var syscallOrigRlimitNofile atomic.Pointer[syscall.Rlimit]
15+
16+
// ClearRlimitNofileCache clears go runtime's nofile rlimit cache.
17+
// The argument is process RLIMIT_NOFILE values.
18+
func ClearRlimitNofileCache(_ *syscall.Rlimit) {
19+
// As reported in issue #4195, the new version of go runtime(since 1.19)
20+
// will cache rlimit-nofile. Before executing execve, the rlimit-nofile
21+
// of the process will be restored with the cache. In runc, this will
22+
// cause the rlimit-nofile setting by the parent process for the container
23+
// to become invalid. It can be solved by clearing this cache. But
24+
// unfortunately, go stdlib doesn't provide such function, so we need to
25+
// link to the private var `origRlimitNofile` in package syscall to hack.
26+
syscallOrigRlimitNofile.Store(nil)
27+
}

0 commit comments

Comments
 (0)