diff options
Diffstat (limited to 'libgo/go/syscall')
30 files changed, 1716 insertions, 332 deletions
diff --git a/libgo/go/syscall/const_plan9.go b/libgo/go/syscall/const_plan9.go new file mode 100644 index 00000000000..ba26f123dea --- /dev/null +++ b/libgo/go/syscall/const_plan9.go @@ -0,0 +1,59 @@ +package syscall + +// Plan 9 Constants + +// Open modes +const ( + O_RDONLY = 0 + O_WRONLY = 1 + O_RDWR = 2 + O_TRUNC = 16 + O_CLOEXEC = 32 + O_EXCL = 0x1000 +) + +// Rfork flags +const ( + RFNAMEG = 1 << 0 + RFENVG = 1 << 1 + RFFDG = 1 << 2 + RFNOTEG = 1 << 3 + RFPROC = 1 << 4 + RFMEM = 1 << 5 + RFNOWAIT = 1 << 6 + RFCNAMEG = 1 << 10 + RFCENVG = 1 << 11 + RFCFDG = 1 << 12 + RFREND = 1 << 13 + RFNOMNT = 1 << 14 +) + +// Qid.Type bits +const ( + QTDIR = 0x80 + QTAPPEND = 0x40 + QTEXCL = 0x20 + QTMOUNT = 0x10 + QTAUTH = 0x08 + QTTMP = 0x04 + QTFILE = 0x00 +) + +// Dir.Mode bits +const ( + DMDIR = 0x80000000 + DMAPPEND = 0x40000000 + DMEXCL = 0x20000000 + DMMOUNT = 0x10000000 + DMAUTH = 0x08000000 + DMTMP = 0x04000000 + DMREAD = 0x4 + DMWRITE = 0x2 + DMEXEC = 0x1 +) + +const ( + STATMAX = 65535 + ERRMAX = 128 + STATFIXLEN = 49 +) diff --git a/libgo/go/syscall/creds_test.go b/libgo/go/syscall/creds_test.go index b1894c66b07..b4a14ff4dd9 100644 --- a/libgo/go/syscall/creds_test.go +++ b/libgo/go/syscall/creds_test.go @@ -56,7 +56,13 @@ func TestSCMCredentials(t *testing.T) { ucred.Gid = 0 oob := syscall.UnixCredentials(&ucred) _, _, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil) - if err.(*net.OpError).Err != syscall.EPERM { + if op, ok := err.(*net.OpError); ok { + err = op.Err + } + if sys, ok := err.(*os.SyscallError); ok { + err = sys.Err + } + if err != syscall.EPERM { t.Fatalf("WriteMsgUnix failed with %v, want EPERM", err) } } diff --git a/libgo/go/syscall/env_plan9.go b/libgo/go/syscall/env_plan9.go index 9ea36c886ab..cbf7f410925 100644 --- a/libgo/go/syscall/env_plan9.go +++ b/libgo/go/syscall/env_plan9.go @@ -16,7 +16,7 @@ var ( ) func readenv(key string) (string, error) { - fd, err := Open("/env/"+key, O_RDONLY) + fd, err := open("/env/"+key, O_RDONLY) if err != nil { return "", err } @@ -35,7 +35,7 @@ func readenv(key string) (string, error) { } func writeenv(key, value string) error { - fd, err := Create("/env/"+key, O_RDWR, 0666) + fd, err := create("/env/"+key, O_RDWR, 0666) if err != nil { return err } @@ -86,7 +86,7 @@ func Unsetenv(key string) error { } func Environ() []string { - fd, err := Open("/env", O_RDONLY) + fd, err := open("/env", O_RDONLY) if err != nil { return nil } diff --git a/libgo/go/syscall/env_windows.go b/libgo/go/syscall/env_windows.go index bc21690d9fd..1cb475428d7 100644 --- a/libgo/go/syscall/env_windows.go +++ b/libgo/go/syscall/env_windows.go @@ -16,19 +16,17 @@ func Getenv(key string) (value string, found bool) { if err != nil { return "", false } - b := make([]uint16, 100) - n, e := GetEnvironmentVariable(keyp, &b[0], uint32(len(b))) - if n == 0 && e == ERROR_ENVVAR_NOT_FOUND { - return "", false - } - if n > uint32(len(b)) { - b = make([]uint16, n) - n, e = GetEnvironmentVariable(keyp, &b[0], uint32(len(b))) - if n > uint32(len(b)) { - n = 0 + n := uint32(100) + for { + b := make([]uint16, n) + n, err = GetEnvironmentVariable(keyp, &b[0], uint32(len(b))) + if n == 0 && err == ERROR_ENVVAR_NOT_FOUND { + return "", false + } + if n <= uint32(len(b)) { + return string(utf16.Decode(b[:n])), true } } - return string(utf16.Decode(b[0:n])), true } func Setenv(key, value string) error { diff --git a/libgo/go/syscall/errors_plan9.go b/libgo/go/syscall/errors_plan9.go new file mode 100644 index 00000000000..ede3d6a329e --- /dev/null +++ b/libgo/go/syscall/errors_plan9.go @@ -0,0 +1,48 @@ +// 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. + +package syscall + +// Constants +const ( + // Invented values to support what package os expects. + O_CREAT = 0x02000 + O_APPEND = 0x00400 + O_NOCTTY = 0x00000 + O_NONBLOCK = 0x00000 + O_SYNC = 0x00000 + O_ASYNC = 0x00000 + + S_IFMT = 0x1f000 + S_IFIFO = 0x1000 + S_IFCHR = 0x2000 + S_IFDIR = 0x4000 + S_IFBLK = 0x6000 + S_IFREG = 0x8000 + S_IFLNK = 0xa000 + S_IFSOCK = 0xc000 +) + +// Errors +var ( + EINVAL = NewError("bad arg in system call") + ENOTDIR = NewError("not a directory") + EISDIR = NewError("file is a directory") + ENOENT = NewError("file does not exist") + EEXIST = NewError("file already exists") + EMFILE = NewError("no free file descriptors") + EIO = NewError("i/o error") + ENAMETOOLONG = NewError("file name too long") + EINTR = NewError("interrupted") + EPERM = NewError("permission denied") + EBUSY = NewError("no free devices") + ETIMEDOUT = NewError("connection timed out") + EPLAN9 = NewError("not supported by plan 9") + + // The following errors do not correspond to any + // Plan 9 system messages. Invented to support + // what package os and others expect. + EACCES = NewError("access permission denied") + EAFNOSUPPORT = NewError("address family not supported by protocol") +) diff --git a/libgo/go/syscall/exec_bsd.go b/libgo/go/syscall/exec_bsd.go index 217e0c842df..f44e897f39b 100644 --- a/libgo/go/syscall/exec_bsd.go +++ b/libgo/go/syscall/exec_bsd.go @@ -15,9 +15,12 @@ type SysProcAttr struct { Credential *Credential // Credential. Ptrace bool // Enable tracing. Setsid bool // Create session. - Setpgid bool // Set process group ID to new pid (SYSV setpgrp) - Setctty bool // Set controlling terminal to fd 0 + Setpgid bool // Set process group ID to Pgid, or, if Pgid == 0, to new pid. + Setctty bool // Set controlling terminal to fd Ctty Noctty bool // Detach fd 0 from controlling terminal + Ctty int // Controlling TTY fd + Foreground bool // Place child's process group in foreground. (Implies Setpgid. Uses Ctty as fd of controlling TTY) + Pgid int // Child's process group ID if Setpgid. } // Implemented in runtime package. @@ -90,8 +93,24 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } // Set process group - if sys.Setpgid { - err1 = raw_setpgid(0, 0) + if sys.Setpgid || sys.Foreground { + // Place child in process group. + err1 = raw_setpgid(0, sys.Pgid) + if err1 != 0 { + goto childerror + } + } + + if sys.Foreground { + pgrp := sys.Pgid + if pgrp == 0 { + r1 = raw_getpid() + + pgrp = int(r1) + } + + // Place process group in foreground. + _, err1 = raw_ioctl_ptr(sys.Ctty, TIOCSPGRP, unsafe.Pointer(&pgrp)) if err1 != 0 { goto childerror } @@ -215,9 +234,9 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } } - // Make fd 0 the tty + // Set the controlling TTY to Ctty if sys.Setctty { - _, err1 = raw_ioctl(0, TIOCSCTTY, 0) + _, err1 = raw_ioctl(sys.Ctty, TIOCSCTTY, 0) if err1 != 0 { goto childerror } diff --git a/libgo/go/syscall/exec_linux.go b/libgo/go/syscall/exec_linux.go index 97bde0c4f52..d9600a142ae 100644 --- a/libgo/go/syscall/exec_linux.go +++ b/libgo/go/syscall/exec_linux.go @@ -27,14 +27,21 @@ type SysProcAttr struct { Credential *Credential // Credential. Ptrace bool // Enable tracing. Setsid bool // Create session. - Setpgid bool // Set process group ID to new pid (SYSV setpgrp) + Setpgid bool // Set process group ID to Pgid, or, if Pgid == 0, to new pid. Setctty bool // Set controlling terminal to fd Ctty (only meaningful if Setsid is set) Noctty bool // Detach fd 0 from controlling terminal - Ctty int // Controlling TTY fd (Linux only) + Ctty int // Controlling TTY fd + Foreground bool // Place child's process group in foreground. (Implies Setpgid. Uses Ctty as fd of controlling TTY) + Pgid int // Child's process group ID if Setpgid. Pdeathsig Signal // Signal that the process will get when its parent dies (Linux only) Cloneflags uintptr // Flags for clone calls (Linux only) UidMappings []SysProcIDMap // User ID mappings for user namespaces. GidMappings []SysProcIDMap // Group ID mappings for user namespaces. + // GidMappingsEnableSetgroups enabling setgroups syscall. + // If false, then setgroups syscall will be disabled for the child process. + // This parameter is no-op if GidMappings == nil. Otherwise for unprivileged + // users this should be set to false for mappings work. + GidMappingsEnableSetgroups bool } // Implemented in runtime package. @@ -62,6 +69,9 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr p [2]int ) + // Record parent PID so child can test if it has died. + ppid := raw_getpid() + // Guard against side effects of shuffling fds below. // Make sure that nextfd is beyond any currently open files so // that we can't run the risk of overwriting any of them. @@ -135,27 +145,6 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } } - // Parent death signal - if sys.Pdeathsig != 0 { - _, err1 = raw_prctl(PR_SET_PDEATHSIG, int(sys.Pdeathsig), 0, 0, 0) - if err1 != 0 { - goto childerror - } - - // Signal self if parent is already dead. This might cause a - // duplicate signal in rare cases, but it won't matter when - // using SIGKILL. - ppid := Getppid() - if ppid == 1 { - pid = Getpid() - err2 := Kill(pid, sys.Pdeathsig) - if err2 != nil { - err1 = err2.(Errno) - goto childerror - } - } - } - // Enable tracing if requested. if sys.Ptrace { err1 = raw_ptrace(_PTRACE_TRACEME, 0, nil, nil) @@ -173,8 +162,27 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } // Set process group - if sys.Setpgid { - err1 = raw_setpgid(0, 0) + if sys.Setpgid || sys.Foreground { + // Place child in process group. + err1 = raw_setpgid(0, sys.Pgid) + if err1 != 0 { + goto childerror + } + } + + if sys.Foreground { + pgrp := int32(sys.Pgid) + if pgrp == 0 { + r1 = uintptr(raw_getpid()) + if err1 != 0 { + goto childerror + } + + pgrp = int32(r1) + } + + // Place process group in foreground. + _, err1 = raw_ioctl_ptr(sys.Ctty, TIOCSPGRP, unsafe.Pointer(&pgrp)) if err1 != 0 { goto childerror } @@ -191,36 +199,19 @@ 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) - for i, v := range cred.Groups { - groups[i] = Gid_t(v) - } - err2 := setgroups(ngroups, &groups[0]) - if err2 == nil { - err1 = 0 - } else { - err1 = err2.(Errno) + if ngroups > 0 { + groups := unsafe.Pointer(&cred.Groups[0]) + err1 = raw_setgroups(ngroups, groups) + if err1 != 0 { + goto childerror } } + err1 = raw_setgid(int(cred.Gid)) if err1 != 0 { goto childerror } - err2 := Setgid(int(cred.Gid)) - if err2 != nil { - err1 = err2.(Errno) - goto childerror - } - err2 = Setuid(int(cred.Uid)) - if err2 != nil { - err1 = err2.(Errno) + err1 = raw_setuid(int(cred.Uid)) + if err1 != 0 { goto childerror } } @@ -233,6 +224,26 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } } + // Parent death signal + if sys.Pdeathsig != 0 { + _, err1 = raw_prctl(PR_SET_PDEATHSIG, int(sys.Pdeathsig), 0, 0, 0) + if err1 != 0 { + goto childerror + } + + // Signal self if parent is already dead. This might cause a + // duplicate signal in rare cases, but it won't matter when + // using SIGKILL. + r1 := raw_getppid() + if r1 != ppid { + pid = raw_getpid() + err1 = raw_kill(pid, sys.Pdeathsig) + if err1 != 0 { + goto childerror + } + } + } + // Pass 1: look for fd[i] < i and move those up above len(fd) // so that pass 2 won't stomp on an fd it needs later. if pipe < nextfd { @@ -298,9 +309,9 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } } - // Make fd 0 the tty - if sys.Setctty && sys.Ctty >= 0 { - _, err1 = raw_ioctl(0, TIOCSCTTY, sys.Ctty) + // Set the controlling TTY to Ctty + if sys.Setctty { + _, err1 = raw_ioctl(sys.Ctty, TIOCSCTTY, 0) if err1 != 0 { goto childerror } @@ -364,6 +375,32 @@ func writeIDMappings(path string, idMap []SysProcIDMap) error { return nil } +// writeSetgroups writes to /proc/PID/setgroups "deny" if enable is false +// and "allow" if enable is true. +// This is needed since kernel 3.19, because you can't write gid_map without +// disabling setgroups() system call. +func writeSetgroups(pid int, enable bool) error { + sgf := "/proc/" + itoa(pid) + "/setgroups" + fd, err := Open(sgf, O_RDWR, 0) + if err != nil { + return err + } + + var data []byte + if enable { + data = []byte("allow") + } else { + data = []byte("deny") + } + + if _, err := Write(fd, data); err != nil { + Close(fd) + return err + } + + return Close(fd) +} + // writeUidGidMappings writes User ID and Group ID mappings for user namespaces // for a process and it is called from the parent process. func writeUidGidMappings(pid int, sys *SysProcAttr) error { @@ -375,6 +412,10 @@ func writeUidGidMappings(pid int, sys *SysProcAttr) error { } if sys.GidMappings != nil { + // If the kernel is too old to support /proc/PID/setgroups, writeSetGroups will return ENOENT; this is OK. + if err := writeSetgroups(pid, sys.GidMappingsEnableSetgroups); err != nil && err != ENOENT { + return err + } gidf := "/proc/" + itoa(pid) + "/gid_map" if err := writeIDMappings(gidf, sys.GidMappings); err != nil { return err diff --git a/libgo/go/syscall/exec_linux_test.go b/libgo/go/syscall/exec_linux_test.go new file mode 100644 index 00000000000..60d2734f66a --- /dev/null +++ b/libgo/go/syscall/exec_linux_test.go @@ -0,0 +1,111 @@ +// Copyright 2015 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 linux + +package syscall_test + +import ( + "io/ioutil" + "os" + "os/exec" + "regexp" + "strconv" + "strings" + "syscall" + "testing" +) + +func whoamiCmd(t *testing.T, uid, gid int, setgroups bool) *exec.Cmd { + if _, err := os.Stat("/proc/self/ns/user"); err != nil { + if os.IsNotExist(err) { + t.Skip("kernel doesn't support user namespaces") + } + t.Fatalf("Failed to stat /proc/self/ns/user: %v", err) + } + cmd := exec.Command("whoami") + cmd.SysProcAttr = &syscall.SysProcAttr{ + Cloneflags: syscall.CLONE_NEWUSER, + UidMappings: []syscall.SysProcIDMap{ + {ContainerID: 0, HostID: uid, Size: 1}, + }, + GidMappings: []syscall.SysProcIDMap{ + {ContainerID: 0, HostID: gid, Size: 1}, + }, + GidMappingsEnableSetgroups: setgroups, + } + return cmd +} + +func testNEWUSERRemap(t *testing.T, uid, gid int, setgroups bool) { + cmd := whoamiCmd(t, uid, gid, setgroups) + out, err := cmd.CombinedOutput() + if err != nil { + // On some systems, there is a sysctl setting. + if os.IsPermission(err) && os.Getuid() != 0 { + data, errRead := ioutil.ReadFile("/proc/sys/kernel/unprivileged_userns_clone") + if errRead == nil && data[0] == '0' { + t.Skip("kernel prohibits user namespace in unprivileged process") + } + } + + t.Fatalf("Cmd failed with err %v, output: %s", err, out) + } + sout := strings.TrimSpace(string(out)) + want := "root" + if sout != want { + t.Fatalf("whoami = %q; want %q", out, want) + } +} + +func TestCloneNEWUSERAndRemapRootDisableSetgroups(t *testing.T) { + if os.Getuid() != 0 { + t.Skip("skipping root only test") + } + testNEWUSERRemap(t, 0, 0, false) +} + +func TestCloneNEWUSERAndRemapRootEnableSetgroups(t *testing.T) { + if os.Getuid() != 0 { + t.Skip("skipping root only test") + } + testNEWUSERRemap(t, 0, 0, false) +} + +// kernelVersion returns the major and minor versions of the Linux +// kernel version. It calls t.Skip if it can't figure it out. +func kernelVersion(t *testing.T) (int, int) { + bytes, err := ioutil.ReadFile("/proc/version") + if err != nil { + t.Skipf("can't get kernel version: %v", err) + } + matches := regexp.MustCompile("([0-9]+).([0-9]+)").FindSubmatch(bytes) + if len(matches) < 3 { + t.Skipf("can't get kernel version from %s", bytes) + } + major, _ := strconv.Atoi(string(matches[1])) + minor, _ := strconv.Atoi(string(matches[2])) + return major, minor +} + +func TestCloneNEWUSERAndRemapNoRootDisableSetgroups(t *testing.T) { + if os.Getuid() == 0 { + t.Skip("skipping unprivileged user only test") + } + testNEWUSERRemap(t, os.Getuid(), os.Getgid(), false) +} + +func TestCloneNEWUSERAndRemapNoRootSetgroupsEnableSetgroups(t *testing.T) { + if os.Getuid() == 0 { + t.Skip("skipping unprivileged user only test") + } + cmd := whoamiCmd(t, os.Getuid(), os.Getgid(), true) + err := cmd.Run() + if err == nil { + t.Skip("probably old kernel without security fix") + } + if !os.IsPermission(err) { + t.Fatalf("Unprivileged gid_map rewriting with GidMappingsEnableSetgroups must fail") + } +} diff --git a/libgo/go/syscall/exec_solaris_test.go b/libgo/go/syscall/exec_solaris_test.go new file mode 100644 index 00000000000..6b8f1ad3837 --- /dev/null +++ b/libgo/go/syscall/exec_solaris_test.go @@ -0,0 +1,37 @@ +// Copyright 2015 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 solaris + +package syscall + +import "unsafe" + +//go:cgo_import_dynamic libc_Getpgid getpgid "libc.so" +//go:cgo_import_dynamic libc_Getpgrp getpgrp "libc.so" + +//go:linkname libc_Getpgid libc_Getpgid +//go:linkname libc_Getpgrp libc_Getpgrp + +var ( + libc_Getpgid, + libc_Getpgrp libcFunc +) + +func Getpgid(pid int) (pgid int, err error) { + r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_Getpgid)), 1, uintptr(pid), 0, 0, 0, 0, 0) + pgid = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +func Getpgrp() (pgrp int) { + r0, _, _ := sysvicall6(uintptr(unsafe.Pointer(&libc_Getpgrp)), 0, 0, 0, 0, 0, 0, 0) + pgrp = int(r0) + return +} + +var Ioctl = ioctl diff --git a/libgo/go/syscall/exec_unix.go b/libgo/go/syscall/exec_unix.go index a49d95bc94c..7427c57b703 100644 --- a/libgo/go/syscall/exec_unix.go +++ b/libgo/go/syscall/exec_unix.go @@ -17,6 +17,12 @@ import ( //sysnb raw_fork() (pid Pid_t, err Errno) //fork() Pid_t +//sysnb raw_getpid() (pid int) +//getpid() Pid_t + +//sysnb raw_getppid() (pid int) +//getppid() Pid_t + //sysnb raw_setsid() (err Errno) //setsid() Pid_t @@ -36,7 +42,10 @@ import ( //close(fd _C_int) _C_int //sysnb raw_ioctl(fd int, cmd int, val int) (rval int, err Errno) -//ioctl(fd _C_int, cmd _C_int, val _C_int) _C_int +//__go_ioctl(fd _C_int, cmd _C_int, val _C_int) _C_int + +//sysnb raw_ioctl_ptr(fd int, cmd int, val unsafe.Pointer) (rval int, err Errno) +//__go_ioctl_ptr(fd _C_int, cmd _C_int, val unsafe.Pointer) _C_int //sysnb raw_execve(argv0 *byte, argv **byte, envv **byte) (err Errno) //execve(argv0 *byte, argv **byte, envv **byte) _C_int @@ -50,6 +59,18 @@ import ( //sysnb raw_dup2(oldfd int, newfd int) (err Errno) //dup2(oldfd _C_int, newfd _C_int) _C_int +//sysnb raw_kill(pid int, sig Signal) (err Errno) +//kill(pid Pid_t, sig _C_int) _C_int + +//sysnb raw_setgroups(size int, list unsafe.Pointer) (err Errno) +//setgroups(size Size_t, list *Gid_t) _C_int + +//sysnb raw_setuid(uid int) (err Errno) +//setuid(uid Uid_t) _C_int + +//sysnb raw_setgid(gid int) (err Errno) +//setgid(gid Gid_t) _C_int + // Lock synchronizing creation of new file descriptors with fork. // // We want the child in a fork/exec sequence to inherit only the @@ -99,9 +120,11 @@ import ( var ForkLock sync.RWMutex -// StringSlicePtr is deprecated. Use SlicePtrFromStrings instead. -// If any string contains a NUL byte this function panics instead -// of returning an error. +// StringSlicePtr converts a slice of strings to a slice of pointers +// to NUL-terminated byte arrays. If any string contains a NUL byte +// this function panics instead of returning an error. +// +// Deprecated: Use SlicePtrFromStrings instead. func StringSlicePtr(ss []string) []*byte { bb := make([]*byte, len(ss)+1) for i := 0; i < len(ss); i++ { @@ -112,7 +135,7 @@ func StringSlicePtr(ss []string) []*byte { } // SlicePtrFromStrings converts a slice of strings to a slice of -// pointers to NUL-terminated byte slices. If any string contains +// pointers to NUL-terminated byte arrays. If any string contains // a NUL byte, it returns (nil, EINVAL). func SlicePtrFromStrings(ss []string) ([]*byte, error) { var err error diff --git a/libgo/go/syscall/exec_unix_test.go b/libgo/go/syscall/exec_unix_test.go new file mode 100644 index 00000000000..9bb95c0f395 --- /dev/null +++ b/libgo/go/syscall/exec_unix_test.go @@ -0,0 +1,215 @@ +// Copyright 2015 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 freebsd linux netbsd openbsd solaris + +package syscall_test + +import ( + "internal/testenv" + "io" + "os" + "os/exec" + "os/signal" + "syscall" + "testing" + "unsafe" +) + +type command struct { + pipe io.WriteCloser + proc *exec.Cmd + test *testing.T +} + +func (c *command) Info() (pid, pgrp int) { + pid = c.proc.Process.Pid + + pgrp, err := syscall.Getpgid(pid) + if err != nil { + c.test.Fatal(err) + } + + return +} + +func (c *command) Start() { + if err := c.proc.Start(); err != nil { + c.test.Fatal(err) + } +} + +func (c *command) Stop() { + c.pipe.Close() + if err := c.proc.Wait(); err != nil { + c.test.Fatal(err) + } +} + +func create(t *testing.T) *command { + testenv.MustHaveExec(t) + + proc := exec.Command("cat") + stdin, err := proc.StdinPipe() + if err != nil { + t.Fatal(err) + } + + return &command{stdin, proc, t} +} + +func parent() (pid, pgrp int) { + return syscall.Getpid(), syscall.Getpgrp() +} + +func TestZeroSysProcAttr(t *testing.T) { + ppid, ppgrp := parent() + + cmd := create(t) + + cmd.Start() + defer cmd.Stop() + + cpid, cpgrp := cmd.Info() + + if cpid == ppid { + t.Fatalf("Parent and child have the same process ID") + } + + if cpgrp != ppgrp { + t.Fatalf("Child is not in parent's process group") + } +} + +func TestSetpgid(t *testing.T) { + ppid, ppgrp := parent() + + cmd := create(t) + + cmd.proc.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + cmd.Start() + defer cmd.Stop() + + cpid, cpgrp := cmd.Info() + + if cpid == ppid { + t.Fatalf("Parent and child have the same process ID") + } + + if cpgrp == ppgrp { + t.Fatalf("Parent and child are in the same process group") + } + + if cpid != cpgrp { + t.Fatalf("Child's process group is not the child's process ID") + } +} + +func TestPgid(t *testing.T) { + ppid, ppgrp := parent() + + cmd1 := create(t) + + cmd1.proc.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + cmd1.Start() + defer cmd1.Stop() + + cpid1, cpgrp1 := cmd1.Info() + + if cpid1 == ppid { + t.Fatalf("Parent and child 1 have the same process ID") + } + + if cpgrp1 == ppgrp { + t.Fatalf("Parent and child 1 are in the same process group") + } + + if cpid1 != cpgrp1 { + t.Fatalf("Child 1's process group is not its process ID") + } + + cmd2 := create(t) + + cmd2.proc.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + Pgid: cpgrp1, + } + cmd2.Start() + defer cmd2.Stop() + + cpid2, cpgrp2 := cmd2.Info() + + if cpid2 == ppid { + t.Fatalf("Parent and child 2 have the same process ID") + } + + if cpgrp2 == ppgrp { + t.Fatalf("Parent and child 2 are in the same process group") + } + + if cpid2 == cpgrp2 { + t.Fatalf("Child 2's process group is its process ID") + } + + if cpid1 == cpid2 { + t.Fatalf("Child 1 and 2 have the same process ID") + } + + if cpgrp1 != cpgrp2 { + t.Fatalf("Child 1 and 2 are not in the same process group") + } +} + +func TestForeground(t *testing.T) { + signal.Ignore(syscall.SIGTTIN, syscall.SIGTTOU) + + tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) + if err != nil { + t.Skipf("Can't test Foreground. Couldn't open /dev/tty: %s", err) + } + + fpgrp := 0 + + errno := syscall.Ioctl(tty.Fd(), syscall.TIOCGPGRP, uintptr(unsafe.Pointer(&fpgrp))) + if errno != 0 { + t.Fatalf("TIOCGPGRP failed with error code: %s", errno) + } + + if fpgrp == 0 { + t.Fatalf("Foreground process group is zero") + } + + ppid, ppgrp := parent() + + cmd := create(t) + + cmd.proc.SysProcAttr = &syscall.SysProcAttr{ + Ctty: int(tty.Fd()), + Foreground: true, + } + cmd.Start() + + cpid, cpgrp := cmd.Info() + + if cpid == ppid { + t.Fatalf("Parent and child have the same process ID") + } + + if cpgrp == ppgrp { + t.Fatalf("Parent and child are in the same process group") + } + + if cpid != cpgrp { + t.Fatalf("Child's process group is not the child's process ID") + } + + cmd.Stop() + + errno = syscall.Ioctl(tty.Fd(), syscall.TIOCSPGRP, uintptr(unsafe.Pointer(&fpgrp))) + if errno != 0 { + t.Fatalf("TIOCSPGRP failed with error code: %s", errno) + } + + signal.Reset() +} diff --git a/libgo/go/syscall/exec_windows.go b/libgo/go/syscall/exec_windows.go index 936aeb577bc..5a01843d2be 100644 --- a/libgo/go/syscall/exec_windows.go +++ b/libgo/go/syscall/exec_windows.go @@ -135,23 +135,17 @@ func FullPath(name string) (path string, err error) { if err != nil { return "", err } - buf := make([]uint16, 100) - n, err := GetFullPathName(p, uint32(len(buf)), &buf[0], nil) - if err != nil { - return "", err - } - if n > uint32(len(buf)) { - // Windows is asking for bigger buffer. - buf = make([]uint16, n) + n := uint32(100) + for { + buf := make([]uint16, n) n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil) if err != nil { return "", err } - if n > uint32(len(buf)) { - return "", EINVAL + if n <= uint32(len(buf)) { + return UTF16ToString(buf[:n]), nil } } - return UTF16ToString(buf[:n]), nil } func isSlash(c uint8) bool { @@ -250,6 +244,9 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle if len(attr.Files) > 3 { return 0, 0, EWINDOWS } + if len(attr.Files) < 3 { + return 0, 0, EINVAL + } if len(attr.Dir) != 0 { // StartProcess assumes that argv0 is relative to attr.Dir, diff --git a/libgo/go/syscall/export_unix_test.go b/libgo/go/syscall/export_unix_test.go new file mode 100644 index 00000000000..b41fe2f86b3 --- /dev/null +++ b/libgo/go/syscall/export_unix_test.go @@ -0,0 +1,12 @@ +// Copyright 2015 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 freebsd linux netbsd openbsd + +package syscall + +func Ioctl(fd, req, arg uintptr) (err Errno) { + _, _, err = Syscall(SYS_IOCTL, fd, req, arg) + return err +} diff --git a/libgo/go/syscall/pwd_plan9.go b/libgo/go/syscall/pwd_plan9.go new file mode 100644 index 00000000000..12486135f0d --- /dev/null +++ b/libgo/go/syscall/pwd_plan9.go @@ -0,0 +1,83 @@ +// Copyright 2015 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. + +// The working directory in Plan 9 is effectively per P, so different +// goroutines and even the same goroutine as it's rescheduled on +// different Ps can see different working directories. +// +// Instead, track a Go process-wide intent of the current working directory, +// and switch to it at important points. + +package syscall + +import "sync" + +var ( + wdmu sync.Mutex // guards following + wdSet bool + wdStr string +) + +func Fixwd() { + wdmu.Lock() + defer wdmu.Unlock() + fixwdLocked() +} + +func fixwdLocked() { + if !wdSet { + return + } + // always call chdir when getwd returns an error + wd, _ := getwd() + if wd == wdStr { + return + } + if err := chdir(wdStr); err != nil { + return + } +} + +// goroutine-specific getwd +func getwd() (wd string, err error) { + fd, err := open(".", O_RDONLY) + if err != nil { + return "", err + } + defer Close(fd) + return Fd2path(fd) +} + +func Getwd() (wd string, err error) { + wdmu.Lock() + defer wdmu.Unlock() + + if wdSet { + return wdStr, nil + } + wd, err = getwd() + if err != nil { + return + } + wdSet = true + wdStr = wd + return wd, nil +} + +func Chdir(path string) error { + wdmu.Lock() + defer wdmu.Unlock() + + if err := chdir(path); err != nil { + return err + } + + wd, err := getwd() + if err != nil { + return err + } + wdSet = true + wdStr = wd + return nil +} diff --git a/libgo/go/syscall/route_bsd.go b/libgo/go/syscall/route_bsd.go index 1dabe42531b..c62fdc3c81d 100644 --- a/libgo/go/syscall/route_bsd.go +++ b/libgo/go/syscall/route_bsd.go @@ -4,23 +4,37 @@ // +build darwin dragonfly freebsd netbsd openbsd -// Routing sockets and messages - package syscall -import "unsafe" +import ( + "runtime" + "unsafe" +) + +var ( + freebsdConfArch string // "machine $arch" line in kern.conftxt on freebsd + minRoutingSockaddrLen = rsaAlignOf(0) +) // Round the length of a raw sockaddr up to align it properly. func rsaAlignOf(salen int) int { salign := sizeofPtr - // NOTE: It seems like 64-bit Darwin kernel still requires - // 32-bit aligned access to BSD subsystem. Also NetBSD 6 - // kernel and beyond require 64-bit aligned access to routing - // facilities. if darwin64Bit { + // Darwin kernels require 32-bit aligned access to + // routing facilities. salign = 4 } else if netbsd32Bit { + // NetBSD 6 and beyond kernels require 64-bit aligned + // access to routing facilities. salign = 8 + } else if runtime.GOOS == "freebsd" { + // In the case of kern.supported_archs="amd64 i386", + // we need to know the underlying kernel's + // architecture because the alignment for routing + // facilities are set at the build time of the kernel. + if freebsdConfArch == "amd64" { + salign = 8 + } } if salen == 0 { return salign @@ -28,6 +42,134 @@ func rsaAlignOf(salen int) int { return (salen + salign - 1) & ^(salign - 1) } +// parseSockaddrLink parses b as a datalink socket address. +func parseSockaddrLink(b []byte) (*SockaddrDatalink, error) { + sa, _, err := parseLinkLayerAddr(b[4:]) + if err != nil { + return nil, err + } + rsa := (*RawSockaddrDatalink)(unsafe.Pointer(&b[0])) + sa.Len = rsa.Len + sa.Family = rsa.Family + sa.Index = rsa.Index + return sa, nil +} + +// parseLinkLayerAddr parses b as a datalink socket address in +// conventional BSD kernel form. +func parseLinkLayerAddr(b []byte) (*SockaddrDatalink, int, error) { + // The encoding looks like the following: + // +----------------------------+ + // | Type (1 octet) | + // +----------------------------+ + // | Name length (1 octet) | + // +----------------------------+ + // | Address length (1 octet) | + // +----------------------------+ + // | Selector length (1 octet) | + // +----------------------------+ + // | Data (variable) | + // +----------------------------+ + type linkLayerAddr struct { + Type byte + Nlen byte + Alen byte + Slen byte + } + lla := (*linkLayerAddr)(unsafe.Pointer(&b[0])) + l := rsaAlignOf(int(4 + lla.Nlen + lla.Alen + lla.Slen)) + if len(b) < l { + return nil, 0, EINVAL + } + b = b[4:] + sa := &SockaddrDatalink{Type: lla.Type, Nlen: lla.Nlen, Alen: lla.Alen, Slen: lla.Slen} + for i := 0; len(sa.Data) > i && i < int(lla.Nlen+lla.Alen+lla.Slen); i++ { + sa.Data[i] = int8(b[i]) + } + return sa, l, nil +} + +// parseSockaddrInet parses b as an internet socket address. +func parseSockaddrInet(b []byte, family byte) (Sockaddr, error) { + switch family { + case AF_INET: + if len(b) < SizeofSockaddrInet4 { + return nil, EINVAL + } + rsa := (*RawSockaddrAny)(unsafe.Pointer(&b[0])) + return anyToSockaddr(rsa) + case AF_INET6: + if len(b) < SizeofSockaddrInet6 { + return nil, EINVAL + } + rsa := (*RawSockaddrAny)(unsafe.Pointer(&b[0])) + return anyToSockaddr(rsa) + default: + return nil, EINVAL + } +} + +const ( + offsetofInet4 = int(unsafe.Offsetof(RawSockaddrInet4{}.Addr)) + offsetofInet6 = int(unsafe.Offsetof(RawSockaddrInet6{}.Addr)) +) + +// parseNetworkLayerAddr parses b as an internet socket address in +// conventional BSD kernel form. +func parseNetworkLayerAddr(b []byte, family byte) (Sockaddr, error) { + // The encoding looks similar to the NLRI encoding. + // +----------------------------+ + // | Length (1 octet) | + // +----------------------------+ + // | Address prefix (variable) | + // +----------------------------+ + // + // The differences between the kernel form and the NLRI + // encoding are: + // + // - The length field of the kernel form indicates the prefix + // length in bytes, not in bits + // + // - In the kernel form, zero value of the length field + // doesn't mean 0.0.0.0/0 or ::/0 + // + // - The kernel form appends leading bytes to the prefix field + // to make the <length, prefix> tuple to be conformed with + // the routing messeage boundary + l := int(rsaAlignOf(int(b[0]))) + if len(b) < l { + return nil, EINVAL + } + // Don't reorder case expressions. + // The case expressions for IPv6 must come first. + switch { + case b[0] == SizeofSockaddrInet6: + sa := &SockaddrInet6{} + copy(sa.Addr[:], b[offsetofInet6:]) + return sa, nil + case family == AF_INET6: + sa := &SockaddrInet6{} + if l-1 < offsetofInet6 { + copy(sa.Addr[:], b[1:l]) + } else { + copy(sa.Addr[:], b[l-offsetofInet6:l]) + } + return sa, nil + case b[0] == SizeofSockaddrInet4: + sa := &SockaddrInet4{} + copy(sa.Addr[:], b[offsetofInet4:]) + return sa, nil + default: // an old fashion, AF_UNSPEC or unknown means AF_INET + sa := &SockaddrInet4{} + if l-1 < offsetofInet4 { + copy(sa.Addr[:], b[1:l]) + } else { + copy(sa.Addr[:], b[l-offsetofInet4:l]) + } + return sa, nil + } +} + // RouteRIB returns routing information base, as known as RIB, // which consists of network facility information, states and // parameters. @@ -50,7 +192,7 @@ func RouteRIB(facility, param int) ([]byte, error) { // RoutingMessage represents a routing message. type RoutingMessage interface { - sockaddr() []Sockaddr + sockaddr() ([]Sockaddr, error) } const anyMessageLen = int(unsafe.Sizeof(anyMessage{})) @@ -68,50 +210,41 @@ type RouteMessage struct { Data []byte } -const rtaRtMask = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_GENMASK - -func (m *RouteMessage) sockaddr() []Sockaddr { - var ( - af int - sas [4]Sockaddr - ) +func (m *RouteMessage) sockaddr() ([]Sockaddr, error) { + var sas [RTAX_MAX]Sockaddr b := m.Data[:] - for i := uint(0); i < RTAX_MAX; i++ { - if m.Header.Addrs&rtaRtMask&(1<<i) == 0 { + family := uint8(AF_UNSPEC) + for i := uint(0); i < RTAX_MAX && len(b) >= minRoutingSockaddrLen; i++ { + if m.Header.Addrs&(1<<i) == 0 { continue } rsa := (*RawSockaddr)(unsafe.Pointer(&b[0])) - switch i { - case RTAX_DST, RTAX_GATEWAY: - sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) + switch rsa.Family { + case AF_LINK: + sa, err := parseSockaddrLink(b) if err != nil { - return nil + return nil, err } - if i == RTAX_DST { - af = int(rsa.Family) + sas[i] = sa + b = b[rsaAlignOf(int(rsa.Len)):] + case AF_INET, AF_INET6: + sa, err := parseSockaddrInet(b, rsa.Family) + if err != nil { + return nil, err } sas[i] = sa - case RTAX_NETMASK, RTAX_GENMASK: - switch af { - case AF_INET: - rsa4 := (*RawSockaddrInet4)(unsafe.Pointer(&b[0])) - sa := new(SockaddrInet4) - for j := 0; rsa4.Len > 0 && j < int(rsa4.Len)-int(unsafe.Offsetof(rsa4.Addr)); j++ { - sa.Addr[j] = rsa4.Addr[j] - } - sas[i] = sa - case AF_INET6: - rsa6 := (*RawSockaddrInet6)(unsafe.Pointer(&b[0])) - sa := new(SockaddrInet6) - for j := 0; rsa6.Len > 0 && j < int(rsa6.Len)-int(unsafe.Offsetof(rsa6.Addr)); j++ { - sa.Addr[j] = rsa6.Addr[j] - } - sas[i] = sa + b = b[rsaAlignOf(int(rsa.Len)):] + family = rsa.Family + default: + sa, err := parseNetworkLayerAddr(b, family) + if err != nil { + return nil, err } + sas[i] = sa + b = b[rsaAlignOf(int(b[0])):] } - b = b[rsaAlignOf(int(rsa.Len)):] } - return sas[:] + return sas[:], nil } // InterfaceMessage represents a routing message containing @@ -121,15 +254,17 @@ type InterfaceMessage struct { Data []byte } -func (m *InterfaceMessage) sockaddr() (sas []Sockaddr) { +func (m *InterfaceMessage) sockaddr() ([]Sockaddr, error) { + var sas [RTAX_MAX]Sockaddr if m.Header.Addrs&RTA_IFP == 0 { - return nil + return nil, nil } - sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(&m.Data[0]))) + sa, err := parseSockaddrLink(m.Data[:]) if err != nil { - return nil + return nil, err } - return append(sas, sa) + sas[RTAX_IFP] = sa + return sas[:], nil } // InterfaceAddrMessage represents a routing message containing @@ -139,79 +274,63 @@ type InterfaceAddrMessage struct { Data []byte } -const rtaIfaMask = RTA_IFA | RTA_NETMASK | RTA_BRD - -func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) { - if m.Header.Addrs&rtaIfaMask == 0 { - return nil - } +func (m *InterfaceAddrMessage) sockaddr() ([]Sockaddr, error) { + var sas [RTAX_MAX]Sockaddr b := m.Data[:] - // We still see AF_UNSPEC in socket addresses on some - // platforms. To identify each address family correctly, we - // will use the address family of RTAX_NETMASK as a preferred - // one on the 32-bit NetBSD kernel, also use the length of - // RTAX_NETMASK socket address on the FreeBSD kernel. - preferredFamily := uint8(AF_UNSPEC) - for i := uint(0); i < RTAX_MAX; i++ { + family := uint8(AF_UNSPEC) + for i := uint(0); i < RTAX_MAX && len(b) >= minRoutingSockaddrLen; i++ { if m.Header.Addrs&(1<<i) == 0 { continue } rsa := (*RawSockaddr)(unsafe.Pointer(&b[0])) - switch i { - case RTAX_IFA: - if rsa.Family == AF_UNSPEC { - rsa.Family = preferredFamily - } - sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) + switch rsa.Family { + case AF_LINK: + sa, err := parseSockaddrLink(b) if err != nil { - return nil + return nil, err } - sas = append(sas, sa) - case RTAX_NETMASK: - switch rsa.Family { - case AF_UNSPEC: - switch rsa.Len { - case SizeofSockaddrInet4: - rsa.Family = AF_INET - case SizeofSockaddrInet6: - rsa.Family = AF_INET6 - default: - rsa.Family = AF_INET // an old fashion, AF_UNSPEC means AF_INET - } - case AF_INET, AF_INET6: - preferredFamily = rsa.Family - default: - return nil + sas[i] = sa + b = b[rsaAlignOf(int(rsa.Len)):] + case AF_INET, AF_INET6: + sa, err := parseSockaddrInet(b, rsa.Family) + if err != nil { + return nil, err } - sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) + sas[i] = sa + b = b[rsaAlignOf(int(rsa.Len)):] + family = rsa.Family + default: + sa, err := parseNetworkLayerAddr(b, family) if err != nil { - return nil + return nil, err } - sas = append(sas, sa) - case RTAX_BRD: - // nothing to do + sas[i] = sa + b = b[rsaAlignOf(int(b[0])):] } - b = b[rsaAlignOf(int(rsa.Len)):] } - return sas + return sas[:], nil } // ParseRoutingMessage parses b as routing messages and returns the // slice containing the RoutingMessage interfaces. func ParseRoutingMessage(b []byte) (msgs []RoutingMessage, err error) { - msgCount := 0 + nmsgs, nskips := 0, 0 for len(b) >= anyMessageLen { - msgCount++ + nmsgs++ any := (*anyMessage)(unsafe.Pointer(&b[0])) if any.Version != RTM_VERSION { b = b[any.Msglen:] continue } - msgs = append(msgs, any.toRoutingMessage(b)) + if m := any.toRoutingMessage(b); m == nil { + nskips++ + } else { + msgs = append(msgs, m) + } b = b[any.Msglen:] } // We failed to parse any of the messages - version mismatch? - if msgCount > 0 && len(msgs) == 0 { + if nmsgs != len(msgs)+nskips { return nil, EINVAL } return msgs, nil @@ -219,6 +338,10 @@ func ParseRoutingMessage(b []byte) (msgs []RoutingMessage, err error) { // ParseRoutingMessage parses msg's payload as raw sockaddrs and // returns the slice containing the Sockaddr interfaces. -func ParseRoutingSockaddr(msg RoutingMessage) (sas []Sockaddr, err error) { - return append(sas, msg.sockaddr()...), nil +func ParseRoutingSockaddr(msg RoutingMessage) ([]Sockaddr, error) { + sas, err := msg.sockaddr() + if err != nil { + return nil, err + } + return sas, nil } diff --git a/libgo/go/syscall/route_bsd_test.go b/libgo/go/syscall/route_bsd_test.go new file mode 100644 index 00000000000..8617663d43a --- /dev/null +++ b/libgo/go/syscall/route_bsd_test.go @@ -0,0 +1,225 @@ +// Copyright 2015 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 freebsd netbsd openbsd + +package syscall_test + +import ( + "fmt" + "net" + "os" + "syscall" + "testing" + "time" +) + +func TestRouteRIB(t *testing.T) { + for _, facility := range []int{syscall.NET_RT_DUMP, syscall.NET_RT_IFLIST} { + for _, param := range []int{syscall.AF_UNSPEC, syscall.AF_INET, syscall.AF_INET6} { + var err error + var b []byte + // The VM allocator wrapper functions can + // return ENOMEM easily. + for i := 0; i < 3; i++ { + b, err = syscall.RouteRIB(facility, param) + if err != nil { + time.Sleep(5 * time.Millisecond) + continue + } + break + } + if err != nil { + t.Error(facility, param, err) + continue + } + msgs, err := syscall.ParseRoutingMessage(b) + if err != nil { + t.Error(facility, param, err) + continue + } + var ipv4loopback, ipv6loopback bool + for _, m := range msgs { + flags, err := parseRoutingMessageHeader(m) + if err != nil { + t.Error(err) + continue + } + sas, err := parseRoutingSockaddrs(m) + if err != nil { + t.Error(err) + continue + } + if flags&(syscall.RTA_DST|syscall.RTA_IFA) != 0 { + sa := sas[syscall.RTAX_DST] + if sa == nil { + sa = sas[syscall.RTAX_IFA] + } + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + if net.IP(sa.Addr[:]).IsLoopback() { + ipv4loopback = true + } + case *syscall.SockaddrInet6: + if net.IP(sa.Addr[:]).IsLoopback() { + ipv6loopback = true + } + } + } + t.Log(facility, param, flags, sockaddrs(sas)) + } + if param == syscall.AF_UNSPEC && len(msgs) > 0 && !ipv4loopback && !ipv6loopback { + t.Errorf("no loopback facility found: ipv4/ipv6=%v/%v, %v", ipv4loopback, ipv6loopback, len(msgs)) + continue + } + } + } +} + +func TestRouteMonitor(t *testing.T) { + if testing.Short() || os.Getuid() != 0 { + t.Skip("must be root") + } + + s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC) + if err != nil { + t.Fatal(err) + } + defer syscall.Close(s) + + tmo := time.After(30 * time.Second) + go func() { + b := make([]byte, os.Getpagesize()) + for { + n, err := syscall.Read(s, b) + if err != nil { + return + } + msgs, err := syscall.ParseRoutingMessage(b[:n]) + if err != nil { + t.Error(err) + return + } + for _, m := range msgs { + flags, err := parseRoutingMessageHeader(m) + if err != nil { + t.Error(err) + continue + } + sas, err := parseRoutingSockaddrs(m) + if err != nil { + t.Error(err) + continue + } + t.Log(flags, sockaddrs(sas)) + } + } + }() + <-tmo +} + +type addrFamily byte + +func (f addrFamily) String() string { + switch f { + case syscall.AF_UNSPEC: + return "unspec" + case syscall.AF_LINK: + return "link" + case syscall.AF_INET: + return "inet4" + case syscall.AF_INET6: + return "inet6" + default: + return fmt.Sprintf("unknown %d", f) + } +} + +type addrFlags uint32 + +var addrFlagNames = [...]string{ + "dst", + "gateway", + "netmask", + "genmask", + "ifp", + "ifa", + "author", + "brd", + "mpls1,tag,src", // sockaddr_mpls=dragonfly,netbsd, sockaddr_in/in6=openbsd + "mpls2,srcmask", // sockaddr_mpls=dragonfly, sockaddr_in/in6=openbsd + "mpls3,label", // sockaddr_mpls=dragonfly, sockaddr_rtlabel=openbsd +} + +func (f addrFlags) String() string { + var s string + for i, name := range addrFlagNames { + if f&(1<<uint(i)) != 0 { + if s != "" { + s += "|" + } + s += name + } + } + if s == "" { + return "<nil>" + } + return s +} + +type sockaddrs []syscall.Sockaddr + +func (sas sockaddrs) String() string { + var s string + for _, sa := range sas { + if sa == nil { + continue + } + if len(s) > 0 { + s += " " + } + switch sa := sa.(type) { + case *syscall.SockaddrDatalink: + s += fmt.Sprintf("[%v/%v/%v t/n/a/s=%v/%v/%v/%v]", sa.Len, addrFamily(sa.Family), sa.Index, sa.Type, sa.Nlen, sa.Alen, sa.Slen) + case *syscall.SockaddrInet4: + s += fmt.Sprintf("%v", net.IP(sa.Addr[:]).To4()) + case *syscall.SockaddrInet6: + s += fmt.Sprintf("%v", net.IP(sa.Addr[:]).To16()) + } + } + if s == "" { + return "<nil>" + } + return s +} + +func (sas sockaddrs) match(flags addrFlags) error { + var f addrFlags + family := syscall.AF_UNSPEC + for i := range sas { + if sas[i] != nil { + f |= 1 << uint(i) + } + switch sas[i].(type) { + case *syscall.SockaddrInet4: + if family == syscall.AF_UNSPEC { + family = syscall.AF_INET + } + if family != syscall.AF_INET { + return fmt.Errorf("got %v; want %v", sockaddrs(sas), family) + } + case *syscall.SockaddrInet6: + if family == syscall.AF_UNSPEC { + family = syscall.AF_INET6 + } + if family != syscall.AF_INET6 { + return fmt.Errorf("got %v; want %v", sockaddrs(sas), family) + } + } + } + if f != flags { + return fmt.Errorf("got %v; want %v", f, flags) + } + return nil +} diff --git a/libgo/go/syscall/route_darwin.go b/libgo/go/syscall/route_darwin.go index ad279072305..89bca12f3e1 100644 --- a/libgo/go/syscall/route_darwin.go +++ b/libgo/go/syscall/route_darwin.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Routing sockets and messages for Darwin - package syscall import "unsafe" @@ -33,29 +31,37 @@ type InterfaceMulticastAddrMessage struct { Data []byte } -const rtaIfmaMask = RTA_GATEWAY | RTA_IFP | RTA_IFA - -func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) { - if m.Header.Addrs&rtaIfmaMask == 0 { - return nil - } +func (m *InterfaceMulticastAddrMessage) sockaddr() ([]Sockaddr, error) { + var sas [RTAX_MAX]Sockaddr b := m.Data[:] - for i := uint(0); i < RTAX_MAX; i++ { - if m.Header.Addrs&rtaIfmaMask&(1<<i) == 0 { + for i := uint(0); i < RTAX_MAX && len(b) >= minRoutingSockaddrLen; i++ { + if m.Header.Addrs&(1<<i) == 0 { continue } rsa := (*RawSockaddr)(unsafe.Pointer(&b[0])) - switch i { - case RTAX_IFA: - sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) - if e != nil { - return nil + switch rsa.Family { + case AF_LINK: + sa, err := parseSockaddrLink(b) + if err != nil { + return nil, err + } + sas[i] = sa + b = b[rsaAlignOf(int(rsa.Len)):] + case AF_INET, AF_INET6: + sa, err := parseSockaddrInet(b, rsa.Family) + if err != nil { + return nil, err + } + sas[i] = sa + b = b[rsaAlignOf(int(rsa.Len)):] + default: + sa, l, err := parseLinkLayerAddr(b) + if err != nil { + return nil, err } - sas = append(sas, sa) - case RTAX_GATEWAY, RTAX_IFP: - // nothing to do + sas[i] = sa + b = b[l:] } - b = b[rsaAlignOf(int(rsa.Len)):] } - return sas + return sas[:], nil } diff --git a/libgo/go/syscall/route_dragonfly.go b/libgo/go/syscall/route_dragonfly.go index 79190d2b01b..5226f7f2e4c 100644 --- a/libgo/go/syscall/route_dragonfly.go +++ b/libgo/go/syscall/route_dragonfly.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Routing sockets and messages for Dragonfly - package syscall import "unsafe" @@ -12,6 +10,8 @@ func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage { switch any.Type { case RTM_ADD, RTM_DELETE, RTM_CHANGE, RTM_GET, RTM_LOSING, RTM_REDIRECT, RTM_MISS, RTM_LOCK, RTM_RESOLVE: p := (*RouteMessage)(unsafe.Pointer(any)) + // We don't support sockaddr_mpls for now. + p.Header.Addrs &= RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_GENMASK | RTA_IFA | RTA_IFP | RTA_BRD | RTA_AUTHOR return &RouteMessage{Header: p.Header, Data: b[SizeofRtMsghdr:any.Msglen]} case RTM_IFINFO: p := (*InterfaceMessage)(unsafe.Pointer(any)) @@ -35,7 +35,7 @@ type InterfaceAnnounceMessage struct { Header IfAnnounceMsghdr } -func (m *InterfaceAnnounceMessage) sockaddr() (sas []Sockaddr) { return nil } +func (m *InterfaceAnnounceMessage) sockaddr() ([]Sockaddr, error) { return nil, nil } // InterfaceMulticastAddrMessage represents a routing message // containing network interface address entries. @@ -44,29 +44,37 @@ type InterfaceMulticastAddrMessage struct { Data []byte } -const rtaIfmaMask = RTA_GATEWAY | RTA_IFP | RTA_IFA - -func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) { - if m.Header.Addrs&rtaIfmaMask == 0 { - return nil - } +func (m *InterfaceMulticastAddrMessage) sockaddr() ([]Sockaddr, error) { + var sas [RTAX_MAX]Sockaddr b := m.Data[:] - for i := uint(0); i < RTAX_MAX; i++ { - if m.Header.Addrs&rtaIfmaMask&(1<<i) == 0 { + for i := uint(0); i < RTAX_MAX && len(b) >= minRoutingSockaddrLen; i++ { + if m.Header.Addrs&(1<<i) == 0 { continue } rsa := (*RawSockaddr)(unsafe.Pointer(&b[0])) - switch i { - case RTAX_IFA: - sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) - if e != nil { - return nil + switch rsa.Family { + case AF_LINK: + sa, err := parseSockaddrLink(b) + if err != nil { + return nil, err + } + sas[i] = sa + b = b[rsaAlignOf(int(rsa.Len)):] + case AF_INET, AF_INET6: + sa, err := parseSockaddrInet(b, rsa.Family) + if err != nil { + return nil, err + } + sas[i] = sa + b = b[rsaAlignOf(int(rsa.Len)):] + default: + sa, l, err := parseLinkLayerAddr(b) + if err != nil { + return nil, err } - sas = append(sas, sa) - case RTAX_GATEWAY, RTAX_IFP: - // nothing to do + sas[i] = sa + b = b[l:] } - b = b[rsaAlignOf(int(rsa.Len)):] } - return sas + return sas[:], nil } diff --git a/libgo/go/syscall/route_freebsd.go b/libgo/go/syscall/route_freebsd.go index 15897b1aca9..0e181038555 100644 --- a/libgo/go/syscall/route_freebsd.go +++ b/libgo/go/syscall/route_freebsd.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Routing sockets and messages for FreeBSD - package syscall import "unsafe" @@ -13,13 +11,31 @@ var freebsdVersion uint32 func init() { freebsdVersion, _ = SysctlUint32("kern.osreldate") + conf, _ := Sysctl("kern.conftxt") + for i, j := 0, 0; j < len(conf); j++ { + if conf[j] != '\n' { + continue + } + s := conf[i:j] + i = j + 1 + if len(s) > len("machine") && s[:len("machine")] == "machine" { + s = s[len("machine"):] + for k := 0; k < len(s); k++ { + if s[k] == ' ' || s[k] == '\t' { + s = s[1:] + } + break + } + freebsdConfArch = s + break + } + } } func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage { switch any.Type { case RTM_ADD, RTM_DELETE, RTM_CHANGE, RTM_GET, RTM_LOSING, RTM_REDIRECT, RTM_MISS, RTM_LOCK, RTM_RESOLVE: - p := (*RouteMessage)(unsafe.Pointer(any)) - return &RouteMessage{Header: p.Header, Data: b[SizeofRtMsghdr:any.Msglen]} + return any.parseRouteMessage(b) case RTM_IFINFO: return any.parseInterfaceMessage(b) case RTM_IFANNOUNCE: @@ -41,7 +57,7 @@ type InterfaceAnnounceMessage struct { Header IfAnnounceMsghdr } -func (m *InterfaceAnnounceMessage) sockaddr() (sas []Sockaddr) { return nil } +func (m *InterfaceAnnounceMessage) sockaddr() ([]Sockaddr, error) { return nil, nil } // InterfaceMulticastAddrMessage represents a routing message // containing network interface address entries. @@ -50,29 +66,37 @@ type InterfaceMulticastAddrMessage struct { Data []byte } -const rtaIfmaMask = RTA_GATEWAY | RTA_IFP | RTA_IFA - -func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) { - if m.Header.Addrs&rtaIfmaMask == 0 { - return nil - } +func (m *InterfaceMulticastAddrMessage) sockaddr() ([]Sockaddr, error) { + var sas [RTAX_MAX]Sockaddr b := m.Data[:] - for i := uint(0); i < RTAX_MAX; i++ { - if m.Header.Addrs&rtaIfmaMask&(1<<i) == 0 { + for i := uint(0); i < RTAX_MAX && len(b) >= minRoutingSockaddrLen; i++ { + if m.Header.Addrs&(1<<i) == 0 { continue } rsa := (*RawSockaddr)(unsafe.Pointer(&b[0])) - switch i { - case RTAX_IFA: - sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) - if e != nil { - return nil + switch rsa.Family { + case AF_LINK: + sa, err := parseSockaddrLink(b) + if err != nil { + return nil, err + } + sas[i] = sa + b = b[rsaAlignOf(int(rsa.Len)):] + case AF_INET, AF_INET6: + sa, err := parseSockaddrInet(b, rsa.Family) + if err != nil { + return nil, err + } + sas[i] = sa + b = b[rsaAlignOf(int(rsa.Len)):] + default: + sa, l, err := parseLinkLayerAddr(b) + if err != nil { + return nil, err } - sas = append(sas, sa) - case RTAX_GATEWAY, RTAX_IFP: - // nothing to do + sas[i] = sa + b = b[l:] } - b = b[rsaAlignOf(int(rsa.Len)):] } - return sas + return sas[:], nil } diff --git a/libgo/go/syscall/route_freebsd_32bit.go b/libgo/go/syscall/route_freebsd_32bit.go index 93efdddb3bb..5c10b05e24d 100644 --- a/libgo/go/syscall/route_freebsd_32bit.go +++ b/libgo/go/syscall/route_freebsd_32bit.go @@ -8,6 +8,15 @@ package syscall import "unsafe" +func (any *anyMessage) parseRouteMessage(b []byte) *RouteMessage { + p := (*RouteMessage)(unsafe.Pointer(any)) + off := int(unsafe.Offsetof(p.Header.Rmx)) + SizeofRtMetrics + if freebsdConfArch == "amd64" { + off += SizeofRtMetrics // rt_metrics on amd64 is simply doubled + } + return &RouteMessage{Header: p.Header, Data: b[rsaAlignOf(off):any.Msglen]} +} + func (any *anyMessage) parseInterfaceMessage(b []byte) *InterfaceMessage { p := (*InterfaceMessage)(unsafe.Pointer(any)) // FreeBSD 10 and beyond have a restructured mbuf @@ -18,7 +27,7 @@ func (any *anyMessage) parseInterfaceMessage(b []byte) *InterfaceMessage { p.Header.Data.Hwassist = uint32(m.Data.Hwassist) p.Header.Data.Epoch = m.Data.Epoch p.Header.Data.Lastchange = m.Data.Lastchange - return &InterfaceMessage{Header: p.Header, Data: b[sizeofIfMsghdr:any.Msglen]} + return &InterfaceMessage{Header: p.Header, Data: b[int(unsafe.Offsetof(p.Header.Data))+int(p.Header.Data.Datalen) : any.Msglen]} } - return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]} + return &InterfaceMessage{Header: p.Header, Data: b[int(unsafe.Offsetof(p.Header.Data))+int(p.Header.Data.Datalen) : any.Msglen]} } diff --git a/libgo/go/syscall/route_freebsd_64bit.go b/libgo/go/syscall/route_freebsd_64bit.go index 9377f2fedca..728837ebb5a 100644 --- a/libgo/go/syscall/route_freebsd_64bit.go +++ b/libgo/go/syscall/route_freebsd_64bit.go @@ -8,7 +8,12 @@ package syscall import "unsafe" +func (any *anyMessage) parseRouteMessage(b []byte) *RouteMessage { + p := (*RouteMessage)(unsafe.Pointer(any)) + return &RouteMessage{Header: p.Header, Data: b[rsaAlignOf(int(unsafe.Offsetof(p.Header.Rmx))+SizeofRtMetrics):any.Msglen]} +} + func (any *anyMessage) parseInterfaceMessage(b []byte) *InterfaceMessage { p := (*InterfaceMessage)(unsafe.Pointer(any)) - return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]} + return &InterfaceMessage{Header: p.Header, Data: b[int(unsafe.Offsetof(p.Header.Data))+int(p.Header.Data.Datalen) : any.Msglen]} } diff --git a/libgo/go/syscall/route_ifma_test.go b/libgo/go/syscall/route_ifma_test.go new file mode 100644 index 00000000000..af2b67dc244 --- /dev/null +++ b/libgo/go/syscall/route_ifma_test.go @@ -0,0 +1,74 @@ +// Copyright 2015 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 freebsd + +package syscall_test + +import ( + "fmt" + "syscall" +) + +func parseRoutingMessageHeader(m syscall.RoutingMessage) (addrFlags, error) { + switch m := m.(type) { + case *syscall.RouteMessage: + errno := syscall.Errno(uintptr(m.Header.Errno)) + if errno != 0 { + return 0, fmt.Errorf("%T: %v, %#v", m, errno, m.Header) + } + return addrFlags(m.Header.Addrs), nil + case *syscall.InterfaceMessage: + return addrFlags(m.Header.Addrs), nil + case *syscall.InterfaceAddrMessage: + return addrFlags(m.Header.Addrs), nil + case *syscall.InterfaceMulticastAddrMessage: + return addrFlags(m.Header.Addrs), nil + default: + panic(fmt.Sprintf("unknown routing message type: %T", m)) + } +} + +func parseRoutingSockaddrs(m syscall.RoutingMessage) ([]syscall.Sockaddr, error) { + switch m := m.(type) { + case *syscall.RouteMessage: + sas, err := syscall.ParseRoutingSockaddr(m) + if err != nil { + return nil, fmt.Errorf("%T: %v, %#v", m, err, m.Data) + } + if err = sockaddrs(sas).match(addrFlags(m.Header.Addrs)); err != nil { + return nil, err + } + return sas, nil + case *syscall.InterfaceMessage: + sas, err := syscall.ParseRoutingSockaddr(m) + if err != nil { + return nil, fmt.Errorf("%T: %v, %#v", m, err, m.Data) + } + if err = sockaddrs(sas).match(addrFlags(m.Header.Addrs)); err != nil { + return nil, err + } + return sas, nil + case *syscall.InterfaceAddrMessage: + sas, err := syscall.ParseRoutingSockaddr(m) + if err != nil { + return nil, fmt.Errorf("%T: %v, %#v", m, err, m.Data) + } + if err = sockaddrs(sas).match(addrFlags(m.Header.Addrs)); err != nil { + return nil, err + } + return sas, nil + case *syscall.InterfaceMulticastAddrMessage: + sas, err := syscall.ParseRoutingSockaddr(m) + if err != nil { + return nil, fmt.Errorf("%T: %v, %#v", m, err, m.Data) + } + if err = sockaddrs(sas).match(addrFlags(m.Header.Addrs)); err != nil { + return nil, err + } + return sas, nil + default: + panic(fmt.Sprintf("unknown routing message type: %T", m)) + } +} diff --git a/libgo/go/syscall/route_netbsd.go b/libgo/go/syscall/route_netbsd.go index 9883aebaf5f..d605ffa30f8 100644 --- a/libgo/go/syscall/route_netbsd.go +++ b/libgo/go/syscall/route_netbsd.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Routing sockets and messages for NetBSD - package syscall import "unsafe" @@ -12,6 +10,8 @@ func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage { switch any.Type { case RTM_ADD, RTM_DELETE, RTM_CHANGE, RTM_GET, RTM_LOSING, RTM_REDIRECT, RTM_MISS, RTM_LOCK, RTM_RESOLVE: p := (*RouteMessage)(unsafe.Pointer(any)) + // We don't support sockaddr_mpls for now. + p.Header.Addrs &= RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_GENMASK | RTA_IFA | RTA_IFP | RTA_BRD | RTA_AUTHOR return &RouteMessage{Header: p.Header, Data: b[SizeofRtMsghdr:any.Msglen]} case RTM_IFINFO: p := (*InterfaceMessage)(unsafe.Pointer(any)) @@ -32,4 +32,4 @@ type InterfaceAnnounceMessage struct { Header IfAnnounceMsghdr } -func (m *InterfaceAnnounceMessage) sockaddr() (sas []Sockaddr) { return nil } +func (m *InterfaceAnnounceMessage) sockaddr() ([]Sockaddr, error) { return nil, nil } diff --git a/libgo/go/syscall/route_noifma_test.go b/libgo/go/syscall/route_noifma_test.go new file mode 100644 index 00000000000..19d5d8ebbf2 --- /dev/null +++ b/libgo/go/syscall/route_noifma_test.go @@ -0,0 +1,63 @@ +// Copyright 2015 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 netbsd openbsd + +package syscall_test + +import ( + "fmt" + "syscall" +) + +func parseRoutingMessageHeader(m syscall.RoutingMessage) (addrFlags, error) { + switch m := m.(type) { + case *syscall.RouteMessage: + errno := syscall.Errno(uintptr(m.Header.Errno)) + if errno != 0 { + return 0, fmt.Errorf("%T: %v, %#v", m, errno, m.Header) + } + return addrFlags(m.Header.Addrs), nil + case *syscall.InterfaceMessage: + return addrFlags(m.Header.Addrs), nil + case *syscall.InterfaceAddrMessage: + return addrFlags(m.Header.Addrs), nil + default: + panic(fmt.Sprintf("unknown routing message type: %T", m)) + } +} + +func parseRoutingSockaddrs(m syscall.RoutingMessage) ([]syscall.Sockaddr, error) { + switch m := m.(type) { + case *syscall.RouteMessage: + sas, err := syscall.ParseRoutingSockaddr(m) + if err != nil { + return nil, fmt.Errorf("%T: %v, %#v", m, err, m.Data) + } + if err = sockaddrs(sas).match(addrFlags(m.Header.Addrs)); err != nil { + return nil, err + } + return sas, nil + case *syscall.InterfaceMessage: + sas, err := syscall.ParseRoutingSockaddr(m) + if err != nil { + return nil, fmt.Errorf("%T: %v, %#v", m, err, m.Data) + } + if err = sockaddrs(sas).match(addrFlags(m.Header.Addrs)); err != nil { + return nil, err + } + return sas, nil + case *syscall.InterfaceAddrMessage: + sas, err := syscall.ParseRoutingSockaddr(m) + if err != nil { + return nil, fmt.Errorf("%T: %v, %#v", m, err, m.Data) + } + if err = sockaddrs(sas).match(addrFlags(m.Header.Addrs)); err != nil { + return nil, err + } + return sas, nil + default: + panic(fmt.Sprintf("unknown routing message type: %T", m)) + } +} diff --git a/libgo/go/syscall/route_openbsd.go b/libgo/go/syscall/route_openbsd.go index e5086400c5d..7804a08910e 100644 --- a/libgo/go/syscall/route_openbsd.go +++ b/libgo/go/syscall/route_openbsd.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Routing sockets and messages for OpenBSD - package syscall import "unsafe" @@ -12,6 +10,8 @@ func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage { switch any.Type { case RTM_ADD, RTM_DELETE, RTM_CHANGE, RTM_GET, RTM_LOSING, RTM_REDIRECT, RTM_MISS, RTM_LOCK, RTM_RESOLVE: p := (*RouteMessage)(unsafe.Pointer(any)) + // We don't support sockaddr_rtlabel for now. + p.Header.Addrs &= RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_GENMASK | RTA_IFA | RTA_IFP | RTA_BRD | RTA_AUTHOR | RTA_SRC | RTA_SRCMASK return &RouteMessage{Header: p.Header, Data: b[p.Header.Hdrlen:any.Msglen]} case RTM_IFINFO: p := (*InterfaceMessage)(unsafe.Pointer(any)) @@ -32,4 +32,4 @@ type InterfaceAnnounceMessage struct { Header IfAnnounceMsghdr } -func (m *InterfaceAnnounceMessage) sockaddr() (sas []Sockaddr) { return nil } +func (m *InterfaceAnnounceMessage) sockaddr() ([]Sockaddr, error) { return nil, nil } diff --git a/libgo/go/syscall/security_windows.go b/libgo/go/syscall/security_windows.go index b22ecf578e0..1625b07ae4b 100644 --- a/libgo/go/syscall/security_windows.go +++ b/libgo/go/syscall/security_windows.go @@ -41,21 +41,20 @@ func TranslateAccountName(username string, from, to uint32, initSize int) (strin if e != nil { return "", e } - b := make([]uint16, 50) - n := uint32(len(b)) - e = TranslateName(u, from, to, &b[0], &n) - if e != nil { + n := uint32(50) + for { + b := make([]uint16, n) + e = TranslateName(u, from, to, &b[0], &n) + if e == nil { + return UTF16ToString(b[:n]), nil + } if e != ERROR_INSUFFICIENT_BUFFER { return "", e } - // make receive buffers of requested size and try again - b = make([]uint16, n) - e = TranslateName(u, from, to, &b[0], &n) - if e != nil { + if n <= uint32(len(b)) { return "", e } } - return UTF16ToString(b), nil } const ( @@ -136,26 +135,23 @@ func LookupSID(system, account string) (sid *SID, domain string, accType uint32, return nil, "", 0, e } } - db := make([]uint16, 50) - dn := uint32(len(db)) - b := make([]byte, 50) - n := uint32(len(b)) - sid = (*SID)(unsafe.Pointer(&b[0])) - e = LookupAccountName(sys, acc, sid, &n, &db[0], &dn, &accType) - if e != nil { + n := uint32(50) + dn := uint32(50) + for { + b := make([]byte, n) + db := make([]uint16, dn) + sid = (*SID)(unsafe.Pointer(&b[0])) + e = LookupAccountName(sys, acc, sid, &n, &db[0], &dn, &accType) + if e == nil { + return sid, UTF16ToString(db), accType, nil + } if e != ERROR_INSUFFICIENT_BUFFER { return nil, "", 0, e } - // make receive buffers of requested size and try again - b = make([]byte, n) - sid = (*SID)(unsafe.Pointer(&b[0])) - db = make([]uint16, dn) - e = LookupAccountName(sys, acc, sid, &n, &db[0], &dn, &accType) - if e != nil { + if n <= uint32(len(b)) { return nil, "", 0, e } } - return sid, UTF16ToString(db), accType, nil } // String converts sid to a string format @@ -197,24 +193,22 @@ func (sid *SID) LookupAccount(system string) (account, domain string, accType ui return "", "", 0, err } } - b := make([]uint16, 50) - n := uint32(len(b)) - db := make([]uint16, 50) - dn := uint32(len(db)) - e := LookupAccountSid(sys, sid, &b[0], &n, &db[0], &dn, &accType) - if e != nil { + n := uint32(50) + dn := uint32(50) + for { + b := make([]uint16, n) + db := make([]uint16, dn) + e := LookupAccountSid(sys, sid, &b[0], &n, &db[0], &dn, &accType) + if e == nil { + return UTF16ToString(b), UTF16ToString(db), accType, nil + } if e != ERROR_INSUFFICIENT_BUFFER { return "", "", 0, e } - // make receive buffers of requested size and try again - b = make([]uint16, n) - db = make([]uint16, dn) - e = LookupAccountSid(nil, sid, &b[0], &n, &db[0], &dn, &accType) - if e != nil { + if n <= uint32(len(b)) { return "", "", 0, e } } - return UTF16ToString(b), UTF16ToString(db), accType, nil } const ( @@ -326,21 +320,20 @@ func (t Token) Close() error { // getInfo retrieves a specified type of information about an access token. func (t Token) getInfo(class uint32, initSize int) (unsafe.Pointer, error) { - b := make([]byte, initSize) - var n uint32 - e := GetTokenInformation(t, class, &b[0], uint32(len(b)), &n) - if e != nil { + n := uint32(initSize) + for { + b := make([]byte, n) + e := GetTokenInformation(t, class, &b[0], uint32(len(b)), &n) + if e == nil { + return unsafe.Pointer(&b[0]), nil + } if e != ERROR_INSUFFICIENT_BUFFER { return nil, e } - // make receive buffers of requested size and try again - b = make([]byte, n) - e = GetTokenInformation(t, class, &b[0], uint32(len(b)), &n) - if e != nil { + if n <= uint32(len(b)) { return nil, e } } - return unsafe.Pointer(&b[0]), nil } // GetTokenUser retrieves access token t user account information. @@ -366,19 +359,18 @@ func (t Token) GetTokenPrimaryGroup() (*Tokenprimarygroup, error) { // GetUserProfileDirectory retrieves path to the // root directory of the access token t user's profile. func (t Token) GetUserProfileDirectory() (string, error) { - b := make([]uint16, 100) - n := uint32(len(b)) - e := GetUserProfileDirectory(t, &b[0], &n) - if e != nil { + n := uint32(100) + for { + b := make([]uint16, n) + e := GetUserProfileDirectory(t, &b[0], &n) + if e == nil { + return UTF16ToString(b), nil + } if e != ERROR_INSUFFICIENT_BUFFER { return "", e } - // make receive buffers of requested size and try again - b = make([]uint16, n) - e = GetUserProfileDirectory(t, &b[0], &n) - if e != nil { + if n <= uint32(len(b)) { return "", e } } - return UTF16ToString(b), nil } diff --git a/libgo/go/syscall/syscall.go b/libgo/go/syscall/syscall.go index ef9d7d65973..ff09711ae04 100644 --- a/libgo/go/syscall/syscall.go +++ b/libgo/go/syscall/syscall.go @@ -20,7 +20,7 @@ // // NOTE: This package is locked down. Code outside the standard // Go repository should be migrated to use the corresponding -// package in the go.sys subrepository. That is also where updates +// 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. // @@ -28,9 +28,11 @@ package syscall import "unsafe" -// StringByteSlice is deprecated. Use ByteSliceFromString instead. +// StringByteSlice converts a string to a NUL-terminated []byte, // If s contains a NUL byte this function panics instead of // returning an error. +// +// Deprecated: Use ByteSliceFromString instead. func StringByteSlice(s string) []byte { a, err := ByteSliceFromString(s) if err != nil { @@ -53,9 +55,11 @@ func ByteSliceFromString(s string) ([]byte, error) { return a, nil } -// StringBytePtr is deprecated. Use BytePtrFromString instead. -// If s contains a NUL byte this function panics instead of -// returning an error. +// StringBytePtr returns a pointer to a NUL-terminated array of bytes. +// If s contains a NUL byte this function panics instead of returning +// an error. +// +// Deprecated: Use BytePtrFromString instead. func StringBytePtr(s string) *byte { return &StringByteSlice(s)[0] } // BytePtrFromString returns a pointer to a NUL-terminated array of diff --git a/libgo/go/syscall/syscall_linux_test.go b/libgo/go/syscall/syscall_linux_test.go new file mode 100644 index 00000000000..40fce6d68c6 --- /dev/null +++ b/libgo/go/syscall/syscall_linux_test.go @@ -0,0 +1,140 @@ +// Copyright 2015 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_test + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "os/signal" + "path/filepath" + "syscall" + "testing" + "time" +) + +func TestMain(m *testing.M) { + if os.Getenv("GO_DEATHSIG_PARENT") == "1" { + deathSignalParent() + } else if os.Getenv("GO_DEATHSIG_CHILD") == "1" { + deathSignalChild() + } + + os.Exit(m.Run()) +} + +func TestLinuxDeathSignal(t *testing.T) { + if os.Getuid() != 0 { + t.Skip("skipping root only test") + } + + // Copy the test binary to a location that a non-root user can read/execute + // after we drop privileges + tempDir, err := ioutil.TempDir("", "TestDeathSignal") + if err != nil { + t.Fatalf("cannot create temporary directory: %v", err) + } + defer os.RemoveAll(tempDir) + os.Chmod(tempDir, 0755) + + tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0])) + + src, err := os.Open(os.Args[0]) + if err != nil { + t.Fatalf("cannot open binary %q, %v", os.Args[0], err) + } + defer src.Close() + + dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err) + } + if _, err := io.Copy(dst, src); err != nil { + t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err) + } + err = dst.Close() + if err != nil { + t.Fatalf("failed to close test binary %q, %v", tmpBinary, err) + } + + cmd := exec.Command(tmpBinary) + cmd.Env = []string{"GO_DEATHSIG_PARENT=1"} + chldStdin, err := cmd.StdinPipe() + if err != nil { + t.Fatal("failed to create new stdin pipe: %v", err) + } + chldStdout, err := cmd.StdoutPipe() + if err != nil { + t.Fatal("failed to create new stdout pipe: %v", err) + } + cmd.Stderr = os.Stderr + + err = cmd.Start() + defer cmd.Wait() + if err != nil { + t.Fatalf("failed to start first child process: %v", err) + } + + chldPipe := bufio.NewReader(chldStdout) + + if got, err := chldPipe.ReadString('\n'); got == "start\n" { + syscall.Kill(cmd.Process.Pid, syscall.SIGTERM) + + go func() { + time.Sleep(5 * time.Second) + chldStdin.Close() + }() + + want := "ok\n" + if got, err = chldPipe.ReadString('\n'); got != want { + t.Fatalf("expected %q, received %q, %v", want, got, err) + } + } else { + t.Fatalf("did not receive start from child, received %q, %v", got, err) + } +} + +func deathSignalParent() { + cmd := exec.Command(os.Args[0]) + cmd.Env = []string{"GO_DEATHSIG_CHILD=1"} + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + attrs := syscall.SysProcAttr{ + Pdeathsig: syscall.SIGUSR1, + // UID/GID 99 is the user/group "nobody" on RHEL/Fedora and is + // unused on Ubuntu + Credential: &syscall.Credential{Uid: 99, Gid: 99}, + } + cmd.SysProcAttr = &attrs + + err := cmd.Start() + if err != nil { + fmt.Fprintf(os.Stderr, "death signal parent error: %v\n") + os.Exit(1) + } + cmd.Wait() + os.Exit(0) +} + +func deathSignalChild() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGUSR1) + go func() { + <-c + fmt.Println("ok") + os.Exit(0) + }() + fmt.Println("start") + + buf := make([]byte, 32) + os.Stdin.Read(buf) + + // We expected to be signaled before stdin closed + fmt.Println("not ok") + os.Exit(1) +} diff --git a/libgo/go/syscall/syscall_unix.go b/libgo/go/syscall/syscall_unix.go index 74f10c29da5..21bf6eafb11 100644 --- a/libgo/go/syscall/syscall_unix.go +++ b/libgo/go/syscall/syscall_unix.go @@ -172,6 +172,30 @@ func Munmap(b []byte) (err error) { return mapper.Munmap(b) } +// Do the interface allocations only once for common +// Errno values. +var ( + errEAGAIN error = EAGAIN + errEINVAL error = EINVAL + errENOENT error = ENOENT +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e Errno) error { + switch e { + case 0: + return nil + case EAGAIN: + return errEAGAIN + case EINVAL: + return errEINVAL + case ENOENT: + return errENOENT + } + return e +} + // A Signal is a number describing a process signal. // It implements the os.Signal interface. type Signal int diff --git a/libgo/go/syscall/syscall_unix_test.go b/libgo/go/syscall/syscall_unix_test.go index 897ad18a974..c7b4560b76b 100644 --- a/libgo/go/syscall/syscall_unix_test.go +++ b/libgo/go/syscall/syscall_unix_test.go @@ -9,6 +9,7 @@ package syscall_test import ( "flag" "fmt" + "internal/testenv" "io/ioutil" "net" "os" @@ -60,20 +61,58 @@ func _() { // TestFcntlFlock tests whether the file locking structure matches // the calling convention of each kernel. +// On some Linux systems, glibc uses another set of values for the +// commands and translates them to the correct value that the kernel +// expects just before the actual fcntl syscall. As Go uses raw +// syscalls directly, it must use the real value, not the glibc value. +// Thus this test also verifies that the Flock_t structure can be +// roundtripped with F_SETLK and F_GETLK. func TestFcntlFlock(t *testing.T) { - name := filepath.Join(os.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) + if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { + t.Skip("skipping; no child processes allowed on iOS") } - defer syscall.Unlink(name) - defer syscall.Close(fd) flock := syscall.Flock_t{ - Type: syscall.F_RDLCK, - Start: 0, Len: 0, Whence: 1, + Type: syscall.F_WRLCK, + Start: 31415, Len: 271828, Whence: 1, } - if err := syscall.FcntlFlock(uintptr(fd), syscall.F_GETLK, &flock); err != nil { - t.Fatalf("FcntlFlock failed: %v", err) + if os.Getenv("GO_WANT_HELPER_PROCESS") == "" { + // parent + name := filepath.Join(os.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 syscall.Close(fd) + if err := syscall.Ftruncate(fd, 1<<20); err != nil { + t.Fatalf("Ftruncate(1<<20) failed: %v", err) + } + if err := syscall.FcntlFlock(uintptr(fd), syscall.F_SETLK, &flock); err != nil { + t.Fatalf("FcntlFlock(F_SETLK) failed: %v", err) + } + cmd := exec.Command(os.Args[0], "-test.run=^TestFcntlFlock$") + cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") + cmd.ExtraFiles = []*os.File{os.NewFile(uintptr(fd), name)} + out, err := cmd.CombinedOutput() + if len(out) > 0 || err != nil { + t.Fatalf("child process: %q, %v", out, err) + } + } else { + // child + got := flock + // make sure the child lock is conflicting with the parent lock + got.Start-- + got.Len++ + if err := syscall.FcntlFlock(3, syscall.F_GETLK, &got); err != nil { + t.Fatalf("FcntlFlock(F_GETLK) failed: %v", err) + } + flock.Pid = int32(syscall.Getppid()) + // Linux kernel always set Whence to 0 + flock.Whence = 0 + if got.Type == flock.Type && got.Start == flock.Start && got.Len == flock.Len && got.Pid == flock.Pid && got.Whence == flock.Whence { + os.Exit(0) + } + t.Fatalf("FcntlFlock got %v, want %v", got, flock) } } @@ -93,6 +132,9 @@ func TestPassFD(t *testing.T) { // TODO(aram): Figure out why ReadMsgUnix is returning empty message. t.Skip("skipping test on solaris, see issue 7402") } + + testenv.MustHaveExec(t) + if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { passFDChild() return @@ -116,11 +158,7 @@ func TestPassFD(t *testing.T) { defer readFile.Close() cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", tempDir) - cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} - path := os.Getenv("LD_LIBRARY_PATH") - if path != "" { - cmd.Env = append(cmd.Env, "LD_LIBRARY_PATH="+path) - } + cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") cmd.ExtraFiles = []*os.File{writeFile} out, err := cmd.CombinedOutput() @@ -179,7 +217,7 @@ func passFDChild() { defer os.Exit(0) // Look for our fd. It should be fd 3, but we work around an fd leak - // bug here (http://golang.org/issue/2603) to let it be elsewhere. + // bug here (https://golang.org/issue/2603) to let it be elsewhere. var uc *net.UnixConn for fd := uintptr(3); fd <= 10; fd++ { f := os.NewFile(fd, "unix-conn") |