Skip to content

Commit ecf7e00

Browse files
ianlancetaylorIan Lance Taylor
authored and
Ian Lance Taylor
committed
[release-branch.go1.20] syscall: restore original NOFILE rlimit in child process
If we increased the NOFILE rlimit when starting the program, restore the original rlimit when forking a child process. In CL 393354 the os package was changed to raise the open file rlimit at program start. That code is not inherently tied to the os package. This CL moves it into the syscall package. This is a backport of CLs 476096 and 476097 from trunk. For #46279 Fixes #59064 Change-Id: Ib813de896de0a5d28fa2b29afdf414a89fbe7b2a Reviewed-on: https://go-review.googlesource.com/c/go/+/478659 Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: David Chase <[email protected]> Reviewed-by: Michael Knyszek <[email protected]> Reviewed-by: Tobias Klauser <[email protected]>
1 parent 1dbbac7 commit ecf7e00

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+380
-90
lines changed

src/runtime/syscall2_solaris.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import _ "unsafe" // for go:linkname
1717
//go:cgo_import_dynamic libc_ioctl ioctl "libc.so"
1818
//go:cgo_import_dynamic libc_setgid setgid "libc.so"
1919
//go:cgo_import_dynamic libc_setgroups setgroups "libc.so"
20+
//go:cgo_import_dynamic libc_setrlimit setrlimit "libc.so"
2021
//go:cgo_import_dynamic libc_setsid setsid "libc.so"
2122
//go:cgo_import_dynamic libc_setuid setuid "libc.so"
2223
//go:cgo_import_dynamic libc_setpgid setpgid "libc.so"
@@ -34,6 +35,7 @@ import _ "unsafe" // for go:linkname
3435
//go:linkname libc_ioctl libc_ioctl
3536
//go:linkname libc_setgid libc_setgid
3637
//go:linkname libc_setgroups libc_setgroups
38+
//go:linkname libc_setrlimit libc_setrlimit
3739
//go:linkname libc_setsid libc_setsid
3840
//go:linkname libc_setuid libc_setuid
3941
//go:linkname libc_setpgid libc_setpgid

