Skip to content

Commit f5eef58

Browse files
ianlancetaylorIan Lance Taylor
authored and
Ian Lance Taylor
committed
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. For #46279 Change-Id: Ia5d2af9ef435e5932965c15eec2e428d2130d230 Reviewed-on: https://go-review.googlesource.com/c/go/+/476097 Reviewed-by: Bryan Mills <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> TryBot-Bypass: Ian Lance Taylor <[email protected]>
1 parent 491153a commit f5eef58

Some content is hidden

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

63 files changed

+369
-73
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
@@ -64,6 +64,8 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
6464
ngroups, groups uintptr
6565
)
6666

67+
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
68+
6769
// guard against side effects of shuffling fds below.
6870
// Make sure that nextfd is beyond any currently open files so
6971
// that we can't run the risk of overwriting any of them.
@@ -273,6 +275,11 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
273275
}
274276
}
275277

278+
// Restore original rlimit.
279+
if rlimOK && rlim.Cur != 0 {
280+
RawSyscall(SYS_SETRLIMIT, uintptr(RLIMIT_NOFILE), uintptr(unsafe.Pointer(&rlim)), 0)
281+
}
282+
276283
// Time to exec.
277284
_, _, err1 = RawSyscall(SYS_EXECVE,
278285
uintptr(unsafe.Pointer(argv0)),

src/syscall/exec_freebsd.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
7171
upid uintptr
7272
)
7373

74+
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
75+
7476
// Record parent PID so child can test if it has died.
7577
ppid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
7678

@@ -297,6 +299,11 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
297299
}
298300
}
299301

302+
// Restore original rlimit.
303+
if rlimOK && rlim.Cur != 0 {
304+
RawSyscall(SYS_SETRLIMIT, uintptr(RLIMIT_NOFILE), uintptr(unsafe.Pointer(&rlim)), 0)
305+
}
306+
300307
// Time to exec.
301308
_, _, err1 = RawSyscall(SYS_EXECVE,
302309
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)
@@ -90,6 +91,8 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
9091
ngroups, groups uintptr
9192
)
9293

94+
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
95+
9396
// guard against side effects of shuffling fds below.
9497
// Make sure that nextfd is beyond any currently open files so
9598
// that we can't run the risk of overwriting any of them.
@@ -292,6 +295,11 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
292295
}
293296
}
294297

298+
// Restore original rlimit.
299+
if rlimOK && rlim.Cur != 0 {
300+
setrlimit1(RLIMIT_NOFILE, unsafe.Pointer(&rlim))
301+
}
302+
295303
// Time to exec.
296304
err1 = execve(
297305
uintptr(unsafe.Pointer(argv0)),

src/syscall/exec_libc2.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
6565
ngroups, groups uintptr
6666
)
6767

68+
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
69+
6870
// guard against side effects of shuffling fds below.
6971
// Make sure that nextfd is beyond any currently open files so
7072
// that we can't run the risk of overwriting any of them.
@@ -269,6 +271,11 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
269271
}
270272
}
271273

274+
// Restore original rlimit.
275+
if rlimOK && rlim.Cur != 0 {
276+
rawSyscall(abi.FuncPCABI0(libc_setrlimit_trampoline), uintptr(RLIMIT_NOFILE), uintptr(unsafe.Pointer(&rlim)), 0)
277+
}
278+
272279
// Time to exec.
273280
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_execve_trampoline),
274281
uintptr(unsafe.Pointer(argv0)),

src/syscall/exec_linux.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,8 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att
240240
c uintptr
241241
)
242242

243+
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
244+
243245
if sys.UidMappings != nil {
244246
puid = []byte("/proc/self/uid_map\000")
245247
uidmap = formatIDMappings(sys.UidMappings)
@@ -609,6 +611,11 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att
609611
}
610612
}
611613

