diff options
author | Ian Lance Taylor <iant@golang.org> | 2018-09-24 21:46:21 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2018-09-24 21:46:21 +0000 |
commit | dd931d9b48647e898dc80927c532ae93cc09e192 (patch) | |
tree | 71be2295cd79b8a182f6130611658db8628772d5 /libgo/go/syscall/syscall_linux_test.go | |
parent | 779d8a5ad09b01428726ea5a0e6c87bd9ac3c0e4 (diff) |
libgo: update to Go 1.11
Reviewed-on: https://go-review.googlesource.com/136435
gotools/:
* Makefile.am (mostlyclean-local): Run chmod on check-go-dir to
make sure it is writable.
(check-go-tools): Likewise.
(check-vet): Copy internal/objabi to check-vet-dir.
* Makefile.in: Rebuild.
From-SVN: r264546
Diffstat (limited to 'libgo/go/syscall/syscall_linux_test.go')
-rw-r--r-- | libgo/go/syscall/syscall_linux_test.go | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/libgo/go/syscall/syscall_linux_test.go b/libgo/go/syscall/syscall_linux_test.go index 2c4d953561a..99de6ebaf2f 100644 --- a/libgo/go/syscall/syscall_linux_test.go +++ b/libgo/go/syscall/syscall_linux_test.go @@ -13,16 +13,139 @@ import ( "os/exec" "os/signal" "path/filepath" + "runtime" + "strconv" + "strings" "syscall" "testing" "time" ) +// chtmpdir changes the working directory to a new temporary directory and +// provides a cleanup function. Used when PWD is read-only. +func chtmpdir(t *testing.T) func() { + oldwd, err := os.Getwd() + if err != nil { + t.Fatalf("chtmpdir: %v", err) + } + d, err := ioutil.TempDir("", "test") + if err != nil { + t.Fatalf("chtmpdir: %v", err) + } + if err := os.Chdir(d); err != nil { + t.Fatalf("chtmpdir: %v", err) + } + return func() { + if err := os.Chdir(oldwd); err != nil { + t.Fatalf("chtmpdir: %v", err) + } + os.RemoveAll(d) + } +} + +func touch(t *testing.T, name string) { + f, err := os.Create(name) + if err != nil { + t.Fatal(err) + } + if err := f.Close(); err != nil { + t.Fatal(err) + } +} + +const ( + _AT_SYMLINK_NOFOLLOW = 0x100 + _AT_FDCWD = -0x64 + _AT_EACCESS = 0x200 + _F_OK = 0 + _R_OK = 4 +) + +func TestFaccessat(t *testing.T) { + defer chtmpdir(t)() + touch(t, "file1") + + err := syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, 0) + if err != nil { + t.Errorf("Faccessat: unexpected error: %v", err) + } + + err = syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, 2) + if err != syscall.EINVAL { + t.Errorf("Faccessat: unexpected error: %v, want EINVAL", err) + } + + err = syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, _AT_EACCESS) + if err != nil { + t.Errorf("Faccessat: unexpected error: %v", err) + } + + err = os.Symlink("file1", "symlink1") + if err != nil { + t.Fatal(err) + } + + err = syscall.Faccessat(_AT_FDCWD, "symlink1", _R_OK, _AT_SYMLINK_NOFOLLOW) + if err != nil { + t.Errorf("Faccessat SYMLINK_NOFOLLOW: unexpected error %v", err) + } + + // We can't really test _AT_SYMLINK_NOFOLLOW, because there + // doesn't seem to be any way to change the mode of a symlink. + // We don't test _AT_EACCESS because such tests are only + // meaningful if run as root. + + err = syscall.Fchmodat(_AT_FDCWD, "file1", 0, 0) + if err != nil { + t.Errorf("Fchmodat: unexpected error %v", err) + } + + err = syscall.Faccessat(_AT_FDCWD, "file1", _F_OK, _AT_SYMLINK_NOFOLLOW) + if err != nil { + t.Errorf("Faccessat: unexpected error: %v", err) + } + + err = syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, _AT_SYMLINK_NOFOLLOW) + if err != syscall.EACCES { + if syscall.Getuid() != 0 { + t.Errorf("Faccessat: unexpected error: %v, want EACCES", err) + } + } +} + +func TestFchmodat(t *testing.T) { + defer chtmpdir(t)() + + touch(t, "file1") + os.Symlink("file1", "symlink1") + + err := syscall.Fchmodat(_AT_FDCWD, "symlink1", 0444, 0) + if err != nil { + t.Fatalf("Fchmodat: unexpected error: %v", err) + } + + fi, err := os.Stat("file1") + if err != nil { + t.Fatal(err) + } + + if fi.Mode() != 0444 { + t.Errorf("Fchmodat: failed to change mode: expected %v, got %v", 0444, fi.Mode()) + } + + err = syscall.Fchmodat(_AT_FDCWD, "symlink1", 0444, _AT_SYMLINK_NOFOLLOW) + if err != syscall.EOPNOTSUPP { + t.Fatalf("Fchmodat: unexpected error: %v, expected EOPNOTSUPP", err) + } +} + func TestMain(m *testing.M) { if os.Getenv("GO_DEATHSIG_PARENT") == "1" { deathSignalParent() } else if os.Getenv("GO_DEATHSIG_CHILD") == "1" { deathSignalChild() + } else if os.Getenv("GO_SYSCALL_NOERROR") == "1" { + syscallNoError() } os.Exit(m.Run()) @@ -166,3 +289,82 @@ func TestParseNetlinkMessage(t *testing.T) { } } } + +func TestSyscallNoError(t *testing.T) { + // On Linux there are currently no syscalls which don't fail and return + // a value larger than 0xfffffffffffff001 so we could test RawSyscall + // vs. RawSyscallNoError on 64bit architectures. + if runtime.GOARCH != "386" && runtime.GOARCH != "arm" { + t.Skip("skipping on non-32bit architecture") + } + + 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("", "TestSyscallNoError") + 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) + } + + uid := uint32(0xfffffffe) + err = os.Chown(tmpBinary, int(uid), -1) + if err != nil { + t.Fatalf("failed to chown test binary %q, %v", tmpBinary, err) + } + + err = os.Chmod(tmpBinary, 0755|os.ModeSetuid) + if err != nil { + t.Fatalf("failed to set setuid bit on test binary %q, %v", tmpBinary, err) + } + + cmd := exec.Command(tmpBinary) + cmd.Env = []string{"GO_SYSCALL_NOERROR=1"} + + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("failed to start first child process: %v", err) + } + + got := strings.TrimSpace(string(out)) + want := strconv.FormatUint(uint64(uid)+1, 10) + " / " + + strconv.FormatUint(uint64(-uid), 10) + " / " + + strconv.FormatUint(uint64(uid), 10) + if got != want { + t.Errorf("expected %s, got %s", want, got) + } +} + +func syscallNoError() { + // Test that the return value from SYS_GETEUID32 (which cannot fail) + // doesn't get treated as an error (see https://golang.org/issue/22924) + euid1, _, e := syscall.RawSyscall(syscall.Sys_GETEUID, 0, 0, 0) + euid2, _ := syscall.RawSyscallNoError(syscall.Sys_GETEUID, 0, 0, 0) + + fmt.Println(uintptr(euid1), "/", int(e), "/", uintptr(euid2)) + os.Exit(0) +} |