src/runtime/syscall_aix.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import "unsafe"
1818
//go:cgo_import_dynamic libc_ioctl ioctl "libc.a/shr_64.o"
1919
//go:cgo_import_dynamic libc_setgid setgid "libc.a/shr_64.o"
2020
//go:cgo_import_dynamic libc_setgroups setgroups "libc.a/shr_64.o"
21+
//go:cgo_import_dynamic libc_setrlimit setrlimit "libc.a/shr_64.o"
2122
//go:cgo_import_dynamic libc_setsid setsid "libc.a/shr_64.o"
2223
//go:cgo_import_dynamic libc_setuid setuid "libc.a/shr_64.o"
2324
//go:cgo_import_dynamic libc_setpgid setpgid "libc.a/shr_64.o"
@@ -31,6 +32,7 @@ import "unsafe"
3132
//go:linkname libc_ioctl libc_ioctl
3233
//go:linkname libc_setgid libc_setgid
3334
//go:linkname libc_setgroups libc_setgroups
35+
//go:linkname libc_setrlimit libc_setrlimit
3436
//go:linkname libc_setsid libc_setsid
3537
//go:linkname libc_setuid libc_setuid
3638
//go:linkname libc_setpgid libc_setpgid
@@ -45,6 +47,7 @@ var (
4547
libc_ioctl,
4648
libc_setgid,
4749
libc_setgroups,
50+
libc_setrlimit,
4851
libc_setsid,
4952
libc_setuid,
5053
libc_setpgid libFunc
@@ -199,6 +202,13 @@ func syscall_setgroups1(ngid, gid uintptr) (err uintptr) {
199202
return
200203
}
201204

205+
//go:linkname syscall_setrlimit1 syscall.setrlimit1
206+
//go:nosplit
207+
func syscall_setrlimit1(which uintptr, lim unsafe.Pointer) (err uintptr) {
208+
_, err = syscall2(&libc_setrlimit, which, uintptr(lim))
209+
return
210+
}
211+
202212
//go:linkname syscall_setsid syscall.setsid
203213
//go:nosplit
204214
func syscall_setsid() (pid, err uintptr) {

src/runtime/syscall_solaris.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ var (
1818
libc_ioctl,
1919
libc_setgid,
2020
libc_setgroups,
21+
libc_setrlimit,
2122
libc_setsid,
2223
libc_setuid,
2324
libc_setpgid,
@@ -234,6 +235,19 @@ func syscall_setgroups(ngid, gid uintptr) (err uintptr) {
234235
return call.err
235236
}
236237

238+
//go:nosplit
239+
//go:linkname syscall_setrlimit
240+
//go:cgo_unsafe_args
241+
func syscall_setrlimit(which uintptr, lim unsafe.Pointer) (err uintptr) {
242+
call := libcall{
243+
fn: uintptr(unsafe.Pointer(&libc_setrlimit)),
244+
n: 2,
245+
args: uintptr(unsafe.Pointer(&which)),
246+
}
247+
asmcgocall(unsafe.Pointer(&asmsysvicall6x), unsafe.Pointer(&call))
248+
return call.err
249+
}
250+
237251
//go:nosplit
238252
//go:linkname syscall_setsid
239253
func syscall_setsid() (pid, err uintptr) {

src/syscall/asm_solaris_amd64.s

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ TEXT ·setgid(SB),NOSPLIT,$0
6060
TEXT ·setgroups1(SB),NOSPLIT,$0
6161
JMP runtime·syscall_setgroups(SB)
6262

63+
TEXT ·setrlimit1(SB),NOSPLIT,$0
64+
JMP runtime·syscall_setrlimit(SB)
65+
6366
TEXT ·setsid(SB),NOSPLIT,$0
6467
JMP runtime·syscall_setsid(SB)
6568

src/syscall/exec_bsd.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
6161
i int
6262
)
6363

64+
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
65+
6466
// guard against side effects of shuffling fds below.
6567
// Make sure that nextfd is beyond any currently open files so
6668
// that we can't run the risk of overwriting any of them.
@@ -270,6 +272,11 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
270272
}
271273
}
272274

275+
// Restore original rlimit.
276+
if rlimOK && rlim.Cur != 0 {
277+
RawSyscall(SYS_SETRLIMIT, uintptr(RLIMIT_NOFILE), uintptr(unsafe.Pointer(&rlim)), 0)
278+
}
279+
273280
// Time to exec.
274281
_, _, err1 = RawSyscall(SYS_EXECVE,
275282
uintptr(unsafe.Pointer(argv0)),

src/syscall/exec_freebsd.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
6666
i int
6767
)
6868

69+
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
70+
6971
// Record parent PID so child can test if it has died.
7072
ppid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
7173

@@ -283,6 +285,11 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
283285
}
284286
}
285287

288+
// Restore original rlimit.
289+
if rlimOK && rlim.Cur != 0 {
290+
RawSyscall(SYS_SETRLIMIT, uintptr(RLIMIT_NOFILE), uintptr(unsafe.Pointer(&rlim)), 0)
291+
}
292+
286293
// Time to exec.
287294
_, _, err1 = RawSyscall(SYS_EXECVE,
288295
uintptr(unsafe.Pointer(argv0)),

src/syscall/exec_libc.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func getpid() (pid uintptr, err Errno)
5353
func ioctl(fd uintptr, req uintptr, arg uintptr) (err Errno)
5454
func setgid(gid uintptr) (err Errno)
5555
func setgroups1(ngid uintptr, gid uintptr) (err Errno)
56+
func setrlimit1(which uintptr, lim unsafe.Pointer) (err Errno)
5657
func setsid() (pid uintptr, err Errno)
5758
func setuid(uid uintptr) (err Errno)
5859
func setpgid(pid uintptr, pgid uintptr) (err Errno)
@@ -87,6 +88,8 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
8788
i int
8889
)
8990

91+
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
92+
9093
// guard against side effects of shuffling fds below.
9194
// Make sure that nextfd is beyond any currently open files so
9295
// that we can't run the risk of overwriting any of them.
@@ -289,6 +292,11 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
289292
}
290293
}
291294

295+
// Restore original rlimit.
296+
if rlimOK && rlim.Cur != 0 {
297+
setrlimit1(RLIMIT_NOFILE, unsafe.Pointer(&rlim))
298+
}
299+
292300
// Time to exec.
293301
err1 = execve(
294302
uintptr(unsafe.Pointer(argv0)),

src/syscall/exec_libc2.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
6262
i int
6363
)
6464