614+
// Restore original rlimit.
615+
if rlimOK && rlim.Cur != 0 {
616+
rawSetrlimit(RLIMIT_NOFILE, &rlim)
617+
}
618+
612619
// Enable tracing if requested.
613620
// Do this right before exec so that we don't unnecessarily trace the runtime
614621
// 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/syscall/rlimit.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@
66

77
package syscall
88

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
17+
918
// Some systems set an artificially low soft limit on open file count, for compatibility
1019
// with code that uses select and its hard-coded maximum file descriptor
1120
// (limited by the size of fd_set).
@@ -23,8 +32,19 @@ package syscall
2332
func init() {
2433
var lim Rlimit
2534
if err := Getrlimit(RLIMIT_NOFILE, &lim); err == nil && lim.Cur != lim.Max {
35+
origRlimitNofile.Store(lim)
2636
lim.Cur = lim.Max
2737
adjustFileLimit(&lim)
28-
Setrlimit(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})
2948
}
49+
return err
3050
}

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)

src/syscall/syscall_darwin.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1)
195195
//sys Setprivexec(flag int) (err error)
196196
//sysnb Setregid(rgid int, egid int) (err error)
197197
//sysnb Setreuid(ruid int, euid int) (err error)
198-
//sysnb Setrlimit(which int, lim *Rlimit) (err error)
198+
//sysnb setrlimit(which int, lim *Rlimit) (err error)
199199
//sysnb Setsid() (pid int, err error)
200200
//sysnb Settimeofday(tp *Timeval) (err error)
201201
//sysnb Setuid(uid int) (err error)

src/syscall/syscall_dragonfly.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
240240
//sys Setpriority(which int, who int, prio int) (err error)
241241
//sysnb Setregid(rgid int, egid int) (err error)
242242
//sysnb Setreuid(ruid int, euid int) (err error)
243-
//sysnb Setrlimit(which int, lim *Rlimit) (err error)
243+
//sysnb setrlimit(which int, lim *Rlimit) (err error)
244244
//sysnb Setsid() (pid int, err error)
245245
//sysnb Settimeofday(tp *Timeval) (err error)
246246
//sysnb Setuid(uid int) (err error)

src/syscall/syscall_freebsd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ func Mknod(path string, mode uint32, dev uint64) (err error) {
234234
//sys Setpriority(which int, who int, prio int) (err error)
235235
//sysnb Setregid(rgid int, egid int) (err error)
236236
//sysnb Setreuid(ruid int, euid int) (err error)
237-
//sysnb Setrlimit(which int, lim *Rlimit) (err error)
237+
//sysnb setrlimit(which int, lim *Rlimit) (err error)
238238
//sysnb Setsid() (pid int, err error)
239239
//sysnb Settimeofday(tp *Timeval) (err error)
240240
//sysnb Setuid(uid int) (err error)

src/syscall/syscall_linux.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,7 @@ func Getpgrp() (pid int) {
10581058
//sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error)
10591059
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
10601060
//sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT
1061-
//sysnb prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) = SYS_PRLIMIT64
1061+
//sysnb prlimit1(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) = SYS_PRLIMIT64
10621062
//sys read(fd int, p []byte) (n int, err error)
10631063
//sys Removexattr(path string, attr string) (err error)
10641064
//sys Setdomainname(p []byte) (err error)
@@ -1261,3 +1261,14 @@ func Munmap(b []byte) (err error) {
12611261
//sys Munlock(b []byte) (err error)
12621262
//sys Mlockall(flags int) (err error)
12631263
//sys Munlockall() (err error)
1264+
1265+
// prlimit changes a resource limit. We use a single definition so that
1266+
// we can tell StartProcess to not restore the original NOFILE limit.
1267+
// This is unexported but can be called from x/sys/unix.
1268+
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
1269+
err = prlimit1(pid, resource, newlimit, old)
1270+
if err == nil && newlimit != nil && resource == RLIMIT_NOFILE {
1271+
origRlimitNofile.Store(Rlimit{0, 0})
1272+
}
1273+
return err
1274+
}

0 commit comments

Comments
 (0)