Source file
src/syscall/exec_linux_test.go
1
2
3
4
5
6
7 package syscall_test
8
9 import (
10 "bytes"
11 "errors"
12 "flag"
13 "fmt"
14 "internal/platform"
15 "internal/syscall/unix"
16 "internal/testenv"
17 "io"
18 "os"
19 "os/exec"
20 "os/user"
21 "path"
22 "path/filepath"
23 "runtime"
24 "strconv"
25 "strings"
26 "syscall"
27 "testing"
28 "time"
29 "unsafe"
30 )
31
32
33
34 func whoamiNEWUSER(t *testing.T, uid, gid int, setgroups bool) *exec.Cmd {
35 t.Helper()
36 testenv.MustHaveExecPath(t, "whoami")
37 cmd := testenv.Command(t, "whoami")
38 cmd.SysProcAttr = &syscall.SysProcAttr{
39 Cloneflags: syscall.CLONE_NEWUSER,
40 UidMappings: []syscall.SysProcIDMap{
41 {ContainerID: 0, HostID: uid, Size: 1},
42 },
43 GidMappings: []syscall.SysProcIDMap{
44 {ContainerID: 0, HostID: gid, Size: 1},
45 },
46 GidMappingsEnableSetgroups: setgroups,
47 }
48 return cmd
49 }
50
51 func TestCloneNEWUSERAndRemap(t *testing.T) {
52 for _, setgroups := range []bool{false, true} {
53 setgroups := setgroups
54 t.Run(fmt.Sprintf("setgroups=%v", setgroups), func(t *testing.T) {
55 uid := os.Getuid()
56 gid := os.Getgid()
57
58 cmd := whoamiNEWUSER(t, uid, gid, setgroups)
59 out, err := cmd.CombinedOutput()
60 t.Logf("%v: %v", cmd, err)
61
62 if uid != 0 && setgroups {
63 t.Logf("as non-root, expected permission error due to unprivileged gid_map")
64 if !os.IsPermission(err) {
65 if err == nil {
66 t.Skipf("unexpected success: probably old kernel without security fix?")
67 }
68 if testenv.SyscallIsNotSupported(err) {
69 t.Skipf("skipping: CLONE_NEWUSER appears to be unsupported")
70 }
71 t.Fatalf("got non-permission error")
72 }
73 return
74 }
75
76 if err != nil {
77 if testenv.SyscallIsNotSupported(err) {
78
79 t.Skipf("skipping: CLONE_NEWUSER appears to be unsupported")
80 }
81 t.Fatalf("unexpected command failure; output:\n%s", out)
82 }
83
84 sout := strings.TrimSpace(string(out))
85 want := "root"
86 if sout != want {
87 t.Fatalf("whoami = %q; want %q", out, want)
88 }
89 })
90 }
91 }
92
93 func TestEmptyCredGroupsDisableSetgroups(t *testing.T) {
94 cmd := whoamiNEWUSER(t, os.Getuid(), os.Getgid(), false)
95 cmd.SysProcAttr.Credential = &syscall.Credential{}
96 if err := cmd.Run(); err != nil {
97 if testenv.SyscallIsNotSupported(err) {
98 t.Skipf("skipping: %v: %v", cmd, err)
99 }
100 t.Fatal(err)
101 }
102 }
103
104 func TestUnshare(t *testing.T) {
105 path := "/proc/net/dev"
106 if _, err := os.Stat(path); err != nil {
107 if os.IsNotExist(err) {
108 t.Skip("kernel doesn't support proc filesystem")
109 }
110 if os.IsPermission(err) {
111 t.Skip("unable to test proc filesystem due to permissions")
112 }
113 t.Fatal(err)
114 }
115
116 b, err := os.ReadFile(path)
117 if err != nil {
118 t.Fatal(err)
119 }
120 orig := strings.TrimSpace(string(b))
121 if strings.Contains(orig, "lo:") && strings.Count(orig, ":") == 1 {
122
123
124
125 t.Skip("not enough network interfaces to test unshare with")
126 }
127
128 cmd := testenv.Command(t, "cat", path)
129 cmd.SysProcAttr = &syscall.SysProcAttr{
130 Unshareflags: syscall.CLONE_NEWNET,
131 }
132 out, err := cmd.CombinedOutput()
133 if err != nil {
134 if testenv.SyscallIsNotSupported(err) {
135
136 t.Skipf("skipping due to permission error: %v", err)
137 }
138 t.Fatalf("Cmd failed with err %v, output: %s", err, out)
139 }
140
141
142 sout := strings.TrimSpace(string(out))
143 if !strings.Contains(sout, "lo:") {
144 t.Fatalf("Expected lo network interface to exist, got %s", sout)
145 }
146
147 origLines := strings.Split(orig, "\n")
148 lines := strings.Split(sout, "\n")
149 if len(lines) >= len(origLines) {
150 t.Logf("%s before unshare:\n%s", path, orig)
151 t.Logf("%s after unshare:\n%s", path, sout)
152 t.Fatalf("Got %d lines of output, want < %d", len(lines), len(origLines))
153 }
154 }
155
156 func TestGroupCleanup(t *testing.T) {
157 testenv.MustHaveExecPath(t, "id")
158 cmd := testenv.Command(t, "id")
159 cmd.SysProcAttr = &syscall.SysProcAttr{
160 Credential: &syscall.Credential{
161 Uid: 0,
162 Gid: 0,
163 },
164 }
165 out, err := cmd.CombinedOutput()
166 if err != nil {
167 if testenv.SyscallIsNotSupported(err) {
168 t.Skipf("skipping: %v: %v", cmd, err)
169 }
170 t.Fatalf("Cmd failed with err %v, output: %s", err, out)
171 }
172 strOut := strings.TrimSpace(string(out))
173 t.Logf("id: %s", strOut)
174
175 expected := "uid=0(root) gid=0(root)"
176
177
178
179 if !strings.HasPrefix(strOut, expected) {
180 t.Errorf("expected prefix: %q", expected)
181 }
182 }
183
184 func TestGroupCleanupUserNamespace(t *testing.T) {
185 testenv.MustHaveExecPath(t, "id")
186 cmd := testenv.Command(t, "id")
187 uid, gid := os.Getuid(), os.Getgid()
188 cmd.SysProcAttr = &syscall.SysProcAttr{
189 Cloneflags: syscall.CLONE_NEWUSER,
190 Credential: &syscall.Credential{
191 Uid: uint32(uid),
192 Gid: uint32(gid),
193 },
194 UidMappings: []syscall.SysProcIDMap{
195 {ContainerID: 0, HostID: uid, Size: 1},
196 },
197 GidMappings: []syscall.SysProcIDMap{
198 {ContainerID: 0, HostID: gid, Size: 1},
199 },
200 }
201 out, err := cmd.CombinedOutput()
202 if err != nil {
203 if testenv.SyscallIsNotSupported(err) {
204 t.Skipf("skipping: %v: %v", cmd, err)
205 }
206 t.Fatalf("Cmd failed with err %v, output: %s", err, out)
207 }
208 strOut := strings.TrimSpace(string(out))
209 t.Logf("id: %s", strOut)
210
211
212
213 expected := "uid=0(root) gid=0(root) groups=0(root)"
214 if !strings.HasPrefix(strOut, expected) {
215 t.Errorf("expected prefix: %q", expected)
216 }
217 }
218
219
220
221 func TestUnshareMountNameSpace(t *testing.T) {
222 const mountNotSupported = "mount is not supported: "
223 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
224 dir := flag.Args()[0]
225 err := syscall.Mount("none", dir, "proc", 0, "")
226 if testenv.SyscallIsNotSupported(err) {
227 fmt.Print(mountNotSupported, err)
228 } else if err != nil {
229 fmt.Fprintf(os.Stderr, "unshare: mount %s: %v\n", dir, err)
230 os.Exit(2)
231 }
232 os.Exit(0)
233 }
234
235 exe := testenv.Executable(t)
236 d := t.TempDir()
237 t.Cleanup(func() {
238
239
240 if _, err := os.Stat(d); err == nil {
241 syscall.Unmount(d, syscall.MNT_FORCE)
242 }
243 })
244 cmd := testenv.Command(t, exe, "-test.run=^TestUnshareMountNameSpace$", d)
245 cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
246 cmd.SysProcAttr = &syscall.SysProcAttr{Unshareflags: syscall.CLONE_NEWNS}
247
248 out, err := cmd.CombinedOutput()
249 if err != nil {
250 if testenv.SyscallIsNotSupported(err) {
251 t.Skipf("skipping: could not start process with CLONE_NEWNS: %v", err)
252 }
253 t.Fatalf("unshare failed: %v\n%s", err, out)
254 } else if len(out) != 0 {
255 if bytes.HasPrefix(out, []byte(mountNotSupported)) {
256 t.Skipf("skipping: helper process reported %s", out)
257 }
258 t.Fatalf("unexpected output from helper process: %s", out)
259 }
260
261
262
263
264 if err := os.Remove(d); err != nil {
265 t.Errorf("rmdir failed on %v: %v", d, err)
266 }
267 }
268
269
270 func TestUnshareMountNameSpaceChroot(t *testing.T) {
271 const mountNotSupported = "mount is not supported: "
272 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
273 dir := flag.Args()[0]
274 err := syscall.Mount("none", dir, "proc", 0, "")
275 if testenv.SyscallIsNotSupported(err) {
276 fmt.Print(mountNotSupported, err)
277 } else if err != nil {
278 fmt.Fprintf(os.Stderr, "unshare: mount %s: %v\n", dir, err)
279 os.Exit(2)
280 }
281 os.Exit(0)
282 }
283
284 d := t.TempDir()
285
286
287
288 testenv.MustHaveGoBuild(t)
289 if platform.MustLinkExternal(runtime.GOOS, runtime.GOARCH, false) {
290 t.Skipf("skipping: can't build static binary because %s/%s requires external linking", runtime.GOOS, runtime.GOARCH)
291 }
292 x := filepath.Join(d, "syscall.test")
293 t.Cleanup(func() {
294
295
296 if _, err := os.Stat(d); err == nil {
297 syscall.Unmount(d, syscall.MNT_FORCE)
298 }
299 })
300
301 cmd := testenv.Command(t, testenv.GoToolPath(t), "test", "-c", "-o", x, "syscall")
302 cmd.Env = append(cmd.Environ(), "CGO_ENABLED=0")
303 if o, err := cmd.CombinedOutput(); err != nil {
304 t.Fatalf("%v: %v\n%s", cmd, err, o)
305 }
306
307 cmd = testenv.Command(t, "/syscall.test", "-test.run=^TestUnshareMountNameSpaceChroot$", "/")
308 cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
309 cmd.SysProcAttr = &syscall.SysProcAttr{Chroot: d, Unshareflags: syscall.CLONE_NEWNS}
310
311 out, err := cmd.CombinedOutput()
312 if err != nil {
313 if testenv.SyscallIsNotSupported(err) {
314 t.Skipf("skipping: could not start process with CLONE_NEWNS and Chroot %q: %v", d, err)
315 }
316 t.Fatalf("unshare failed: %v\n%s", err, out)
317 } else if len(out) != 0 {
318 if bytes.HasPrefix(out, []byte(mountNotSupported)) {
319 t.Skipf("skipping: helper process reported %s", out)
320 }
321 t.Fatalf("unexpected output from helper process: %s", out)
322 }
323
324
325
326
327 if err := os.Remove(x); err != nil {
328 t.Errorf("rm failed on %v: %v", x, err)
329 }
330 if err := os.Remove(d); err != nil {
331 t.Errorf("rmdir failed on %v: %v", d, err)
332 }
333 }
334
335
336 func TestUnshareUidGidMapping(t *testing.T) {
337 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
338 defer os.Exit(0)
339 if err := syscall.Chroot(os.TempDir()); err != nil {
340 fmt.Fprintln(os.Stderr, err)
341 os.Exit(2)
342 }
343 }
344
345 if os.Getuid() == 0 {
346 t.Skip("test exercises unprivileged user namespace, fails with privileges")
347 }
348
349 exe := testenv.Executable(t)
350 cmd := testenv.Command(t, exe, "-test.run=^TestUnshareUidGidMapping$")
351 cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
352 cmd.SysProcAttr = &syscall.SysProcAttr{
353 Unshareflags: syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER,
354 GidMappingsEnableSetgroups: false,
355 UidMappings: []syscall.SysProcIDMap{
356 {
357 ContainerID: 0,
358 HostID: syscall.Getuid(),
359 Size: 1,
360 },
361 },
362 GidMappings: []syscall.SysProcIDMap{
363 {
364 ContainerID: 0,
365 HostID: syscall.Getgid(),
366 Size: 1,
367 },
368 },
369 }
370 out, err := cmd.CombinedOutput()
371 if err != nil {
372 if testenv.SyscallIsNotSupported(err) {
373 t.Skipf("skipping: could not start process with CLONE_NEWNS and CLONE_NEWUSER: %v", err)
374 }
375 t.Fatalf("Cmd failed with err %v, output: %s", err, out)
376 }
377 }
378
379 func prepareCgroupFD(t *testing.T) (int, string) {
380 t.Helper()
381
382 const O_PATH = 0x200000
383
384
385 const prefix = "/sys/fs/cgroup"
386 selfCg, err := os.ReadFile("/proc/self/cgroup")
387 if err != nil {
388 if os.IsNotExist(err) || os.IsPermission(err) {
389 t.Skip(err)
390 }
391 t.Fatal(err)
392 }
393
394
395
396
397 if bytes.Count(selfCg, []byte("\n")) > 1 {
398 t.Skip("cgroup v2 not available")
399 }
400 cg := bytes.TrimPrefix(selfCg, []byte("0::"))
401 if len(cg) == len(selfCg) {
402 t.Skipf("cgroup v2 not available (/proc/self/cgroup contents: %q)", selfCg)
403 }
404
405
406 subCgroup, err := os.MkdirTemp(prefix+string(bytes.TrimSpace(cg)), "subcg-")
407 if err != nil {
408
409
410 if os.IsNotExist(err) || testenv.SyscallIsNotSupported(err) {
411 t.Skipf("skipping: %v", err)
412 }
413 t.Fatal(err)
414 }
415 t.Cleanup(func() { syscall.Rmdir(subCgroup) })
416
417 cgroupFD, err := syscall.Open(subCgroup, O_PATH, 0)
418 if err != nil {
419 t.Fatal(&os.PathError{Op: "open", Path: subCgroup, Err: err})
420 }
421 t.Cleanup(func() { syscall.Close(cgroupFD) })
422
423 return cgroupFD, "/" + path.Base(subCgroup)
424 }
425
426 func TestUseCgroupFD(t *testing.T) {
427 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
428
429 selfCg, err := os.ReadFile("/proc/self/cgroup")
430 if err != nil {
431 fmt.Fprintln(os.Stderr, err)
432 os.Exit(2)
433 }
434 fmt.Print(string(selfCg))
435 os.Exit(0)
436 }
437
438 exe := testenv.Executable(t)
439 fd, suffix := prepareCgroupFD(t)
440
441 cmd := testenv.Command(t, exe, "-test.run=^TestUseCgroupFD$")
442 cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
443 cmd.SysProcAttr = &syscall.SysProcAttr{
444 UseCgroupFD: true,
445 CgroupFD: fd,
446 }
447 out, err := cmd.CombinedOutput()
448 if err != nil {
449 if testenv.SyscallIsNotSupported(err) && !errors.Is(err, syscall.EINVAL) {
450
451
452
453
454 t.Skipf("clone3 with CLONE_INTO_CGROUP not available: %v", err)
455 }
456 t.Fatalf("Cmd failed with err %v, output: %s", err, out)
457 }
458
459 if !bytes.HasSuffix(bytes.TrimSpace(out), []byte(suffix)) {
460 t.Fatalf("got: %q, want: a line that ends with %q", out, suffix)
461 }
462 }
463
464 func TestCloneTimeNamespace(t *testing.T) {
465 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
466 timens, err := os.Readlink("/proc/self/ns/time")
467 if err != nil {
468 fmt.Fprintln(os.Stderr, err)
469 os.Exit(2)
470 }
471 fmt.Print(string(timens))
472 os.Exit(0)
473 }
474
475 exe := testenv.Executable(t)
476 cmd := testenv.Command(t, exe, "-test.run=^TestCloneTimeNamespace$")
477 cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
478 cmd.SysProcAttr = &syscall.SysProcAttr{
479 Cloneflags: syscall.CLONE_NEWTIME,
480 }
481 out, err := cmd.CombinedOutput()
482 if err != nil {
483 if testenv.SyscallIsNotSupported(err) {
484
485 t.Skipf("skipping, CLONE_NEWTIME not supported: %v", err)
486 }
487 t.Fatalf("Cmd failed with err %v, output: %s", err, out)
488 }
489
490
491
492 timens, err := os.Readlink("/proc/self/ns/time")
493 if err != nil {
494 t.Fatal(err)
495 }
496
497 parentTimeNS := timens
498 childTimeNS := string(out)
499 if childTimeNS == parentTimeNS {
500 t.Fatalf("expected child time namespace to be different from parent time namespace: %s", parentTimeNS)
501 }
502 }
503
504 func testPidFD(t *testing.T, userns bool) error {
505 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
506
507 time.Sleep(time.Hour)
508 }
509
510 exe := testenv.Executable(t)
511 var pidfd int
512 cmd := testenv.Command(t, exe, "-test.run=^TestPidFD$")
513 cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
514 cmd.SysProcAttr = &syscall.SysProcAttr{
515 PidFD: &pidfd,
516 }
517 if userns {
518 cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWUSER
519 }
520 if err := cmd.Start(); err != nil {
521 return err
522 }
523 defer func() {
524 cmd.Process.Kill()
525 cmd.Wait()
526 }()
527 t.Log("got pidfd:", pidfd)
528
529 if pidfd == -1 {
530 t.Skip("pidfd not supported")
531 }
532 defer syscall.Close(pidfd)
533
534
535 sig := syscall.SIGINT
536 if err := unix.PidFDSendSignal(uintptr(pidfd), sig); err != nil {
537 if err != syscall.EINVAL && testenv.SyscallIsNotSupported(err) {
538 t.Skip("pidfd_send_signal syscall not supported:", err)
539 }
540 t.Fatal("pidfd_send_signal syscall failed:", err)
541 }
542
543 err := cmd.Wait()
544 if cmd.ProcessState == nil || cmd.ProcessState.Sys().(syscall.WaitStatus).Signal() != sig {
545 t.Fatal("unexpected child error:", err)
546 }
547 return nil
548 }
549
550 func TestPidFD(t *testing.T) {
551 if err := testPidFD(t, false); err != nil {
552 t.Fatal("can't start a process:", err)
553 }
554 }
555
556 func TestPidFDWithUserNS(t *testing.T) {
557 if err := testPidFD(t, true); err != nil {
558 if testenv.SyscallIsNotSupported(err) {
559 t.Skip("userns not supported:", err)
560 }
561 t.Fatal("can't start a process:", err)
562 }
563 }
564
565 func TestPidFDClone3(t *testing.T) {
566 *syscall.ForceClone3 = true
567 defer func() { *syscall.ForceClone3 = false }()
568
569 if err := testPidFD(t, false); err != nil {
570 if testenv.SyscallIsNotSupported(err) {
571 t.Skip("clone3 not supported:", err)
572 }
573 t.Fatal("can't start a process:", err)
574 }
575 }
576
577 type capHeader struct {
578 version uint32
579 pid int32
580 }
581
582 type capData struct {
583 effective uint32
584 permitted uint32
585 inheritable uint32
586 }
587
588 const CAP_SYS_TIME = 25
589 const CAP_SYSLOG = 34
590
591 type caps struct {
592 hdr capHeader
593 data [2]capData
594 }
595
596 func getCaps() (caps, error) {
597 var c caps
598
599
600 if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(nil)), 0); errno != 0 {
601 return c, fmt.Errorf("SYS_CAPGET: %v", errno)
602 }
603
604
605 if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(&c.data[0])), 0); errno != 0 {
606 return c, fmt.Errorf("SYS_CAPGET: %v", errno)
607 }
608
609 return c, nil
610 }
611
612 func TestAmbientCaps(t *testing.T) {
613 testAmbientCaps(t, false)
614 }
615
616 func TestAmbientCapsUserns(t *testing.T) {
617 b, err := os.ReadFile("/proc/sys/kernel/apparmor_restrict_unprivileged_userns")
618 if err == nil && strings.TrimSpace(string(b)) == "1" {
619 t.Skip("AppArmor restriction for unprivileged user namespaces is enabled")
620 }
621 testAmbientCaps(t, true)
622 }
623
624 func testAmbientCaps(t *testing.T, userns bool) {
625 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
626 caps, err := getCaps()
627 if err != nil {
628 fmt.Fprintln(os.Stderr, err)
629 os.Exit(2)
630 }
631 if caps.data[0].effective&(1<<uint(CAP_SYS_TIME)) == 0 {
632 fmt.Fprintln(os.Stderr, "CAP_SYS_TIME unexpectedly not in the effective capability mask")
633 os.Exit(2)
634 }
635 if caps.data[1].effective&(1<<uint(CAP_SYSLOG&31)) == 0 {
636 fmt.Fprintln(os.Stderr, "CAP_SYSLOG unexpectedly not in the effective capability mask")
637 os.Exit(2)
638 }
639 os.Exit(0)
640 }
641
642
643 if runtime.GOOS == "android" {
644 t.Skip("skipping test on android; see Issue 27327")
645 }
646
647 u, err := user.Lookup("nobody")
648 if err != nil {
649 t.Fatal(err)
650 }
651 uid, err := strconv.ParseInt(u.Uid, 0, 32)
652 if err != nil {
653 t.Fatal(err)
654 }
655 gid, err := strconv.ParseInt(u.Gid, 0, 32)
656 if err != nil {
657 t.Fatal(err)
658 }
659
660
661 f, err := os.CreateTemp("", "gotest")
662 if err != nil {
663 t.Fatal(err)
664 }
665 t.Cleanup(func() {
666 f.Close()
667 os.Remove(f.Name())
668 })
669
670 exe := testenv.Executable(t)
671 e, err := os.Open(exe)
672 if err != nil {
673 t.Fatal(err)
674 }
675 defer e.Close()
676 if _, err := io.Copy(f, e); err != nil {
677 t.Fatal(err)
678 }
679 if err := f.Chmod(0755); err != nil {
680 t.Fatal(err)
681 }
682 if err := f.Close(); err != nil {
683 t.Fatal(err)
684 }
685
686 cmd := testenv.Command(t, f.Name(), "-test.run=^"+t.Name()+"$")
687 cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
688 cmd.Stdout = os.Stdout
689 cmd.Stderr = os.Stderr
690 cmd.SysProcAttr = &syscall.SysProcAttr{
691 Credential: &syscall.Credential{
692 Uid: uint32(uid),
693 Gid: uint32(gid),
694 },
695 AmbientCaps: []uintptr{CAP_SYS_TIME, CAP_SYSLOG},
696 }
697 if userns {
698 cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWUSER
699 const nobody = 65534
700 uid := os.Getuid()
701 gid := os.Getgid()
702 cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{{
703 ContainerID: int(nobody),
704 HostID: uid,
705 Size: int(1),
706 }}
707 cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{{
708 ContainerID: int(nobody),
709 HostID: gid,
710 Size: int(1),
711 }}
712
713
714 cmd.SysProcAttr.Credential = &syscall.Credential{
715 Uid: nobody,
716 Gid: nobody,
717 }
718 }
719 if err := cmd.Run(); err != nil {
720 if testenv.SyscallIsNotSupported(err) {
721 t.Skipf("skipping: %v: %v", cmd, err)
722 }
723 t.Fatal(err.Error())
724 }
725 }
726
View as plain text