65+
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
66+
6567
// guard against side effects of shuffling fds below.
6668
// Make sure that nextfd is beyond any currently open files so
6769
// that we can't run the risk of overwriting any of them.
@@ -266,6 +268,11 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
266268
}
267269
}
268270

271+
// Restore original rlimit.
272+
if rlimOK && rlim.Cur != 0 {
273+
rawSyscall(abi.FuncPCABI0(libc_setrlimit_trampoline), uintptr(RLIMIT_NOFILE), uintptr(unsafe.Pointer(&rlim)), 0)
274+
}
275+
269276
// Time to exec.
270277
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_execve_trampoline),
271278
uintptr(unsafe.Pointer(argv0)),

src/syscall/exec_linux.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att
228228
clone3 *cloneArgs
229229
)
230230

231+
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
232+
231233
if sys.UidMappings != nil {
232234
puid = []byte("/proc/self/uid_map\000")
233235
uidmap = formatIDMappings(sys.UidMappings)
@@ -592,6 +594,11 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att
592594
}
593595
}
594596

597+
// Restore original rlimit.
598+
if rlimOK && rlim.Cur != 0 {
599+
rawSetrlimit(RLIMIT_NOFILE, &rlim)
600+
}
601+
595602
// Enable tracing if requested.
596603
// Do this right before exec so that we don't unnecessarily trace the runtime
597604
// setting up after the fork. See issue #21428.

src/syscall/exec_unix.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ func Exec(argv0 string, argv []string, envv []string) (err error) {
282282
}
283283
runtime_BeforeExec()
284284

