Source file
src/syscall/syscall_linux_test.go
1
2
3
4
5 package syscall_test
6
7 import (
8 "context"
9 "fmt"
10 "internal/testenv"
11 "io"
12 "io/fs"
13 "os"
14 "os/exec"
15 "path/filepath"
16 "runtime"
17 "slices"
18 "strconv"
19 "strings"
20 "sync"
21 "syscall"
22 "testing"
23 "unsafe"
24 )
25
26 func touch(t *testing.T, name string) {
27 f, err := os.Create(name)
28 if err != nil {
29 t.Fatal(err)
30 }
31 if err := f.Close(); err != nil {
32 t.Fatal(err)
33 }
34 }
35
36 const (
37 _AT_SYMLINK_NOFOLLOW = 0x100
38 _AT_FDCWD = -0x64
39 _AT_EACCESS = 0x200
40 _F_OK = 0
41 _R_OK = 4
42 )
43
44 func TestFaccessat(t *testing.T) {
45 t.Chdir(t.TempDir())
46 touch(t, "file1")
47
48 err := syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, 0)
49 if err != nil {
50 t.Errorf("Faccessat: unexpected error: %v", err)
51 }
52
53 err = syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, 2)
54 if err != syscall.EINVAL {
55 t.Errorf("Faccessat: unexpected error: %v, want EINVAL", err)
56 }
57
58 err = syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, _AT_EACCESS)
59 if err != nil {
60 t.Errorf("Faccessat: unexpected error: %v", err)
61 }
62
63 err = os.Symlink("file1", "symlink1")
64 if err != nil {
65 t.Fatal(err)
66 }
67
68 err = syscall.Faccessat(_AT_FDCWD, "symlink1", _R_OK, _AT_SYMLINK_NOFOLLOW)
69 if err != nil {
70 t.Errorf("Faccessat SYMLINK_NOFOLLOW: unexpected error %v", err)
71 }
72
73
74
75
76
77
78 err = syscall.Fchmodat(_AT_FDCWD, "file1", 0, 0)
79 if err != nil {
80 t.Errorf("Fchmodat: unexpected error %v", err)
81 }
82
83 err = syscall.Faccessat(_AT_FDCWD, "file1", _F_OK, _AT_SYMLINK_NOFOLLOW)
84 if err != nil {
85 t.Errorf("Faccessat: unexpected error: %v", err)
86 }
87
88 err = syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, _AT_SYMLINK_NOFOLLOW)
89 if err != syscall.EACCES {
90 if syscall.Getuid() != 0 {
91 t.Errorf("Faccessat: unexpected error: %v, want EACCES", err)
92 }
93 }
94 }
95
96 func TestFchmodat(t *testing.T) {
97 t.Chdir(t.TempDir())
98
99 touch(t, "file1")
100 os.Symlink("file1", "symlink1")
101
102 err := syscall.Fchmodat(_AT_FDCWD, "symlink1", 0444, 0)
103 if err != nil {
104 t.Fatalf("Fchmodat: unexpected error: %v", err)
105 }
106
107 fi, err := os.Stat("file1")
108 if err != nil {
109 t.Fatal(err)
110 }
111
112 if fi.Mode() != 0444 {
113 t.Errorf("Fchmodat: failed to change mode: expected %v, got %v", 0444, fi.Mode())
114 }
115
116 err = syscall.Fchmodat(_AT_FDCWD, "symlink1", 0444, _AT_SYMLINK_NOFOLLOW)
117 if err != syscall.EOPNOTSUPP {
118 t.Fatalf("Fchmodat: unexpected error: %v, expected EOPNOTSUPP", err)
119 }
120 }
121
122 func TestMain(m *testing.M) {
123 if os.Getenv("GO_DEATHSIG_PARENT") == "1" {
124 deathSignalParent()
125 } else if os.Getenv("GO_DEATHSIG_CHILD") == "1" {
126 deathSignalChild()
127 } else if os.Getenv("GO_SYSCALL_NOERROR") == "1" {
128 syscallNoError()
129 }
130
131 os.Exit(m.Run())
132 }
133
134 func TestParseNetlinkMessage(t *testing.T) {
135 for i, b := range [][]byte{
136 {103, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 11, 0, 1, 0, 0, 0, 0, 5, 8, 0, 3,
137 0, 8, 0, 6, 0, 0, 0, 0, 1, 63, 0, 10, 0, 69, 16, 0, 59, 39, 82, 64, 0, 64, 6, 21, 89, 127, 0, 0,
138 1, 127, 0, 0, 1, 230, 228, 31, 144, 32, 186, 155, 211, 185, 151, 209, 179, 128, 24, 1, 86,
139 53, 119, 0, 0, 1, 1, 8, 10, 0, 17, 234, 12, 0, 17, 189, 126, 107, 106, 108, 107, 106, 13, 10,
140 },
141 {106, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 11, 0, 1, 0, 0, 0, 0, 3, 8, 0, 3,
142 0, 8, 0, 6, 0, 0, 0, 0, 1, 66, 0, 10, 0, 69, 0, 0, 62, 230, 255, 64, 0, 64, 6, 85, 184, 127, 0, 0,
143 1, 127, 0, 0, 1, 237, 206, 31, 144, 73, 197, 128, 65, 250, 60, 192, 97, 128, 24, 1, 86, 253, 21, 0,
144 0, 1, 1, 8, 10, 0, 51, 106, 89, 0, 51, 102, 198, 108, 104, 106, 108, 107, 104, 108, 107, 104, 10,
145 },
146 {102, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 11, 0, 1, 0, 0, 0, 0, 1, 8, 0, 3, 0,
147 8, 0, 6, 0, 0, 0, 0, 1, 62, 0, 10, 0, 69, 0, 0, 58, 231, 2, 64, 0, 64, 6, 85, 185, 127, 0, 0, 1, 127,
148 0, 0, 1, 237, 206, 31, 144, 73, 197, 128, 86, 250, 60, 192, 97, 128, 24, 1, 86, 104, 64, 0, 0, 1, 1, 8,
149 10, 0, 52, 198, 200, 0, 51, 135, 232, 101, 115, 97, 103, 103, 10,
150 },
151 } {
152 m, err := syscall.ParseNetlinkMessage(b)
153 if err != syscall.EINVAL {
154 t.Errorf("#%d: got %v; want EINVAL", i, err)
155 }
156 if m != nil {
157 t.Errorf("#%d: got %v; want nil", i, m)
158 }
159 }
160 }
161
162 func TestSyscallNoError(t *testing.T) {
163
164
165
166 if unsafe.Sizeof(uintptr(0)) != 4 {
167 t.Skip("skipping on non-32bit architecture")
168 }
169
170
171
172
173
174 if runtime.GOARCH == "mips" || runtime.GOARCH == "mipsle" {
175 t.Skipf("skipping on %s", runtime.GOARCH)
176 }
177
178 if os.Getuid() != 0 {
179 t.Skip("skipping root only test")
180 }
181 if testing.Short() && testenv.Builder() != "" && os.Getenv("USER") == "swarming" {
182
183
184
185
186 t.Skip("skipping root only test on a non-root builder")
187 }
188
189 if runtime.GOOS == "android" {
190 t.Skip("skipping on rooted android, see issue 27364")
191 }
192
193
194
195 tempDir := t.TempDir()
196 os.Chmod(tempDir, 0755)
197
198 tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0]))
199
200 src, err := os.Open(os.Args[0])
201 if err != nil {
202 t.Fatalf("cannot open binary %q, %v", os.Args[0], err)
203 }
204 defer src.Close()
205
206 dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
207 if err != nil {
208 t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err)
209 }
210 if _, err := io.Copy(dst, src); err != nil {
211 t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err)
212 }
213 err = dst.Close()
214 if err != nil {
215 t.Fatalf("failed to close test binary %q, %v", tmpBinary, err)
216 }
217
218 uid := uint32(0xfffffffe)
219 err = os.Chown(tmpBinary, int(uid), -1)
220 if err != nil {
221 t.Fatalf("failed to chown test binary %q, %v", tmpBinary, err)
222 }
223
224 err = os.Chmod(tmpBinary, 0755|fs.ModeSetuid)
225 if err != nil {
226 t.Fatalf("failed to set setuid bit on test binary %q, %v", tmpBinary, err)
227 }
228
229 cmd := exec.Command(tmpBinary)
230 cmd.Env = append(os.Environ(), "GO_SYSCALL_NOERROR=1")
231
232 out, err := cmd.CombinedOutput()
233 if err != nil {
234 t.Fatalf("failed to start first child process: %v", err)
235 }
236
237 got := strings.TrimSpace(string(out))
238 want := strconv.FormatUint(uint64(uid)+1, 10) + " / " +
239 strconv.FormatUint(uint64(-uid), 10) + " / " +
240 strconv.FormatUint(uint64(uid), 10)
241 if got != want {
242 if filesystemIsNoSUID(tmpBinary) {
243 t.Skip("skipping test when temp dir is mounted nosuid")
244 }
245
246 t.Errorf("expected %s,\ngot %s", want, got)
247 }
248 }
249
250
251
252 func filesystemIsNoSUID(path string) bool {
253 var st syscall.Statfs_t
254 if syscall.Statfs(path, &st) != nil {
255 return false
256 }
257 return st.Flags&syscall.MS_NOSUID != 0
258 }
259
260 func syscallNoError() {
261
262
263 euid1, _, e := syscall.RawSyscall(syscall.Sys_GETEUID, 0, 0, 0)
264 euid2, _ := syscall.RawSyscallNoError(syscall.Sys_GETEUID, 0, 0, 0)
265
266 fmt.Println(uintptr(euid1), "/", int(e), "/", uintptr(euid2))
267 os.Exit(0)
268 }
269
270
271 const (
272 PR_GET_KEEPCAPS uintptr = 7
273 PR_SET_KEEPCAPS = 8
274 )
275
276
277
278
279 func TestAllThreadsSyscall(t *testing.T) {
280 if _, _, err := syscall.AllThreadsSyscall(syscall.SYS_PRCTL, PR_SET_KEEPCAPS, 0, 0); err == syscall.ENOTSUP {
281 t.Skip("AllThreadsSyscall disabled with cgo")
282 }
283
284 fns := []struct {
285 label string
286 fn func(uintptr) error
287 }{
288 {
289 label: "prctl<3-args>",
290 fn: func(v uintptr) error {
291 _, _, e := syscall.AllThreadsSyscall(syscall.SYS_PRCTL, PR_SET_KEEPCAPS, v, 0)
292 if e != 0 {
293 return e
294 }
295 return nil
296 },
297 },
298 {
299 label: "prctl<6-args>",
300 fn: func(v uintptr) error {
301 _, _, e := syscall.AllThreadsSyscall6(syscall.SYS_PRCTL, PR_SET_KEEPCAPS, v, 0, 0, 0, 0)
302 if e != 0 {
303 return e
304 }
305 return nil
306 },
307 },
308 }
309
310 waiter := func(q <-chan uintptr, r chan<- uintptr, once bool) {
311 for x := range q {
312 runtime.LockOSThread()
313 v, _, e := syscall.Syscall(syscall.SYS_PRCTL, PR_GET_KEEPCAPS, 0, 0)
314 if e != 0 {
315 t.Errorf("tid=%d prctl(PR_GET_KEEPCAPS) failed: %v", syscall.Gettid(), e)
316 } else if x != v {
317 t.Errorf("tid=%d prctl(PR_GET_KEEPCAPS) mismatch: got=%d want=%d", syscall.Gettid(), v, x)
318 }
319 r <- v
320 if once {
321 break
322 }
323 runtime.UnlockOSThread()
324 }
325 }
326
327
328 const launches = 11
329 question := make(chan uintptr)
330 response := make(chan uintptr)
331 defer close(question)
332
333 routines := 0
334 for i, v := range fns {
335 for j := 0; j < launches; j++ {
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350 once := routines%5 == 4
351 go waiter(question, response, once)
352
353
354
355
356
357
358
359 routines++
360
361
362
363
364
365 want := uintptr(j & 1)
366
367
368 if err := v.fn(want); err != nil {
369 t.Errorf("[%d,%d] %s(PR_SET_KEEPCAPS, %d, ...): %v", i, j, v.label, j&1, err)
370 }
371
372
373
374
375 for k := 0; k < routines; k++ {
376 question <- want
377 }
378
379
380
381
382 for k := 0; k < routines; k++ {
383 if got := <-response; got != want {
384 t.Errorf("[%d,%d,%d] waiter result got=%d, want=%d", i, j, k, got, want)
385 }
386 }
387
388
389
390 runtime.Gosched()
391
392 if once {
393
394 routines--
395 }
396
397
398
399 if v, _, e := syscall.Syscall(syscall.SYS_PRCTL, PR_GET_KEEPCAPS, 0, 0); e != 0 {
400 t.Errorf("[%d,%d] prctl(PR_GET_KEEPCAPS) failed: %v", i, j, e)
401 } else if v != want {
402 t.Errorf("[%d,%d] prctl(PR_GET_KEEPCAPS) gave wrong value: got=%v, want=1", i, j, v)
403 }
404 }
405 }
406 }
407
408
409
410 func compareStatus(filter, expect string) error {
411 expected := filter + expect
412 pid := syscall.Getpid()
413 fs, err := os.ReadDir(fmt.Sprintf("/proc/%d/task", pid))
414 if err != nil {
415 return fmt.Errorf("unable to find %d tasks: %v", pid, err)
416 }
417 expectedProc := fmt.Sprintf("Pid:\t%d", pid)
418 foundAThread := false
419 for _, f := range fs {
420 tf := fmt.Sprintf("/proc/%s/status", f.Name())
421 d, err := os.ReadFile(tf)
422 if err != nil {
423
424
425
426
427
428
429
430 continue
431 }
432 lines := strings.Split(string(d), "\n")
433 for _, line := range lines {
434
435 line = strings.TrimSpace(line)
436 if strings.HasPrefix(line, "Pid:\t") {
437
438
439
440
441
442
443
444
445
446 if line != expectedProc {
447 break
448 }
449
450
451
452 }
453 if strings.HasPrefix(line, filter) {
454 if line == expected {
455 foundAThread = true
456 break
457 }
458 if filter == "Groups:" && strings.HasPrefix(line, "Groups:\t") {
459
460
461 a := strings.Split(line[8:], " ")
462 slices.Sort(a)
463 got := strings.Join(a, " ")
464 if got == expected[8:] {
465 foundAThread = true
466 break
467 }
468
469 }
470 return fmt.Errorf("%q got:%q want:%q (bad) [pid=%d file:'%s' %v]\n", tf, line, expected, pid, string(d), expectedProc)
471 }
472 }
473 }
474 if !foundAThread {
475 return fmt.Errorf("found no thread /proc/<TID>/status files for process %q", expectedProc)
476 }
477 return nil
478 }
479
480
481
482 func killAThread(c <-chan struct{}) {
483 runtime.LockOSThread()
484 <-c
485 return
486 }
487
488
489
490
491
492
493
494
495
496
497
498 func TestSetuidEtc(t *testing.T) {
499 if syscall.Getuid() != 0 {
500 t.Skip("skipping root only test")
501 }
502 if syscall.Getgid() != 0 {
503 t.Skip("skipping the test when root's gid is not default value 0")
504 }
505 if testing.Short() && testenv.Builder() != "" && os.Getenv("USER") == "swarming" {
506
507
508
509
510 t.Skip("skipping root only test on a non-root builder")
511 }
512 if _, err := os.Stat("/etc/alpine-release"); err == nil {
513 t.Skip("skipping glibc test on alpine - go.dev/issue/19938")
514 }
515 vs := []struct {
516 call string
517 fn func() error
518 filter, expect string
519 }{
520 {call: "Setegid(1)", fn: func() error { return syscall.Setegid(1) }, filter: "Gid:", expect: "\t0\t1\t0\t1"},
521 {call: "Setegid(0)", fn: func() error { return syscall.Setegid(0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
522
523 {call: "Seteuid(1)", fn: func() error { return syscall.Seteuid(1) }, filter: "Uid:", expect: "\t0\t1\t0\t1"},
524 {call: "Setuid(0)", fn: func() error { return syscall.Setuid(0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"},
525
526 {call: "Setgid(1)", fn: func() error { return syscall.Setgid(1) }, filter: "Gid:", expect: "\t1\t1\t1\t1"},
527 {call: "Setgid(0)", fn: func() error { return syscall.Setgid(0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
528
529 {call: "Setgroups([]int{0,1,2,3})", fn: func() error { return syscall.Setgroups([]int{0, 1, 2, 3}) }, filter: "Groups:", expect: "\t0 1 2 3"},
530 {call: "Setgroups(nil)", fn: func() error { return syscall.Setgroups(nil) }, filter: "Groups:", expect: ""},
531 {call: "Setgroups([]int{0})", fn: func() error { return syscall.Setgroups([]int{0}) }, filter: "Groups:", expect: "\t0"},
532
533 {call: "Setregid(101,0)", fn: func() error { return syscall.Setregid(101, 0) }, filter: "Gid:", expect: "\t101\t0\t0\t0"},
534 {call: "Setregid(0,102)", fn: func() error { return syscall.Setregid(0, 102) }, filter: "Gid:", expect: "\t0\t102\t102\t102"},
535 {call: "Setregid(0,0)", fn: func() error { return syscall.Setregid(0, 0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
536
537 {call: "Setreuid(1,0)", fn: func() error { return syscall.Setreuid(1, 0) }, filter: "Uid:", expect: "\t1\t0\t0\t0"},
538 {call: "Setreuid(0,2)", fn: func() error { return syscall.Setreuid(0, 2) }, filter: "Uid:", expect: "\t0\t2\t2\t2"},
539 {call: "Setreuid(0,0)", fn: func() error { return syscall.Setreuid(0, 0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"},
540
541 {call: "Setresgid(101,0,102)", fn: func() error { return syscall.Setresgid(101, 0, 102) }, filter: "Gid:", expect: "\t101\t0\t102\t0"},
542 {call: "Setresgid(0,102,101)", fn: func() error { return syscall.Setresgid(0, 102, 101) }, filter: "Gid:", expect: "\t0\t102\t101\t102"},
543 {call: "Setresgid(0,0,0)", fn: func() error { return syscall.Setresgid(0, 0, 0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
544
545 {call: "Setresuid(1,0,2)", fn: func() error { return syscall.Setresuid(1, 0, 2) }, filter: "Uid:", expect: "\t1\t0\t2\t0"},
546 {call: "Setresuid(0,2,1)", fn: func() error { return syscall.Setresuid(0, 2, 1) }, filter: "Uid:", expect: "\t0\t2\t1\t2"},
547 {call: "Setresuid(0,0,0)", fn: func() error { return syscall.Setresuid(0, 0, 0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"},
548 }
549
550 for i, v := range vs {
551
552 c := make(chan struct{})
553 go killAThread(c)
554 close(c)
555
556 if err := v.fn(); err != nil {
557 t.Errorf("[%d] %q failed: %v", i, v.call, err)
558 continue
559 }
560 if err := compareStatus(v.filter, v.expect); err != nil {
561 t.Errorf("[%d] %q comparison: %v", i, v.call, err)
562 }
563 }
564 }
565
566
567
568 func TestAllThreadsSyscallError(t *testing.T) {
569
570
571 r1, r2, err := syscall.AllThreadsSyscall(syscall.SYS_CAPGET, 0, 0, 0)
572 if err == syscall.ENOTSUP {
573 t.Skip("AllThreadsSyscall disabled with cgo")
574 }
575 if err != syscall.EFAULT {
576 t.Errorf("AllThreadSyscall(SYS_CAPGET) got %d, %d, %v, want err %v", r1, r2, err, syscall.EFAULT)
577 }
578 }
579
580
581
582
583 func TestAllThreadsSyscallBlockedSyscall(t *testing.T) {
584 if _, _, err := syscall.AllThreadsSyscall(syscall.SYS_PRCTL, PR_SET_KEEPCAPS, 0, 0); err == syscall.ENOTSUP {
585 t.Skip("AllThreadsSyscall disabled with cgo")
586 }
587
588 rd, wr, err := os.Pipe()
589 if err != nil {
590 t.Fatalf("unable to obtain a pipe: %v", err)
591 }
592
593
594 var wg sync.WaitGroup
595 ready := make(chan bool)
596 wg.Add(1)
597 go func() {
598 data := make([]byte, 1)
599
600
601
602
603 ready <- true
604
605
606
607 n, err := syscall.Read(int(rd.Fd()), data)
608 if !(n == 0 && err == nil) {
609 t.Errorf("expected read to return 0, got %d, %s", n, err)
610 }
611
612
613
614 rd.Close()
615 wg.Done()
616 }()
617 <-ready
618
619
620
621 pid := syscall.Getpid()
622 for i := 0; i < 100; i++ {
623 if id, _, e := syscall.AllThreadsSyscall(syscall.SYS_GETPID, 0, 0, 0); e != 0 {
624 t.Errorf("[%d] getpid failed: %v", i, e)
625 } else if int(id) != pid {
626 t.Errorf("[%d] getpid got=%d, want=%d", i, id, pid)
627 }
628
629
630 runtime.Gosched()
631 }
632 wr.Close()
633 wg.Wait()
634 }
635
636 func TestPrlimitSelf(t *testing.T) {
637 origLimit := syscall.OrigRlimitNofile()
638 origRlimitNofile := syscall.GetInternalOrigRlimitNofile()
639
640 if origLimit == nil {
641 defer origRlimitNofile.Store(origLimit)
642 origRlimitNofile.Store(&syscall.Rlimit{
643 Cur: 1024,
644 Max: 65536,
645 })
646 }
647
648
649 var lim syscall.Rlimit
650 if err := syscall.Prlimit(0, syscall.RLIMIT_NOFILE, nil, &lim); err != nil {
651 t.Fatalf("Failed to get the current nofile limit: %v", err)
652 }
653
654 if err := syscall.Prlimit(0, syscall.RLIMIT_NOFILE, &lim, nil); err != nil {
655 t.Fatalf("Prlimit self failed: %v", err)
656 }
657
658 rlimLater := origRlimitNofile.Load()
659 if rlimLater != nil {
660 t.Fatalf("origRlimitNofile got=%v, want=nil", rlimLater)
661 }
662 }
663
664 func TestPrlimitOtherProcess(t *testing.T) {
665 origLimit := syscall.OrigRlimitNofile()
666 origRlimitNofile := syscall.GetInternalOrigRlimitNofile()
667
668 if origLimit == nil {
669 defer origRlimitNofile.Store(origLimit)
670 origRlimitNofile.Store(&syscall.Rlimit{
671 Cur: 1024,
672 Max: 65536,
673 })
674 }
675 rlimOrig := origRlimitNofile.Load()
676
677
678
679 cmd := exec.Command("sleep", "infinity")
680 cmd.Start()
681 defer func() {
682 cmd.Process.Kill()
683 cmd.Process.Wait()
684 }()
685
686
687 var lim syscall.Rlimit
688 if err := syscall.Prlimit(cmd.Process.Pid, syscall.RLIMIT_NOFILE, nil, &lim); err != nil {
689 t.Fatalf("Failed to get the current nofile limit: %v", err)
690 }
691
692 if err := syscall.Prlimit(cmd.Process.Pid, syscall.RLIMIT_NOFILE, &lim, nil); err != nil {
693 t.Fatalf("Prlimit(%d) failed: %v", cmd.Process.Pid, err)
694 }
695
696 rlimLater := origRlimitNofile.Load()
697 if rlimLater != rlimOrig {
698 t.Fatalf("origRlimitNofile got=%v, want=%v", rlimLater, rlimOrig)
699 }
700 }
701
702 const magicRlimitValue = 42
703
704
705
706
707 func TestPrlimitFileLimit(t *testing.T) {
708 switch os.Getenv("GO_WANT_HELPER_PROCESS") {
709 case "prlimit1":
710 testPrlimitFileLimitHelper1(t)
711 return
712 case "prlimit2":
713 testPrlimitFileLimitHelper2(t)
714 return
715 }
716
717 origRlimitNofile := syscall.GetInternalOrigRlimitNofile()
718 defer origRlimitNofile.Store(origRlimitNofile.Load())
719
720
721
722
723 var lim syscall.Rlimit
724 if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim); err != nil {
725 t.Fatal(err)
726 }
727 max := lim.Max
728
729 lim = syscall.Rlimit{
730 Cur: magicRlimitValue + 1,
731 Max: max,
732 }
733 if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &lim); err != nil {
734 t.Fatal(err)
735 }
736
737 ctx, cancel := context.WithCancel(context.Background())
738 defer cancel()
739
740 exe, err := os.Executable()
741 if err != nil {
742 t.Fatal(err)
743 }
744
745 r1, w1, err := os.Pipe()
746 if err != nil {
747 t.Fatal(err)
748 }
749 defer r1.Close()
750 defer w1.Close()
751
752 r2, w2, err := os.Pipe()
753 if err != nil {
754 t.Fatal(err)
755 }
756 defer r2.Close()
757 defer w2.Close()
758
759 var output strings.Builder
760
761 const arg = "-test.run=^TestPrlimitFileLimit$"
762 cmd := testenv.CommandContext(t, ctx, exe, arg, "-test.v")
763 cmd = testenv.CleanCmdEnv(cmd)
764 cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=prlimit1")
765 cmd.ExtraFiles = []*os.File{r1, w2}
766 cmd.Stdout = &output
767 cmd.Stderr = &output
768
769 t.Logf("running %s %s", exe, arg)
770
771 if err := cmd.Start(); err != nil {
772 t.Fatal(err)
773 }
774
775
776 b := make([]byte, 1)
777 if n, err := r2.Read(b); err != nil {
778 t.Fatal(err)
779 } else if n != 1 {
780 t.Fatalf("read %d bytes, want 1", n)
781 }
782
783
784 lim = syscall.Rlimit{
785 Cur: magicRlimitValue,
786 Max: max,
787 }
788 if err := syscall.Prlimit(cmd.Process.Pid, syscall.RLIMIT_NOFILE, &lim, nil); err != nil {
789 t.Fatalf("Prlimit failed: %v", err)
790 }
791
792
793 if n, err := w1.Write(b); err != nil {
794 t.Fatal(err)
795 } else if n != 1 {
796 t.Fatalf("wrote %d bytes, want 1", n)
797 }
798
799 err = cmd.Wait()
800 if output.Len() > 0 {
801 t.Logf("%s", output.String())
802 }
803
804 if err != nil {
805 t.Errorf("child failed: %v", err)
806 }
807 }
808
809
810 func testPrlimitFileLimitHelper1(t *testing.T) {
811 var lim syscall.Rlimit
812 if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim); err != nil {
813 t.Fatal(err)
814 }
815 t.Logf("helper1 rlimit is %v", lim)
816 t.Logf("helper1 cached rlimit is %v", syscall.OrigRlimitNofile())
817
818
819 b := []byte{0}
820 if n, err := syscall.Write(4, b); err != nil {
821 t.Fatal(err)
822 } else if n != 1 {
823 t.Fatalf("wrote %d bytes, want 1", n)
824 }
825
826
827 if n, err := syscall.Read(3, b); err != nil {
828 t.Fatal(err)
829 } else if n != 1 {
830 t.Fatalf("read %d bytes, want 1", n)
831 }
832
833 if err := syscall.Close(3); err != nil {
834 t.Errorf("Close(3): %v", err)
835 }
836 if err := syscall.Close(4); err != nil {
837 t.Errorf("Close(4): %v", err)
838 }
839
840 if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim); err != nil {
841 t.Fatal(err)
842 }
843 t.Logf("after prlimit helper1 rlimit is %v", lim)
844 t.Logf("after prlimit helper1 cached rlimit is %v", syscall.OrigRlimitNofile())
845
846
847
848
849 ctx, cancel := context.WithCancel(context.Background())
850 defer cancel()
851
852 exe, err := os.Executable()
853 if err != nil {
854 t.Fatal(err)
855 }
856
857 const arg = "-test.run=^TestPrlimitFileLimit$"
858 cmd := testenv.CommandContext(t, ctx, exe, arg, "-test.v")
859 cmd = testenv.CleanCmdEnv(cmd)
860 cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=prlimit2")
861 t.Logf("running %s %s", exe, arg)
862 out, err := cmd.CombinedOutput()
863 if len(out) > 0 {
864 t.Logf("%s", out)
865 }
866 if err != nil {
867 t.Errorf("grandchild failed: %v", err)
868 } else {
869 fmt.Println("OK")
870 }
871 }
872
873
874 func testPrlimitFileLimitHelper2(t *testing.T) {
875 var lim syscall.Rlimit
876 if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim); err != nil {
877 t.Fatal(err)
878 }
879
880 t.Logf("helper2 rlimit is %v", lim)
881 cached := syscall.OrigRlimitNofile()
882 t.Logf("helper2 cached rlimit is %v", cached)
883
884
885
886
887 if cached == nil {
888 t.Fatal("no cached rlimit")
889 } else if cached.Cur != magicRlimitValue {
890 t.Fatalf("cached rlimit is %d, want %d", cached.Cur, magicRlimitValue)
891 }
892
893 fmt.Println("OK")
894 }
895
View as plain text