Source file
src/syscall/exec_linux_test.go
Documentation: syscall
1
2
3
4
5
6
7
8 package syscall_test
9
10 import (
11 "flag"
12 "fmt"
13 "internal/testenv"
14 "io"
15 "os"
16 "os/exec"
17 "os/user"
18 "path/filepath"
19 "runtime"
20 "strconv"
21 "strings"
22 "syscall"
23 "testing"
24 "unsafe"
25 )
26
27 func isDocker() bool {
28 _, err := os.Stat("/.dockerenv")
29 return err == nil
30 }
31
32 func isLXC() bool {
33 return os.Getenv("container") == "lxc"
34 }
35
36 func skipInContainer(t *testing.T) {
37
38
39
40
41
42
43
44
45 if isDocker() {
46 t.Skip("skip this test in Docker container")
47 }
48 if isLXC() {
49 t.Skip("skip this test in LXC container")
50 }
51 }
52
53 func skipNoUserNamespaces(t *testing.T) {
54 if _, err := os.Stat("/proc/self/ns/user"); err != nil {
55 if os.IsNotExist(err) {
56 t.Skip("kernel doesn't support user namespaces")
57 }
58 if os.IsPermission(err) {
59 t.Skip("unable to test user namespaces due to permissions")
60 }
61 t.Fatalf("Failed to stat /proc/self/ns/user: %v", err)
62 }
63 }
64
65 func skipUnprivilegedUserClone(t *testing.T) {
66
67
68 data, errRead := os.ReadFile("/proc/sys/kernel/unprivileged_userns_clone")
69 if errRead != nil || len(data) < 1 || data[0] == '0' {
70 t.Skip("kernel prohibits user namespace in unprivileged process")
71 }
72 }
73
74
75
76
77 func isChrooted(t *testing.T) bool {
78 root, err := os.Stat("/")
79 if err != nil {
80 t.Fatalf("cannot stat /: %v", err)
81 }
82 return root.Sys().(*syscall.Stat_t).Ino != 2
83 }
84
85 func checkUserNS(t *testing.T) {
86 skipInContainer(t)
87 skipNoUserNamespaces(t)
88 if isChrooted(t) {
89
90
91
92 t.Skip("cannot create user namespaces when chrooted")
93 }
94
95 if os.Getuid() != 0 {
96 skipUnprivilegedUserClone(t)
97 }
98
99
100 if _, err := os.Stat("/sys/module/user_namespace/parameters/enable"); err == nil {
101 buf, _ := os.ReadFile("/sys/module/user_namespace/parameters/enabled")
102 if !strings.HasPrefix(string(buf), "Y") {
103 t.Skip("kernel doesn't support user namespaces")
104 }
105 }
106
107
108 if _, err := os.Stat("/proc/sys/user/max_user_namespaces"); err == nil {
109 buf, errRead := os.ReadFile("/proc/sys/user/max_user_namespaces")
110 if errRead == nil && buf[0] == '0' {
111 t.Skip("kernel doesn't support user namespaces")
112 }
113 }
114
115
116
117
118
119 if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
120 t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
121 }
122 }
123
124 func whoamiCmd(t *testing.T, uid, gid int, setgroups bool) *exec.Cmd {
125 checkUserNS(t)
126 cmd := exec.Command("whoami")
127 cmd.SysProcAttr = &syscall.SysProcAttr{
128 Cloneflags: syscall.CLONE_NEWUSER,
129 UidMappings: []syscall.SysProcIDMap{
130 {ContainerID: 0, HostID: uid, Size: 1},
131 },
132 GidMappings: []syscall.SysProcIDMap{
133 {ContainerID: 0, HostID: gid, Size: 1},
134 },
135 GidMappingsEnableSetgroups: setgroups,
136 }
137 return cmd
138 }
139
140 func testNEWUSERRemap(t *testing.T, uid, gid int, setgroups bool) {
141 cmd := whoamiCmd(t, uid, gid, setgroups)
142 out, err := cmd.CombinedOutput()
143 if err != nil {
144 t.Fatalf("Cmd failed with err %v, output: %s", err, out)
145 }
146 sout := strings.TrimSpace(string(out))
147 want := "root"
148 if sout != want {
149 t.Fatalf("whoami = %q; want %q", out, want)
150 }
151 }
152
153 func TestCloneNEWUSERAndRemapRootDisableSetgroups(t *testing.T) {
154 if os.Getuid() != 0 {
155 t.Skip("skipping root only test")
156 }
157 testNEWUSERRemap(t, 0, 0, false)
158 }
159
160 func TestCloneNEWUSERAndRemapRootEnableSetgroups(t *testing.T) {
161 if os.Getuid() != 0 {
162 t.Skip("skipping root only test")
163 }
164 testNEWUSERRemap(t, 0, 0, true)
165 }
166
167 func TestCloneNEWUSERAndRemapNoRootDisableSetgroups(t *testing.T) {
168 if os.Getuid() == 0 {
169 t.Skip("skipping unprivileged user only test")
170 }
171 testNEWUSERRemap(t, os.Getuid(), os.Getgid(), false)
172 }
173
174 func TestCloneNEWUSERAndRemapNoRootSetgroupsEnableSetgroups(t *testing.T) {
175 if os.Getuid() == 0 {
176 t.Skip("skipping unprivileged user only test")
177 }
178 cmd := whoamiCmd(t, os.Getuid(), os.Getgid(), true)
179 err := cmd.Run()
180 if err == nil {
181 t.Skip("probably old kernel without security fix")
182 }
183 if !os.IsPermission(err) {
184 t.Fatalf("Unprivileged gid_map rewriting with GidMappingsEnableSetgroups must fail")
185 }
186 }
187
188 func TestEmptyCredGroupsDisableSetgroups(t *testing.T) {
189 cmd := whoamiCmd(t, os.Getuid(), os.Getgid(), false)
190 cmd.SysProcAttr.Credential = &syscall.Credential{}
191 if err := cmd.Run(); err != nil {
192 t.Fatal(err)
193 }
194 }
195
196 func TestUnshare(t *testing.T) {
197 skipInContainer(t)
198
199
200 if os.Getuid() != 0 {
201 t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
202 }
203
204
205
206
207
208 if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
209 t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
210 }
211
212 path := "/proc/net/dev"
213 if _, err := os.Stat(path); err != nil {
214 if os.IsNotExist(err) {
215 t.Skip("kernel doesn't support proc filesystem")
216 }
217 if os.IsPermission(err) {
218 t.Skip("unable to test proc filesystem due to permissions")
219 }
220 t.Fatal(err)
221 }
222 if _, err := os.Stat("/proc/self/ns/net"); err != nil {
223 if os.IsNotExist(err) {
224 t.Skip("kernel doesn't support net namespace")
225 }
226 t.Fatal(err)
227 }
228
229 orig, err := os.ReadFile(path)
230 if err != nil {
231 t.Fatal(err)
232 }
233 origLines := strings.Split(strings.TrimSpace(string(orig)), "\n")
234
235 cmd := exec.Command("cat", path)
236 cmd.SysProcAttr = &syscall.SysProcAttr{
237 Unshareflags: syscall.CLONE_NEWNET,
238 }
239 out, err := cmd.CombinedOutput()
240 if err != nil {
241 if strings.Contains(err.Error(), "operation not permitted") {
242
243
244
245 t.Skip("skipping due to permission error")
246 }
247 t.Fatalf("Cmd failed with err %v, output: %s", err, out)
248 }
249
250
251 sout := strings.TrimSpace(string(out))
252 if !strings.Contains(sout, "lo:") {
253 t.Fatalf("Expected lo network interface to exist, got %s", sout)
254 }
255
256 lines := strings.Split(sout, "\n")
257 if len(lines) >= len(origLines) {
258 t.Fatalf("Got %d lines of output, want <%d", len(lines), len(origLines))
259 }
260 }
261
262 func TestGroupCleanup(t *testing.T) {
263 if os.Getuid() != 0 {
264 t.Skip("we need root for credential")
265 }
266 cmd := exec.Command("id")
267 cmd.SysProcAttr = &syscall.SysProcAttr{
268 Credential: &syscall.Credential{
269 Uid: 0,
270 Gid: 0,
271 },
272 }
273 out, err := cmd.CombinedOutput()
274 if err != nil {
275 t.Fatalf("Cmd failed with err %v, output: %s", err, out)
276 }
277 strOut := strings.TrimSpace(string(out))
278 expected := "uid=0(root) gid=0(root)"
279
280
281
282 if !strings.HasPrefix(strOut, expected) {
283 t.Errorf("id command output: %q, expected prefix: %q", strOut, expected)
284 }
285 }
286
287 func TestGroupCleanupUserNamespace(t *testing.T) {
288 if os.Getuid() != 0 {
289 t.Skip("we need root for credential")
290 }
291 checkUserNS(t)
292 cmd := exec.Command("id")
293 uid, gid := os.Getuid(), os.Getgid()
294 cmd.SysProcAttr = &syscall.SysProcAttr{
295 Cloneflags: syscall.CLONE_NEWUSER,
296 Credential: &syscall.Credential{
297 Uid: uint32(uid),
298 Gid: uint32(gid),
299 },
300 UidMappings: []syscall.SysProcIDMap{
301 {ContainerID: 0, HostID: uid, Size: 1},
302 },
303 GidMappings: []syscall.SysProcIDMap{
304 {ContainerID: 0, HostID: gid, Size: 1},
305 },
306 }
307 out, err := cmd.CombinedOutput()
308 if err != nil {
309 t.Fatalf("Cmd failed with err %v, output: %s", err, out)
310 }
311 strOut := strings.TrimSpace(string(out))
312
313
314 expected := []string{
315 "uid=0(root) gid=0(root) groups=0(root)",
316 "uid=0(root) gid=0(root) groups=0(root),65534(nobody)",
317 "uid=0(root) gid=0(root) groups=0(root),65534(nogroup)",
318 "uid=0(root) gid=0(root) groups=0(root),65534",
319 "uid=0(root) gid=0(root) groups=0(root),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody)",
320 "uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023",
321 "uid=0(root) gid=0(root) groups=0(root),65534(nobody) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023",
322 }
323 for _, e := range expected {
324 if strOut == e {
325 return
326 }
327 }
328 t.Errorf("id command output: %q, expected one of %q", strOut, expected)
329 }
330
331
332
333 func TestUnshareMountNameSpaceHelper(*testing.T) {
334 if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
335 return
336 }
337 defer os.Exit(0)
338 if err := syscall.Mount("none", flag.Args()[0], "proc", 0, ""); err != nil {
339 fmt.Fprintf(os.Stderr, "unshare: mount %v failed: %v", os.Args, err)
340 os.Exit(2)
341 }
342 }
343
344
345 func TestUnshareMountNameSpace(t *testing.T) {
346 skipInContainer(t)
347
348
349 if os.Getuid() != 0 {
350 t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
351 }
352
353 d, err := os.MkdirTemp("", "unshare")
354 if err != nil {
355 t.Fatalf("tempdir: %v", err)
356 }
357
358 cmd := exec.Command(os.Args[0], "-test.run=TestUnshareMountNameSpaceHelper", d)
359 cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
360 cmd.SysProcAttr = &syscall.SysProcAttr{Unshareflags: syscall.CLONE_NEWNS}
361
362 o, err := cmd.CombinedOutput()
363 if err != nil {
364 if strings.Contains(err.Error(), ": permission denied") {
365 t.Skipf("Skipping test (golang.org/issue/19698); unshare failed due to permissions: %s, %v", o, err)
366 }
367 t.Fatalf("unshare failed: %s, %v", o, err)
368 }
369
370
371
372
373
374
375 if err := os.Remove(d); err != nil {
376 t.Errorf("rmdir failed on %v: %v", d, err)
377 if err := syscall.Unmount(d, syscall.MNT_FORCE); err != nil {
378 t.Errorf("Can't unmount %v: %v", d, err)
379 }
380 if err := os.Remove(d); err != nil {
381 t.Errorf("rmdir after unmount failed on %v: %v", d, err)
382 }
383 }
384 }
385
386
387 func TestUnshareMountNameSpaceChroot(t *testing.T) {
388 skipInContainer(t)
389
390
391 if os.Getuid() != 0 {
392 t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
393 }
394
395 d, err := os.MkdirTemp("", "unshare")
396 if err != nil {
397 t.Fatalf("tempdir: %v", err)
398 }
399
400
401
402 x := filepath.Join(d, "syscall.test")
403 cmd := exec.Command(testenv.GoToolPath(t), "test", "-c", "-o", x, "syscall")
404 cmd.Env = append(os.Environ(), "CGO_ENABLED=0")
405 if o, err := cmd.CombinedOutput(); err != nil {
406 t.Fatalf("Build of syscall in chroot failed, output %v, err %v", o, err)
407 }
408
409 cmd = exec.Command("/syscall.test", "-test.run=TestUnshareMountNameSpaceHelper", "/")
410 cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
411 cmd.SysProcAttr = &syscall.SysProcAttr{Chroot: d, Unshareflags: syscall.CLONE_NEWNS}
412
413 o, err := cmd.CombinedOutput()
414 if err != nil {
415 if strings.Contains(err.Error(), ": permission denied") {
416 t.Skipf("Skipping test (golang.org/issue/19698); unshare failed due to permissions: %s, %v", o, err)
417 }
418 t.Fatalf("unshare failed: %s, %v", o, err)
419 }
420
421
422
423
424
425
426 if err := os.Remove(x); err != nil {
427 t.Errorf("rm failed on %v: %v", x, err)
428 if err := syscall.Unmount(d, syscall.MNT_FORCE); err != nil {
429 t.Fatalf("Can't unmount %v: %v", d, err)
430 }
431 if err := os.Remove(x); err != nil {
432 t.Fatalf("rm failed on %v: %v", x, err)
433 }
434 }
435
436 if err := os.Remove(d); err != nil {
437 t.Errorf("rmdir failed on %v: %v", d, err)
438 }
439 }
440
441 func TestUnshareUidGidMappingHelper(*testing.T) {
442 if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
443 return
444 }
445 defer os.Exit(0)
446 if err := syscall.Chroot(os.TempDir()); err != nil {
447 fmt.Fprintln(os.Stderr, err)
448 os.Exit(2)
449 }
450 }
451
452
453 func TestUnshareUidGidMapping(t *testing.T) {
454 if os.Getuid() == 0 {
455 t.Skip("test exercises unprivileged user namespace, fails with privileges")
456 }
457 checkUserNS(t)
458 cmd := exec.Command(os.Args[0], "-test.run=TestUnshareUidGidMappingHelper")
459 cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
460 cmd.SysProcAttr = &syscall.SysProcAttr{
461 Unshareflags: syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER,
462 GidMappingsEnableSetgroups: false,
463 UidMappings: []syscall.SysProcIDMap{
464 {
465 ContainerID: 0,
466 HostID: syscall.Getuid(),
467 Size: 1,
468 },
469 },
470 GidMappings: []syscall.SysProcIDMap{
471 {
472 ContainerID: 0,
473 HostID: syscall.Getgid(),
474 Size: 1,
475 },
476 },
477 }
478 out, err := cmd.CombinedOutput()
479 if err != nil {
480 t.Fatalf("Cmd failed with err %v, output: %s", err, out)
481 }
482 }
483
484 type capHeader struct {
485 version uint32
486 pid int32
487 }
488
489 type capData struct {
490 effective uint32
491 permitted uint32
492 inheritable uint32
493 }
494
495 const CAP_SYS_TIME = 25
496 const CAP_SYSLOG = 34
497
498 type caps struct {
499 hdr capHeader
500 data [2]capData
501 }
502
503 func getCaps() (caps, error) {
504 var c caps
505
506
507 if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(nil)), 0); errno != 0 {
508 return c, fmt.Errorf("SYS_CAPGET: %v", errno)
509 }
510
511
512 if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(&c.data[0])), 0); errno != 0 {
513 return c, fmt.Errorf("SYS_CAPGET: %v", errno)
514 }
515
516 return c, nil
517 }
518
519 func mustSupportAmbientCaps(t *testing.T) {
520 var uname syscall.Utsname
521 if err := syscall.Uname(&uname); err != nil {
522 t.Fatalf("Uname: %v", err)
523 }
524 var buf [65]byte
525 for i, b := range uname.Release {
526 buf[i] = byte(b)
527 }
528 ver := string(buf[:])
529 if i := strings.Index(ver, "\x00"); i != -1 {
530 ver = ver[:i]
531 }
532 if strings.HasPrefix(ver, "2.") ||
533 strings.HasPrefix(ver, "3.") ||
534 strings.HasPrefix(ver, "4.1.") ||
535 strings.HasPrefix(ver, "4.2.") {
536 t.Skipf("kernel version %q predates required 4.3; skipping test", ver)
537 }
538 }
539
540
541
542 func TestAmbientCapsHelper(*testing.T) {
543 if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
544 return
545 }
546 defer os.Exit(0)
547
548 caps, err := getCaps()
549 if err != nil {
550 fmt.Fprintln(os.Stderr, err)
551 os.Exit(2)
552 }
553 if caps.data[0].effective&(1<<uint(CAP_SYS_TIME)) == 0 {
554 fmt.Fprintln(os.Stderr, "CAP_SYS_TIME unexpectedly not in the effective capability mask")
555 os.Exit(2)
556 }
557 if caps.data[1].effective&(1<<uint(CAP_SYSLOG&31)) == 0 {
558 fmt.Fprintln(os.Stderr, "CAP_SYSLOG unexpectedly not in the effective capability mask")
559 os.Exit(2)
560 }
561 }
562
563 func TestAmbientCaps(t *testing.T) {
564
565
566 if os.Getuid() != 0 {
567 t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
568 }
569
570 testAmbientCaps(t, false)
571 }
572
573 func TestAmbientCapsUserns(t *testing.T) {
574 checkUserNS(t)
575 testAmbientCaps(t, true)
576 }
577
578 func testAmbientCaps(t *testing.T, userns bool) {
579 skipInContainer(t)
580 mustSupportAmbientCaps(t)
581
582 skipUnprivilegedUserClone(t)
583
584
585 if runtime.GOOS == "android" {
586 t.Skip("skipping test on android; see Issue 27327")
587 }
588
589 u, err := user.Lookup("nobody")
590 if err != nil {
591 t.Fatal(err)
592 }
593 uid, err := strconv.ParseInt(u.Uid, 0, 32)
594 if err != nil {
595 t.Fatal(err)
596 }
597 gid, err := strconv.ParseInt(u.Gid, 0, 32)
598 if err != nil {
599 t.Fatal(err)
600 }
601
602
603 f, err := os.CreateTemp("", "gotest")
604 if err != nil {
605 t.Fatal(err)
606 }
607 defer os.Remove(f.Name())
608 defer f.Close()
609 e, err := os.Open(os.Args[0])
610 if err != nil {
611 t.Fatal(err)
612 }
613 defer e.Close()
614 if _, err := io.Copy(f, e); err != nil {
615 t.Fatal(err)
616 }
617 if err := f.Chmod(0755); err != nil {
618 t.Fatal(err)
619 }
620 if err := f.Close(); err != nil {
621 t.Fatal(err)
622 }
623
624 cmd := exec.Command(f.Name(), "-test.run=TestAmbientCapsHelper")
625 cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
626 cmd.Stdout = os.Stdout
627 cmd.Stderr = os.Stderr
628 cmd.SysProcAttr = &syscall.SysProcAttr{
629 Credential: &syscall.Credential{
630 Uid: uint32(uid),
631 Gid: uint32(gid),
632 },
633 AmbientCaps: []uintptr{CAP_SYS_TIME, CAP_SYSLOG},
634 }
635 if userns {
636 cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWUSER
637 const nobody = 65534
638 uid := os.Getuid()
639 gid := os.Getgid()
640 cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{{
641 ContainerID: int(nobody),
642 HostID: int(uid),
643 Size: int(1),
644 }}
645 cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{{
646 ContainerID: int(nobody),
647 HostID: int(gid),
648 Size: int(1),
649 }}
650
651
652 cmd.SysProcAttr.Credential = &syscall.Credential{
653 Uid: nobody,
654 Gid: nobody,
655 }
656 }
657 if err := cmd.Run(); err != nil {
658 t.Fatal(err.Error())
659 }
660 }
661
View as plain text