Skip to content

Commit 584afc6

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 <[email protected]>
1 parent ba4b52d commit 584afc6

File tree

4 files changed

+51
-23
lines changed

4 files changed

+51
-23
lines changed

libcontainer/init_linux.go

Lines changed: 9 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,16 @@ 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) {
659658
for _, rlimit := range limits {
660-
if rlimit.Type == resource {
661-
return true
659+
if rlimit.Type == syscall.RLIMIT_NOFILE {
660+
system.ClearRlimitNofileCache(&syscall.Rlimit{
661+
Cur: rlimit.Soft,
662+
Max: rlimit.Hard,
663+
})
664+
return
662665
}
663666
}
664-
return false
665667
}
666668

667669
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)