diff options
author | Ian Lance Taylor <iant@golang.org> | 2017-09-14 17:11:35 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2017-09-14 17:11:35 +0000 |
commit | bc998d034f45d1828a8663b2eed928faf22a7d01 (patch) | |
tree | 8d262a22ca7318f4bcd64269fe8fe9e45bcf8d0f /libgo/go/syscall | |
parent | a41a6142df74219f596e612d3a7775f68ca6e96f (diff) | |
download | gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.zip gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.tar.gz gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.tar.bz2 |
libgo: update to go1.9
Reviewed-on: https://go-review.googlesource.com/63753
From-SVN: r252767
Diffstat (limited to 'libgo/go/syscall')
-rw-r--r-- | libgo/go/syscall/errors_plan9.go | 1 | ||||
-rw-r--r-- | libgo/go/syscall/exec_bsd.go | 42 | ||||
-rw-r--r-- | libgo/go/syscall/exec_freebsd.go | 25 | ||||
-rw-r--r-- | libgo/go/syscall/exec_linux.go | 149 | ||||
-rw-r--r-- | libgo/go/syscall/exec_linux_test.go | 305 | ||||
-rw-r--r-- | libgo/go/syscall/exec_unix.go | 18 | ||||
-rw-r--r-- | libgo/go/syscall/forkpipe_bsd.go | 20 | ||||
-rw-r--r-- | libgo/go/syscall/net.go | 34 | ||||
-rw-r--r-- | libgo/go/syscall/syscall.go | 13 | ||||
-rw-r--r-- | libgo/go/syscall/syscall_dragonfly.go | 4 | ||||
-rw-r--r-- | libgo/go/syscall/syscall_linux_386.go | 4 | ||||
-rw-r--r-- | libgo/go/syscall/syscall_linux_amd64.go | 2 | ||||
-rw-r--r-- | libgo/go/syscall/syscall_linux_mipsx.go | 4 | ||||
-rw-r--r-- | libgo/go/syscall/syscall_linux_s390x.go | 4 | ||||
-rw-r--r-- | libgo/go/syscall/syscall_unix_test.go | 8 |
15 files changed, 553 insertions, 80 deletions
diff --git a/libgo/go/syscall/errors_plan9.go b/libgo/go/syscall/errors_plan9.go index 6952562..74a5659 100644 --- a/libgo/go/syscall/errors_plan9.go +++ b/libgo/go/syscall/errors_plan9.go @@ -45,6 +45,7 @@ var ( // what package os and others expect. EACCES = NewError("access permission denied") EAFNOSUPPORT = NewError("address family not supported by protocol") + ESPIPE = NewError("illegal seek") ) // Notes diff --git a/libgo/go/syscall/exec_bsd.go b/libgo/go/syscall/exec_bsd.go index 80991ec..9115cf0 100644 --- a/libgo/go/syscall/exec_bsd.go +++ b/libgo/go/syscall/exec_bsd.go @@ -26,6 +26,7 @@ type SysProcAttr struct { // Implemented in runtime package. func runtime_BeforeFork() func runtime_AfterFork() +func runtime_AfterForkInChild() // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child. // If a dup or exec fails, write the errno error to pipe. @@ -77,6 +78,8 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // Fork succeeded, now in child. + runtime_AfterForkInChild() + // Enable tracing if requested. if sys.Ptrace { err1 = raw_ptrace(_PTRACE_TRACEME, 0, nil, nil) @@ -126,27 +129,24 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // User and groups if cred := sys.Credential; cred != nil { ngroups := len(cred.Groups) - if ngroups == 0 { - err2 := setgroups(0, nil) - if err2 == nil { - err1 = 0 - } else { - err1 = err2.(Errno) - } - } else { - groups := make([]Gid_t, ngroups) + var groups *Gid_t + if ngroups > 0 { + gids := make([]Gid_t, ngroups) for i, v := range cred.Groups { - groups[i] = Gid_t(v) + gids[i] = Gid_t(v) } - err2 := setgroups(ngroups, &groups[0]) + groups = &gids[0] + } + if !cred.NoSetGroups { + err2 := setgroups(ngroups, groups) if err2 == nil { err1 = 0 } else { err1 = err2.(Errno) } - } - if err1 != 0 { - goto childerror + if err1 != 0 { + goto childerror + } } err2 := Setgid(int(cred.Gid)) if err2 != nil { @@ -255,17 +255,3 @@ childerror: raw_exit(253) } } - -// Try to open a pipe with O_CLOEXEC set on both file descriptors. -func forkExecPipe(p []int) error { - err := Pipe(p) - if err != nil { - return err - } - _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC) - if err != nil { - return err - } - _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC) - return err -} diff --git a/libgo/go/syscall/exec_freebsd.go b/libgo/go/syscall/exec_freebsd.go new file mode 100644 index 0000000..4ed32c0 --- /dev/null +++ b/libgo/go/syscall/exec_freebsd.go @@ -0,0 +1,25 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syscall + +func forkExecPipe(p []int) error { + err := Pipe2(p, O_CLOEXEC) + if err == nil { + return nil + } + + // FreeBSD 9 fallback. + // TODO: remove this for Go 1.10 per Issue 19072 + err = Pipe(p) + if err != nil { + return err + } + _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC) + if err != nil { + return err + } + _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC) + return err +} diff --git a/libgo/go/syscall/exec_linux.go b/libgo/go/syscall/exec_linux.go index 8d6467a..6e2f83e 100644 --- a/libgo/go/syscall/exec_linux.go +++ b/libgo/go/syscall/exec_linux.go @@ -13,6 +13,12 @@ import ( //sysnb raw_prctl(option int, arg2 int, arg3 int, arg4 int, arg5 int) (ret int, err Errno) //prctl(option _C_int, arg2 _C_long, arg3 _C_long, arg4 _C_long, arg5 _C_long) _C_int +//sysnb rawUnshare(flags int) (err Errno) +//unshare(flags _C_int) _C_int + +//sysnb rawMount(source *byte, target *byte, fstype *byte, flags uintptr, data *byte) (err Errno) +//mount(source *byte, target *byte, fstype *byte, flags _C_long, data *byte) _C_int + // SysProcIDMap holds Container ID to Host ID mappings used for User Namespaces in Linux. // See user_namespaces(7). type SysProcIDMap struct { @@ -42,11 +48,18 @@ type SysProcAttr struct { // This parameter is no-op if GidMappings == nil. Otherwise for unprivileged // users this should be set to false for mappings work. GidMappingsEnableSetgroups bool + AmbientCaps []uintptr // Ambient capabilities (Linux only) } +var ( + none = [...]byte{'n', 'o', 'n', 'e', 0} + slash = [...]byte{'/', 0} +) + // Implemented in runtime package. func runtime_BeforeFork() func runtime_AfterFork() +func runtime_AfterForkInChild() // Implemented in clone_linux.c func rawClone(flags _C_ulong, child_stack *byte, ptid *Pid_t, ctid *Pid_t, regs unsafe.Pointer) _C_long @@ -62,16 +75,62 @@ func rawClone(flags _C_ulong, child_stack *byte, ptid *Pid_t, ctid *Pid_t, regs // functions that do not grow the stack. //go:norace func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { + // Set up and fork. This returns immediately in the parent or + // if there's an error. + r1, err1, p, locked := forkAndExecInChild1(argv0, argv, envv, chroot, dir, attr, sys, pipe) + if locked { + runtime_AfterFork() + } + if err1 != 0 { + return 0, err1 + } + + // parent; return PID + pid = int(r1) + + if sys.UidMappings != nil || sys.GidMappings != nil { + Close(p[0]) + err := writeUidGidMappings(pid, sys) + var err2 Errno + if err != nil { + err2 = err.(Errno) + } + RawSyscall(SYS_WRITE, uintptr(p[1]), uintptr(unsafe.Pointer(&err2)), unsafe.Sizeof(err2)) + Close(p[1]) + } + + return pid, 0 +} + +// forkAndExecInChild1 implements the body of forkAndExecInChild up to +// the parent's post-fork path. This is a separate function so we can +// separate the child's and parent's stack frames if we're using +// vfork. +// +// This is go:noinline because the point is to keep the stack frames +// of this and forkAndExecInChild separate. +// +//go:noinline +//go:norace +func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (r1 uintptr, err1 Errno, p [2]int, locked bool) { + // Defined in linux/prctl.h starting with Linux 4.3. + const ( + PR_CAP_AMBIENT = 0x2f + PR_CAP_AMBIENT_RAISE = 0x2 + ) + + // vfork requires that the child not touch any of the parent's + // active stack frames. Hence, the child does all post-fork + // processing in this stack frame and never returns, while the + // parent returns immediately from this frame and does all + // post-fork processing in the outer frame. // Declare all variables at top in case any // declarations require heap allocation (e.g., err1). var ( - r1 uintptr - r2 _C_long - err1 Errno err2 Errno nextfd int i int - p [2]int + r2 int ) // Record parent PID so child can test if it has died. @@ -94,39 +153,42 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // synchronizing writing of User ID/Group ID mappings. if sys.UidMappings != nil || sys.GidMappings != nil { if err := forkExecPipe(p[:]); err != nil { - return 0, err.(Errno) + err1 = err.(Errno) + return } } // About to call fork. // No more allocation or calls of non-assembly functions. runtime_BeforeFork() - r2 = rawClone(_C_ulong(uintptr(SIGCHLD)|sys.Cloneflags), nil, nil, nil, unsafe.Pointer(nil)) + locked = true + r2 = int(rawClone(_C_ulong(uintptr(SIGCHLD)|sys.Cloneflags), nil, nil, nil, unsafe.Pointer(nil))) if r2 < 0 { - runtime_AfterFork() - return 0, GetErrno() + err1 = GetErrno() } - if r2 != 0 { - // parent; return PID - runtime_AfterFork() - pid = int(r2) - - if sys.UidMappings != nil || sys.GidMappings != nil { - Close(p[0]) - err := writeUidGidMappings(pid, sys) - if err != nil { - err2 = err.(Errno) - } - RawSyscall(SYS_WRITE, uintptr(p[1]), uintptr(unsafe.Pointer(&err2)), unsafe.Sizeof(err2)) - Close(p[1]) - } - - return pid, 0 + // If we're in the parent, we must return immediately + // so we're not in the same stack frame as the child. + // This can at most use the return PC, which the child + // will not modify, and the results of + // rawVforkSyscall, which must have been written after + // the child was replaced. + r1 = uintptr(r2) + return } // Fork succeeded, now in child. + runtime_AfterForkInChild() + + // Enable the "keep capabilities" flag to set ambient capabilities later. + if len(sys.AmbientCaps) > 0 { + _, _, err1 = RawSyscall6(SYS_PRCTL, PR_SET_KEEPCAPS, 1, 0, 0, 0, 0) + if err1 != 0 { + goto childerror + } + } + // Wait for User ID/Group ID mappings to be written. if sys.UidMappings != nil || sys.GidMappings != nil { if _, _, err1 = RawSyscall(SYS_CLOSE, uintptr(p[1]), 0, 0); err1 != 0 { @@ -184,17 +246,30 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } } - // Chroot - if chroot != nil { - err1 = raw_chroot(chroot) + // Unshare + if sys.Unshareflags != 0 { + err1 = rawUnshare(int(sys.Unshareflags)) if err1 != 0 { goto childerror } + // The unshare system call in Linux doesn't unshare mount points + // mounted with --shared. Systemd mounts / with --shared. For a + // long discussion of the pros and cons of this see debian bug 739593. + // The Go model of unsharing is more like Plan 9, where you ask + // to unshare and the namespaces are unconditionally unshared. + // To make this model work we must further mark / as MS_PRIVATE. + // This is what the standard unshare command does. + if sys.Unshareflags&CLONE_NEWNS == CLONE_NEWNS { + err1 = rawMount(&none[0], &slash[0], nil, MS_REC|MS_PRIVATE, nil) + if err1 != 0 { + goto childerror + } + } } - // Unshare - if sys.Unshareflags != 0 { - _, _, err1 = RawSyscall(SYS_UNSHARE, sys.Unshareflags, 0, 0) + // Chroot + if chroot != nil { + err1 = raw_chroot(chroot) if err1 != 0 { goto childerror } @@ -207,10 +282,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr if ngroups > 0 { groups = unsafe.Pointer(&cred.Groups[0]) } - // Don't call setgroups in case of user namespace, gid mappings - // and disabled setgroups, because otherwise unprivileged user namespace - // will fail with any non-empty SysProcAttr.Credential. - if !(sys.GidMappings != nil && !sys.GidMappingsEnableSetgroups && ngroups == 0) { + if !(sys.GidMappings != nil && !sys.GidMappingsEnableSetgroups && ngroups == 0) && !cred.NoSetGroups { err1 = raw_setgroups(ngroups, groups) if err1 != 0 { goto childerror @@ -226,6 +298,13 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } } + for _, c := range sys.AmbientCaps { + _, _, err1 = RawSyscall6(SYS_PRCTL, PR_CAP_AMBIENT, uintptr(PR_CAP_AMBIENT_RAISE), c, 0, 0, 0) + if err1 != 0 { + goto childerror + } + } + // Chdir if dir != nil { err1 = raw_chdir(dir) @@ -321,7 +400,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // Set the controlling TTY to Ctty if sys.Setctty { - _, err1 = raw_ioctl(sys.Ctty, TIOCSCTTY, 0) + _, err1 = raw_ioctl(sys.Ctty, TIOCSCTTY, sys.Ctty) if err1 != 0 { goto childerror } diff --git a/libgo/go/syscall/exec_linux_test.go b/libgo/go/syscall/exec_linux_test.go index 7a4b571..114deec 100644 --- a/libgo/go/syscall/exec_linux_test.go +++ b/libgo/go/syscall/exec_linux_test.go @@ -7,12 +7,20 @@ package syscall_test import ( + "flag" + "fmt" + "internal/testenv" + "io" "io/ioutil" "os" "os/exec" + "os/user" + "path/filepath" + "strconv" "strings" "syscall" "testing" + "unsafe" ) // Check if we are in a chroot by checking if the inode of / is @@ -49,6 +57,14 @@ func checkUserNS(t *testing.T) { t.Skip("kernel prohibits user namespace in unprivileged process") } } + // On Centos 7 make sure they set the kernel parameter user_namespace=1 + // See issue 16283 and 20796. + if _, err := os.Stat("/sys/module/user_namespace/parameters/enable"); err == nil { + buf, _ := ioutil.ReadFile("/sys/module/user_namespace/parameters/enabled") + if !strings.HasPrefix(string(buf), "Y") { + t.Skip("kernel doesn't support user namespaces") + } + } // When running under the Go continuous build, skip tests for // now when under Kubernetes. (where things are root but not quite) // Both of these are our own environment variables. @@ -174,6 +190,12 @@ func TestUnshare(t *testing.T) { } out, err := cmd.CombinedOutput() if err != nil { + if strings.Contains(err.Error(), "operation not permitted") { + // Issue 17206: despite all the checks above, + // this still reportedly fails for some users. + // (older kernels?). Just skip. + t.Skip("skipping due to permission error") + } t.Fatalf("Cmd failed with err %v, output: %s", err, out) } @@ -205,9 +227,10 @@ func TestGroupCleanup(t *testing.T) { t.Fatalf("Cmd failed with err %v, output: %s", err, out) } strOut := strings.TrimSpace(string(out)) - expected := "uid=0(root) gid=0(root) groups=0(root)" + expected := "uid=0(root) gid=0(root)" // Just check prefix because some distros reportedly output a // context parameter; see https://golang.org/issue/16224. + // Alpine does not output groups; see https://golang.org/issue/19938. if !strings.HasPrefix(strOut, expected) { t.Errorf("id command output: %q, expected prefix: %q", strOut, expected) } @@ -245,6 +268,7 @@ func TestGroupCleanupUserNamespace(t *testing.T) { "uid=0(root) gid=0(root) groups=0(root),65534(nobody)", "uid=0(root) gid=0(root) groups=0(root),65534(nogroup)", "uid=0(root) gid=0(root) groups=0(root),65534", + "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)", // Alpine; see https://golang.org/issue/19938 } for _, e := range expected { if strOut == e { @@ -253,3 +277,282 @@ func TestGroupCleanupUserNamespace(t *testing.T) { } t.Errorf("id command output: %q, expected one of %q", strOut, expected) } + +// TestUnshareHelperProcess isn't a real test. It's used as a helper process +// for TestUnshareMountNameSpace. +func TestUnshareMountNameSpaceHelper(*testing.T) { + if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { + return + } + defer os.Exit(0) + if err := syscall.Mount("none", flag.Args()[0], "proc", 0, ""); err != nil { + fmt.Fprintf(os.Stderr, "unshare: mount %v failed: %v", os.Args, err) + os.Exit(2) + } +} + +// Test for Issue 38471: unshare fails because systemd has forced / to be shared +func TestUnshareMountNameSpace(t *testing.T) { + // Make sure we are running as root so we have permissions to use unshare + // and create a network namespace. + if os.Getuid() != 0 { + t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace") + } + + // When running under the Go continuous build, skip tests for + // now when under Kubernetes. (where things are root but not quite) + // Both of these are our own environment variables. + // See Issue 12815. + if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" { + t.Skip("skipping test on Kubernetes-based builders; see Issue 12815") + } + + d, err := ioutil.TempDir("", "unshare") + if err != nil { + t.Fatalf("tempdir: %v", err) + } + + cmd := exec.Command(os.Args[0], "-test.run=TestUnshareMountNameSpaceHelper", d) + cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} + cmd.SysProcAttr = &syscall.SysProcAttr{Unshareflags: syscall.CLONE_NEWNS} + + o, err := cmd.CombinedOutput() + if err != nil { + if strings.Contains(err.Error(), ": permission denied") { + t.Skipf("Skipping test (golang.org/issue/19698); unshare failed due to permissions: %s, %v", o, err) + } + t.Fatalf("unshare failed: %s, %v", o, err) + } + + // How do we tell if the namespace was really unshared? It turns out + // to be simple: just try to remove the directory. If it's still mounted + // on the rm will fail with EBUSY. Then we have some cleanup to do: + // we must unmount it, then try to remove it again. + + if err := os.Remove(d); err != nil { + t.Errorf("rmdir failed on %v: %v", d, err) + if err := syscall.Unmount(d, syscall.MNT_FORCE); err != nil { + t.Errorf("Can't unmount %v: %v", d, err) + } + if err := os.Remove(d); err != nil { + t.Errorf("rmdir after unmount failed on %v: %v", d, err) + } + } +} + +// Test for Issue 20103: unshare fails when chroot is used +func TestUnshareMountNameSpaceChroot(t *testing.T) { + // Make sure we are running as root so we have permissions to use unshare + // and create a network namespace. + if os.Getuid() != 0 { + t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace") + } + + // When running under the Go continuous build, skip tests for + // now when under Kubernetes. (where things are root but not quite) + // Both of these are our own environment variables. + // See Issue 12815. + if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" { + t.Skip("skipping test on Kubernetes-based builders; see Issue 12815") + } + + d, err := ioutil.TempDir("", "unshare") + if err != nil { + t.Fatalf("tempdir: %v", err) + } + + // Since we are doing a chroot, we need the binary there, + // and it must be statically linked. + x := filepath.Join(d, "syscall.test") + cmd := exec.Command(testenv.GoToolPath(t), "test", "-c", "-o", x, "syscall") + cmd.Env = append(os.Environ(), "CGO_ENABLED=0") + if o, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("Build of syscall in chroot failed, output %v, err %v", o, err) + } + + cmd = exec.Command("/syscall.test", "-test.run=TestUnshareMountNameSpaceHelper", "/") + cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} + cmd.SysProcAttr = &syscall.SysProcAttr{Chroot: d, Unshareflags: syscall.CLONE_NEWNS} + + o, err := cmd.CombinedOutput() + if err != nil { + if strings.Contains(err.Error(), ": permission denied") { + t.Skipf("Skipping test (golang.org/issue/19698); unshare failed due to permissions: %s, %v", o, err) + } + t.Fatalf("unshare failed: %s, %v", o, err) + } + + // How do we tell if the namespace was really unshared? It turns out + // to be simple: just try to remove the executable. If it's still mounted + // on, the rm will fail. Then we have some cleanup to do: + // we must force unmount it, then try to remove it again. + + if err := os.Remove(x); err != nil { + t.Errorf("rm failed on %v: %v", x, err) + if err := syscall.Unmount(d, syscall.MNT_FORCE); err != nil { + t.Fatalf("Can't unmount %v: %v", d, err) + } + if err := os.Remove(x); err != nil { + t.Fatalf("rm failed on %v: %v", x, err) + } + } + + if err := os.Remove(d); err != nil { + t.Errorf("rmdir failed on %v: %v", d, err) + } +} + +type capHeader struct { + version uint32 + pid int +} + +type capData struct { + effective uint32 + permitted uint32 + inheritable uint32 +} + +const CAP_SYS_TIME = 25 + +type caps struct { + hdr capHeader + data [2]capData +} + +func getCaps() (caps, error) { + var c caps + + // Get capability version + if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(nil)), 0); errno != 0 { + return c, fmt.Errorf("SYS_CAPGET: %v", errno) + } + + // Get current capabilities + if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(&c.data[0])), 0); errno != 0 { + return c, fmt.Errorf("SYS_CAPGET: %v", errno) + } + + return c, nil +} + +func mustSupportAmbientCaps(t *testing.T) { + var uname syscall.Utsname + if err := syscall.Uname(&uname); err != nil { + t.Fatalf("Uname: %v", err) + } + var buf [65]byte + for i, b := range uname.Release { + buf[i] = byte(b) + } + ver := string(buf[:]) + if i := strings.Index(ver, "\x00"); i != -1 { + ver = ver[:i] + } + if strings.HasPrefix(ver, "2.") || + strings.HasPrefix(ver, "3.") || + strings.HasPrefix(ver, "4.1.") || + strings.HasPrefix(ver, "4.2.") { + t.Skipf("kernel version %q predates required 4.3; skipping test", ver) + } +} + +// TestAmbientCapsHelper isn't a real test. It's used as a helper process for +// TestAmbientCaps. +func TestAmbientCapsHelper(*testing.T) { + if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { + return + } + defer os.Exit(0) + + caps, err := getCaps() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } + if caps.data[0].effective&(1<<uint(CAP_SYS_TIME)) == 0 { + fmt.Fprintln(os.Stderr, "CAP_SYS_TIME unexpectedly not in the effective capability mask") + os.Exit(2) + } +} + +func TestAmbientCaps(t *testing.T) { + // Make sure we are running as root so we have permissions to use unshare + // and create a network namespace. + if os.Getuid() != 0 { + t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace") + } + mustSupportAmbientCaps(t) + + // When running under the Go continuous build, skip tests for + // now when under Kubernetes. (where things are root but not quite) + // Both of these are our own environment variables. + // See Issue 12815. + if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" { + t.Skip("skipping test on Kubernetes-based builders; see Issue 12815") + } + + caps, err := getCaps() + if err != nil { + t.Fatal(err) + } + + // Add CAP_SYS_TIME to the permitted and inheritable capability mask, + // otherwise we will not be able to add it to the ambient capability mask. + caps.data[0].permitted |= 1 << uint(CAP_SYS_TIME) + caps.data[0].inheritable |= 1 << uint(CAP_SYS_TIME) + + if _, _, errno := syscall.Syscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(&caps.hdr)), uintptr(unsafe.Pointer(&caps.data[0])), 0); errno != 0 { + t.Fatalf("SYS_CAPSET: %v", errno) + } + + u, err := user.Lookup("nobody") + if err != nil { + t.Fatal(err) + } + uid, err := strconv.ParseInt(u.Uid, 0, 32) + if err != nil { + t.Fatal(err) + } + gid, err := strconv.ParseInt(u.Gid, 0, 32) + if err != nil { + t.Fatal(err) + } + + // Copy the test binary to a temporary location which is readable by nobody. + f, err := ioutil.TempFile("", "gotest") + if err != nil { + t.Fatal(err) + } + defer os.Remove(f.Name()) + defer f.Close() + e, err := os.Open(os.Args[0]) + if err != nil { + t.Fatal(err) + } + defer e.Close() + if _, err := io.Copy(f, e); err != nil { + t.Fatal(err) + } + if err := f.Chmod(0755); err != nil { + t.Fatal(err) + } + if err := f.Close(); err != nil { + t.Fatal(err) + } + + cmd := exec.Command(f.Name(), "-test.run=TestAmbientCapsHelper") + cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.SysProcAttr = &syscall.SysProcAttr{ + Credential: &syscall.Credential{ + Uid: uint32(uid), + Gid: uint32(gid), + }, + AmbientCaps: []uintptr{CAP_SYS_TIME}, + } + if err := cmd.Run(); err != nil { + t.Fatal(err.Error()) + } +} diff --git a/libgo/go/syscall/exec_unix.go b/libgo/go/syscall/exec_unix.go index f2bc741..8d83e91 100644 --- a/libgo/go/syscall/exec_unix.go +++ b/libgo/go/syscall/exec_unix.go @@ -163,9 +163,10 @@ func SetNonblock(fd int, nonblocking bool) (err error) { // Credential holds user and group identities to be assumed // by a child process started by StartProcess. type Credential struct { - Uid uint32 // User ID. - Gid uint32 // Group ID. - Groups []uint32 // Supplementary group IDs. + Uid uint32 // User ID. + Gid uint32 // Group ID. + Groups []uint32 // Supplementary group IDs. + NoSetGroups bool // If true, don't set supplementary groups } // ProcAttr holds attributes that will be applied to a new process started @@ -292,6 +293,14 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle return pid, 0, err } +// Implemented in runtime package. +func runtime_BeforeExec() +func runtime_AfterExec() + +// execveSolaris is non-nil on Solaris, set to execve in exec_solaris.go; this +// avoids a build dependency for other platforms. +var execveSolaris func(path uintptr, argv uintptr, envp uintptr) (err Errno) + // Exec invokes the execve(2) system call. func Exec(argv0 string, argv []string, envv []string) (err error) { argv0p, err := BytePtrFromString(argv0) @@ -306,6 +315,9 @@ func Exec(argv0 string, argv []string, envv []string) (err error) { if err != nil { return err } + runtime_BeforeExec() + err1 := raw_execve(argv0p, &argvp[0], &envvp[0]) + runtime_AfterExec() return Errno(err1) } diff --git a/libgo/go/syscall/forkpipe_bsd.go b/libgo/go/syscall/forkpipe_bsd.go new file mode 100644 index 0000000..d418072 --- /dev/null +++ b/libgo/go/syscall/forkpipe_bsd.go @@ -0,0 +1,20 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly netbsd openbsd + +package syscall + +func forkExecPipe(p []int) error { + err := Pipe(p) + if err != nil { + return err + } + _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC) + if err != nil { + return err + } + _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC) + return err +} diff --git a/libgo/go/syscall/net.go b/libgo/go/syscall/net.go new file mode 100644 index 0000000..272d3af --- /dev/null +++ b/libgo/go/syscall/net.go @@ -0,0 +1,34 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syscall + +// A RawConn is a raw network connection. +type RawConn interface { + // Control invokes f on the underlying connection's file + // descriptor or handle. + // The file descriptor fd is guaranteed to remain valid while + // f executes but not after f returns. + Control(f func(fd uintptr)) error + + // Read invokes f on the underlying connection's file + // descriptor or handle; f is expected to try to read from the + // file descriptor. + // If f returns true, Read returns. Otherwise Read blocks + // waiting for the connection to be ready for reading and + // tries again repeatedly. + // The file descriptor is guaranteed to remain valid while f + // executes but not after f returns. + Read(f func(fd uintptr) (done bool)) error + + // Write is like Read but for writing. + Write(f func(fd uintptr) (done bool)) error +} + +// Conn is implemented by some types in the net package to provide +// access to the underlying file descriptor or handle. +type Conn interface { + // SyscallConn returns a raw network connection. + SyscallConn() (RawConn, error) +} diff --git a/libgo/go/syscall/syscall.go b/libgo/go/syscall/syscall.go index 91dfa9a..8415383 100644 --- a/libgo/go/syscall/syscall.go +++ b/libgo/go/syscall/syscall.go @@ -22,7 +22,10 @@ // Go repository should be migrated to use the corresponding // package in the golang.org/x/sys repository. That is also where updates // required by new systems or versions should be applied. -// See https://golang.org/s/go1.4-syscall for more information. +// Signal, Errno and SysProcAttr are not yet available in +// golang.org/x/sys and must still be referenced from the +// syscall package. See https://golang.org/s/go1.4-syscall +// for more information. // package syscall @@ -102,11 +105,3 @@ func (tv *Timeval) Nano() int64 { // Getpagesize is provided by the runtime. func Getpagesize() int - -// use is a no-op, but the compiler cannot see that it is. -// Calling use(p) ensures that p is kept live until that point. -// This was needed until Go 1.6 to call syscall.Syscall correctly. -// As of Go 1.6 the compiler handles that case automatically. -// The uses and definition of use can be removed early in the Go 1.7 cycle. -//go:noescape -func use(p unsafe.Pointer) diff --git a/libgo/go/syscall/syscall_dragonfly.go b/libgo/go/syscall/syscall_dragonfly.go index c2fc67f..3e6617c 100644 --- a/libgo/go/syscall/syscall_dragonfly.go +++ b/libgo/go/syscall/syscall_dragonfly.go @@ -1,4 +1,4 @@ -// Copyright 2009,2010 The Go Authors. All rights reserved. +// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -15,7 +15,7 @@ func direntReclen(buf []byte) (uint64, bool) { if !ok { return 0, false } - return (16 + namlen + 1 + 7) & ^uint64(7), true + return (16 + namlen + 1 + 7) &^ 7, true } func direntNamlen(buf []byte) (uint64, bool) { diff --git a/libgo/go/syscall/syscall_linux_386.go b/libgo/go/syscall/syscall_linux_386.go index 591d3e1..9abcab9 100644 --- a/libgo/go/syscall/syscall_linux_386.go +++ b/libgo/go/syscall/syscall_linux_386.go @@ -22,3 +22,7 @@ func PtraceGetRegs(pid int, regsout *PtraceRegs) (err error) { func PtraceSetRegs(pid int, regs *PtraceRegs) (err error) { return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs))) } + +func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) { + panic("not implemented") +} diff --git a/libgo/go/syscall/syscall_linux_amd64.go b/libgo/go/syscall/syscall_linux_amd64.go index 609faed06..8ee7cd7 100644 --- a/libgo/go/syscall/syscall_linux_amd64.go +++ b/libgo/go/syscall/syscall_linux_amd64.go @@ -23,3 +23,5 @@ func PtraceGetRegs(pid int, regsout *PtraceRegs) (err error) { func PtraceSetRegs(pid int, regs *PtraceRegs) (err error) { return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs))) } + +func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) diff --git a/libgo/go/syscall/syscall_linux_mipsx.go b/libgo/go/syscall/syscall_linux_mipsx.go index 06dd1ea..1a15218 100644 --- a/libgo/go/syscall/syscall_linux_mipsx.go +++ b/libgo/go/syscall/syscall_linux_mipsx.go @@ -24,3 +24,7 @@ func PtraceGetRegs(pid int, regsout *PtraceRegs) (err error) { func PtraceSetRegs(pid int, regs *PtraceRegs) (err error) { return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs))) } + +func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) { + panic("not implemented") +} diff --git a/libgo/go/syscall/syscall_linux_s390x.go b/libgo/go/syscall/syscall_linux_s390x.go index 1767a6e..fa3bb30 100644 --- a/libgo/go/syscall/syscall_linux_s390x.go +++ b/libgo/go/syscall/syscall_linux_s390x.go @@ -46,3 +46,7 @@ func PtraceSetRegs(pid int, regs *PtraceRegs) (err error) { } return ptrace(PTRACE_POKEUSR_AREA, pid, uintptr(unsafe.Pointer(&parea)), 0) } + +func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) { + panic("not implemented") +} diff --git a/libgo/go/syscall/syscall_unix_test.go b/libgo/go/syscall/syscall_unix_test.go index 2f25d18..b1fe78d 100644 --- a/libgo/go/syscall/syscall_unix_test.go +++ b/libgo/go/syscall/syscall_unix_test.go @@ -78,12 +78,16 @@ func TestFcntlFlock(t *testing.T) { } if os.Getenv("GO_WANT_HELPER_PROCESS") == "" { // parent - name := filepath.Join(os.TempDir(), "TestFcntlFlock") + tempDir, err := ioutil.TempDir("", "TestFcntlFlock") + if err != nil { + t.Fatalf("Failed to create temp dir: %v", err) + } + name := filepath.Join(tempDir, "TestFcntlFlock") fd, err := syscall.Open(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0) if err != nil { t.Fatalf("Open failed: %v", err) } - defer syscall.Unlink(name) + defer os.RemoveAll(tempDir) defer syscall.Close(fd) if err := syscall.Ftruncate(fd, 1<<20); err != nil { t.Fatalf("Ftruncate(1<<20) failed: %v", err) |