285+
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
286+
if rlimOK && rlim.Cur != 0 {
287+
Setrlimit(RLIMIT_NOFILE, &rlim)
288+
}
289+
285290
var err1 error
286291
if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "aix" {
287292
// RawSyscall should never be used on Solaris, illumos, or AIX.

src/syscall/exec_unix_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77
package syscall_test
88

99
import (
10+
"bytes"
11+
"fmt"
1012
"internal/testenv"
1113
"io"
1214
"math/rand"
1315
"os"
1416
"os/exec"
1517
"os/signal"
18+
"strconv"
1619
"syscall"
1720
"testing"
1821
"time"
@@ -345,3 +348,42 @@ func TestExecHelper(t *testing.T) {
345348

346349
t.Error("syscall.Exec returned")
347350
}
351+
352+
// Test that rlimit values are restored by exec.
353+
func TestRlimitRestored(t *testing.T) {
354+
if os.Getenv("GO_WANT_HELPER_PROCESS") != "" {
355+
fmt.Println(syscall.OrigRlimitNofile().Cur)
356+
os.Exit(0)
357+
}
358+
359+
orig := syscall.OrigRlimitNofile()
360+
if orig.Cur == 0 {
361+
t.Skip("skipping test because rlimit not adjusted at startup")
362+
}
363+
364+
executable, err := os.Executable()
365+
if err != nil {
366+
executable = os.Args[0]
367+
}
368+
369+
cmd := testenv.Command(t, executable, "-test.run=TestRlimitRestored")
370+
cmd = testenv.CleanCmdEnv(cmd)
371+
cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
372+
373+
out, err := cmd.CombinedOutput()
374+
if len(out) > 0 {
375+
t.Logf("%s", out)
376+
}
377+
if err != nil {
378+
t.Fatalf("subprocess failed: %v", err)
379+
}
380+
s := string(bytes.TrimSpace(out))
381+
v, err := strconv.ParseUint(s, 10, 64)
382+
if err != nil {
383+
t.Fatalf("could not parse %q as number: %v", s, v)
384+
}
385+
386+
if v != uint64(orig.Cur) {
387+
t.Errorf("exec rlimit = %d, want %d", v, orig)
388+
}
389+
}

src/syscall/export_rlimit_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build unix
6+
7+
package syscall
8+
9+
func OrigRlimitNofile() Rlimit {
10+
if rlim, ok := origRlimitNofile.Load().(Rlimit); ok {
11+
return rlim
12+
}
13+
return Rlimit{0, 0}
14+
}

src/os/rlimit.go renamed to src/syscall/rlimit.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,16 @@
44

55
//go:build unix
66

7-
package os
7+
package syscall
88

9-
import "syscall"
9+
import (
10+
"sync/atomic"
11+
)
12+
13+
// origRlimitNofile, if not {0, 0}, is the original soft RLIMIT_NOFILE.
14+
// When we can assume that we are bootstrapping with Go 1.19,
15+
// this can be atomic.Pointer[Rlimit].
16+
var origRlimitNofile atomic.Value // of Rlimit
1017

1118
// Some systems set an artificially low soft limit on open file count, for compatibility
1219
// with code that uses select and its hard-coded maximum file descriptor
@@ -23,10 +30,21 @@ import "syscall"
2330
// Code that really wants Go to leave the limit alone can set the hard limit,
2431
// which Go of course has no choice but to respect.
2532
func init() {
26-
var lim syscall.Rlimit
27-
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim); err == nil && lim.Cur != lim.Max {
33+
var lim Rlimit
34+
if err := Getrlimit(RLIMIT_NOFILE, &lim); err == nil && lim.Cur != lim.Max {
35+
origRlimitNofile.Store(lim)
2836
lim.Cur = lim.Max
2937
adjustFileLimit(&lim)
30-
syscall.Setrlimit(syscall.RLIMIT_NOFILE, &lim)
38+
setrlimit(RLIMIT_NOFILE, &lim)
39+
}
40+
}
41+
42+
func Setrlimit(resource int, rlim *Rlimit) error {
43+
err := setrlimit(resource, rlim)
44+
if err == nil && resource == RLIMIT_NOFILE {
45+
// Store zeroes in origRlimitNofile to tell StartProcess
46+
// to not adjust the rlimit in the child process.
47+
origRlimitNofile.Store(Rlimit{0, 0})
3148
}
49+
return err
3250
}

src/os/rlimit_darwin.go renamed to src/syscall/rlimit_darwin.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@
44

55
//go:build darwin
66

7-
package os
8-
9-
import "syscall"
7+
package syscall
108

119
// adjustFileLimit adds per-OS limitations on the Rlimit used for RLIMIT_NOFILE. See rlimit.go.
12-
func adjustFileLimit(lim *syscall.Rlimit) {
10+
func adjustFileLimit(lim *Rlimit) {
1311
// On older macOS, setrlimit(RLIMIT_NOFILE, lim) with lim.Cur = infinity fails.
1412
// Set to the value of kern.maxfilesperproc instead.
15-
n, err := syscall.SysctlUint32("kern.maxfilesperproc")
13+
n, err := SysctlUint32("kern.maxfilesperproc")
1614
if err != nil {
1715
return
1816
}

src/os/rlimit_stub.go renamed to src/syscall/rlimit_stub.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44

55
//go:build aix || dragonfly || freebsd || linux || netbsd || openbsd || solaris
66

7-
package os
8-
9-
import "syscall"
7+
package syscall
108

119
// adjustFileLimit adds per-OS limitations on the Rlimit used for RLIMIT_NOFILE. See rlimit.go.
12-
func adjustFileLimit(lim *syscall.Rlimit) {}
10+
func adjustFileLimit(lim *Rlimit) {}

src/os/rlimit_test.go renamed to src/syscall/rlimit_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
package os_test
5+
package syscall_test
66

77
import (
8-
. "os"
8+
"os"
99
"runtime"
1010
"testing"
1111
)
@@ -24,9 +24,9 @@ func TestOpenFileLimit(t *testing.T) {
2424
fileCount = 768
2525
}
2626

27-
var files []*File
27+
var files []*os.File
2828
for i := 0; i < fileCount; i++ {
29-
f, err := Open("rlimit.go")
29+
f, err := os.Open("rlimit.go")
3030
if err != nil {
3131
t.Error(err)
3232
break

src/syscall/syscall_aix.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ func PtraceDetach(pid int) (err error) { return ptrace64(PT_DETACH, int64(pid),
633633
//sys Setpriority(which int, who int, prio int) (err error)
634634
//sysnb Setregid(rgid int, egid int) (err error)
635635
//sysnb Setreuid(ruid int, euid int) (err error)
636-
//sysnb Setrlimit(which int, lim *Rlimit) (err error)
636+
//sysnb setrlimit(which int, lim *Rlimit) (err error)
637637
//sys Stat(path string, stat *Stat_t) (err error)
638638
//sys Statfs(path string, buf *Statfs_t) (err error)
639639
//sys Symlink(path string, link string) (err error)

0 commit comments

Comments
 (0)