diff options
author | Ian Lance Taylor <iant@golang.org> | 2020-12-23 09:57:37 -0800 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2020-12-30 15:13:24 -0800 |
commit | cfcbb4227fb20191e04eb8d7766ae6202f526afd (patch) | |
tree | e2effea96f6f204451779f044415c2385e45042b /libgo/go/syscall | |
parent | 0696141107d61483f38482b941549959a0d7f613 (diff) | |
download | gcc-cfcbb4227fb20191e04eb8d7766ae6202f526afd.zip gcc-cfcbb4227fb20191e04eb8d7766ae6202f526afd.tar.gz gcc-cfcbb4227fb20191e04eb8d7766ae6202f526afd.tar.bz2 |
libgo: update to Go1.16beta1 release
This does not yet include support for the //go:embed directive added
in this release.
* Makefile.am (check-runtime): Don't create check-runtime-dir.
(mostlyclean-local): Don't remove check-runtime-dir.
(check-go-tool, check-vet): Copy in go.mod and modules.txt.
(check-cgo-test, check-carchive-test): Add go.mod file.
* Makefile.in: Regenerate.
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/280172
Diffstat (limited to 'libgo/go/syscall')
23 files changed, 463 insertions, 101 deletions
diff --git a/libgo/go/syscall/dirent_test.go b/libgo/go/syscall/dirent_test.go index f631533..7dac98f 100644 --- a/libgo/go/syscall/dirent_test.go +++ b/libgo/go/syscall/dirent_test.go @@ -9,7 +9,6 @@ package syscall_test import ( "bytes" "fmt" - "io/ioutil" "os" "path/filepath" "runtime" @@ -27,7 +26,7 @@ func TestDirent(t *testing.T) { filenameMinSize = 11 ) - d, err := ioutil.TempDir("", "dirent-test") + d, err := os.MkdirTemp("", "dirent-test") if err != nil { t.Fatalf("tempdir: %v", err) } @@ -36,7 +35,7 @@ func TestDirent(t *testing.T) { for i, c := range []byte("0123456789") { name := string(bytes.Repeat([]byte{c}, filenameMinSize+i)) - err = ioutil.WriteFile(filepath.Join(d, name), nil, 0644) + err = os.WriteFile(filepath.Join(d, name), nil, 0644) if err != nil { t.Fatalf("writefile: %v", err) } @@ -93,7 +92,7 @@ func TestDirentRepeat(t *testing.T) { } // Make a directory containing N files - d, err := ioutil.TempDir("", "direntRepeat-test") + d, err := os.MkdirTemp("", "direntRepeat-test") if err != nil { t.Fatalf("tempdir: %v", err) } @@ -104,7 +103,7 @@ func TestDirentRepeat(t *testing.T) { files = append(files, fmt.Sprintf("file%d", i)) } for _, file := range files { - err = ioutil.WriteFile(filepath.Join(d, file), []byte("contents"), 0644) + err = os.WriteFile(filepath.Join(d, file), []byte("contents"), 0644) if err != nil { t.Fatalf("writefile: %v", err) } diff --git a/libgo/go/syscall/exec_linux_test.go b/libgo/go/syscall/exec_linux_test.go index 1a44f04..bbab36d 100644 --- a/libgo/go/syscall/exec_linux_test.go +++ b/libgo/go/syscall/exec_linux_test.go @@ -11,7 +11,6 @@ import ( "fmt" "internal/testenv" "io" - "io/ioutil" "os" "os/exec" "os/user" @@ -65,7 +64,7 @@ func skipNoUserNamespaces(t *testing.T) { func skipUnprivilegedUserClone(t *testing.T) { // Skip the test if the sysctl that prevents unprivileged user // from creating user namespaces is enabled. - data, errRead := ioutil.ReadFile("/proc/sys/kernel/unprivileged_userns_clone") + data, errRead := os.ReadFile("/proc/sys/kernel/unprivileged_userns_clone") if errRead != nil || len(data) < 1 || data[0] == '0' { t.Skip("kernel prohibits user namespace in unprivileged process") } @@ -98,7 +97,7 @@ func checkUserNS(t *testing.T) { // 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") + buf, _ := os.ReadFile("/sys/module/user_namespace/parameters/enabled") if !strings.HasPrefix(string(buf), "Y") { t.Skip("kernel doesn't support user namespaces") } @@ -106,7 +105,7 @@ func checkUserNS(t *testing.T) { // On Centos 7.5+, user namespaces are disabled if user.max_user_namespaces = 0 if _, err := os.Stat("/proc/sys/user/max_user_namespaces"); err == nil { - buf, errRead := ioutil.ReadFile("/proc/sys/user/max_user_namespaces") + buf, errRead := os.ReadFile("/proc/sys/user/max_user_namespaces") if errRead == nil && buf[0] == '0' { t.Skip("kernel doesn't support user namespaces") } @@ -226,7 +225,7 @@ func TestUnshare(t *testing.T) { t.Fatal(err) } - orig, err := ioutil.ReadFile(path) + orig, err := os.ReadFile(path) if err != nil { t.Fatal(err) } @@ -349,7 +348,7 @@ func TestUnshareMountNameSpace(t *testing.T) { t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace") } - d, err := ioutil.TempDir("", "unshare") + d, err := os.MkdirTemp("", "unshare") if err != nil { t.Fatalf("tempdir: %v", err) } @@ -391,7 +390,7 @@ func TestUnshareMountNameSpaceChroot(t *testing.T) { t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace") } - d, err := ioutil.TempDir("", "unshare") + d, err := os.MkdirTemp("", "unshare") if err != nil { t.Fatalf("tempdir: %v", err) } @@ -599,7 +598,7 @@ func testAmbientCaps(t *testing.T, userns bool) { } // Copy the test binary to a temporary location which is readable by nobody. - f, err := ioutil.TempFile("", "gotest") + f, err := os.CreateTemp("", "gotest") if err != nil { t.Fatal(err) } diff --git a/libgo/go/syscall/exec_unix.go b/libgo/go/syscall/exec_unix.go index 7c6d942..2441f5e 100644 --- a/libgo/go/syscall/exec_unix.go +++ b/libgo/go/syscall/exec_unix.go @@ -347,9 +347,9 @@ func Exec(argv0 string, argv []string, envv []string) (err error) { var err1 error if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "aix" || runtime.GOOS == "hurd" { - // RawSyscall should never be used on Solaris or AIX. + // RawSyscall should never be used on Solaris, illumos, or AIX. err1 = raw_execve(argv0p, &argvp[0], &envvp[0]) - } else if runtime.GOOS == "darwin" { + } else if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { // Similarly on Darwin. err1 = execveDarwin(argv0p, &argvp[0], &envvp[0]) } else { diff --git a/libgo/go/syscall/exec_windows.go b/libgo/go/syscall/exec_windows.go index 8d6141c..46cbd75 100644 --- a/libgo/go/syscall/exec_windows.go +++ b/libgo/go/syscall/exec_windows.go @@ -25,73 +25,89 @@ var ForkLock sync.RWMutex // but only if there is space or tab inside s. func EscapeArg(s string) string { if len(s) == 0 { - return "\"\"" + return `""` } - n := len(s) + for i := 0; i < len(s); i++ { + switch s[i] { + case '"', '\\', ' ', '\t': + // Some escaping required. + b := make([]byte, 0, len(s)+2) + b = appendEscapeArg(b, s) + return string(b) + } + } + return s +} + +// appendEscapeArg escapes the string s, as per escapeArg, +// appends the result to b, and returns the updated slice. +func appendEscapeArg(b []byte, s string) []byte { + if len(s) == 0 { + return append(b, `""`...) + } + + needsBackslash := false hasSpace := false for i := 0; i < len(s); i++ { switch s[i] { case '"', '\\': - n++ + needsBackslash = true case ' ', '\t': hasSpace = true } } - if hasSpace { - n += 2 + + if !needsBackslash && !hasSpace { + // No special handling required; normal case. + return append(b, s...) } - if n == len(s) { - return s + if !needsBackslash { + // hasSpace is true, so we need to quote the string. + b = append(b, '"') + b = append(b, s...) + return append(b, '"') } - qs := make([]byte, n) - j := 0 if hasSpace { - qs[j] = '"' - j++ + b = append(b, '"') } slashes := 0 for i := 0; i < len(s); i++ { - switch s[i] { + c := s[i] + switch c { default: slashes = 0 - qs[j] = s[i] case '\\': slashes++ - qs[j] = s[i] case '"': for ; slashes > 0; slashes-- { - qs[j] = '\\' - j++ + b = append(b, '\\') } - qs[j] = '\\' - j++ - qs[j] = s[i] + b = append(b, '\\') } - j++ + b = append(b, c) } if hasSpace { for ; slashes > 0; slashes-- { - qs[j] = '\\' - j++ + b = append(b, '\\') } - qs[j] = '"' - j++ + b = append(b, '"') } - return string(qs[:j]) + + return b } // makeCmdLine builds a command line out of args by escaping "special" // characters and joining the arguments with spaces. func makeCmdLine(args []string) string { - var s string + var b []byte for _, v := range args { - if s != "" { - s += " " + if len(b) > 0 { + b = append(b, ' ') } - s += EscapeArg(v) + b = appendEscapeArg(b, v) } - return s + return string(b) } // createEnvBlock converts an array of environment strings into @@ -225,6 +241,7 @@ type SysProcAttr struct { Token Token // if set, runs new process in the security context represented by the token ProcessAttributes *SecurityAttributes // if set, applies these security attributes as the descriptor for the new process ThreadAttributes *SecurityAttributes // if set, applies these security attributes as the descriptor for the main thread of the new process + NoInheritHandles bool // if set, each inheritable handle in the calling process is not inherited by the new process } var zeroProcAttr ProcAttr @@ -325,9 +342,9 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT if sys.Token != 0 { - err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, true, flags, createEnvBlock(attr.Env), dirp, si, pi) + err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, si, pi) } else { - err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, true, flags, createEnvBlock(attr.Env), dirp, si, pi) + err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, si, pi) } if err != nil { return 0, 0, err diff --git a/libgo/go/syscall/exec_windows_test.go b/libgo/go/syscall/exec_windows_test.go new file mode 100644 index 0000000..eda1d36 --- /dev/null +++ b/libgo/go/syscall/exec_windows_test.go @@ -0,0 +1,43 @@ +// Copyright 2020 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 ( + "syscall" + "testing" +) + +func TestEscapeArg(t *testing.T) { + var tests = []struct { + input, output string + }{ + {``, `""`}, + {`a`, `a`}, + {` `, `" "`}, + {`\`, `\`}, + {`"`, `\"`}, + {`\"`, `\\\"`}, + {`\\"`, `\\\\\"`}, + {`\\ `, `"\\ "`}, + {` \\`, `" \\\\"`}, + {`a `, `"a "`}, + {`C:\`, `C:\`}, + {`C:\Program Files (x32)\Common\`, `"C:\Program Files (x32)\Common\\"`}, + {`C:\Users\Игорь\`, `C:\Users\Игорь\`}, + {`Андрей\file`, `Андрей\file`}, + {`C:\Windows\temp`, `C:\Windows\temp`}, + {`c:\temp\newfile`, `c:\temp\newfile`}, + {`\\?\C:\Windows`, `\\?\C:\Windows`}, + {`\\?\`, `\\?\`}, + {`\\.\C:\Windows\`, `\\.\C:\Windows\`}, + {`\\server\share\file`, `\\server\share\file`}, + {`\\newserver\tempshare\really.txt`, `\\newserver\tempshare\really.txt`}, + } + for _, test := range tests { + if got := syscall.EscapeArg(test.input); got != test.output { + t.Errorf("EscapeArg(%#q) = %#q, want %#q", test.input, got, test.output) + } + } +} diff --git a/libgo/go/syscall/export_unix_test.go b/libgo/go/syscall/export_unix_test.go index 4d67be9..1b78756 100644 --- a/libgo/go/syscall/export_unix_test.go +++ b/libgo/go/syscall/export_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd hurd linux netbsd openbsd solaris +// +build dragonfly freebsd hurd linux netbsd openbsd solaris package syscall diff --git a/libgo/go/syscall/fs_js.go b/libgo/go/syscall/fs_js.go index 262ec28..673feea 100644 --- a/libgo/go/syscall/fs_js.go +++ b/libgo/go/syscall/fs_js.go @@ -8,7 +8,6 @@ package syscall import ( "errors" - "io" "sync" "syscall/js" ) @@ -456,11 +455,11 @@ func Seek(fd int, offset int64, whence int) (int64, error) { var newPos int64 switch whence { - case io.SeekStart: + case 0: newPos = offset - case io.SeekCurrent: + case 1: newPos = f.pos + offset - case io.SeekEnd: + case 2: var st Stat_t if err := Fstat(fd, &st); err != nil { return 0, err diff --git a/libgo/go/syscall/getdirentries_test.go b/libgo/go/syscall/getdirentries_test.go index 2a3419c..66bb8ac 100644 --- a/libgo/go/syscall/getdirentries_test.go +++ b/libgo/go/syscall/getdirentries_test.go @@ -8,7 +8,6 @@ package syscall_test import ( "fmt" - "io/ioutil" "os" "path/filepath" "sort" @@ -29,7 +28,7 @@ func testGetdirentries(t *testing.T, count int) { if count > 100 && testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" { t.Skip("skipping in -short mode") } - d, err := ioutil.TempDir("", "getdirentries-test") + d, err := os.MkdirTemp("", "getdirentries-test") if err != nil { t.Fatalf("Tempdir: %v", err) } @@ -41,7 +40,7 @@ func testGetdirentries(t *testing.T, count int) { // Make files in the temp directory for _, name := range names { - err := ioutil.WriteFile(filepath.Join(d, name), []byte("data"), 0) + err := os.WriteFile(filepath.Join(d, name), []byte("data"), 0) if err != nil { t.Fatalf("WriteFile: %v", err) } diff --git a/libgo/go/syscall/libcall_posix.go b/libgo/go/syscall/libcall_posix.go index 31d6bf1..f37457a 100644 --- a/libgo/go/syscall/libcall_posix.go +++ b/libgo/go/syscall/libcall_posix.go @@ -292,6 +292,9 @@ func Gettimeofday(tv *Timeval) (err error) { //sysnb Setgid(gid int) (err error) //setgid(gid Gid_t) _C_int +//sysnb Setegid(uid int) (err error) +//setegid(uid Uid_t) _C_int + //sysnb Setregid(rgid int, egid int) (err error) //setregid(rgid Gid_t, egid Gid_t) _C_int @@ -317,6 +320,9 @@ func Settimeofday(tv *Timeval) (err error) { //sysnb Setuid(uid int) (err error) //setuid(uid Uid_t) _C_int +//sysnb Seteuid(uid int) (err error) +//seteuid(uid Uid_t) _C_int + //sys Symlink(oldpath string, newpath string) (err error) //symlink(oldpath *byte, newpath *byte) _C_int diff --git a/libgo/go/syscall/mkasm_darwin.go b/libgo/go/syscall/mkasm_darwin.go index f6f75f9..1783387 100644 --- a/libgo/go/syscall/mkasm_darwin.go +++ b/libgo/go/syscall/mkasm_darwin.go @@ -11,23 +11,22 @@ package main import ( "bytes" "fmt" - "io/ioutil" "log" "os" "strings" ) func main() { - in1, err := ioutil.ReadFile("syscall_darwin.go") + in1, err := os.ReadFile("syscall_darwin.go") if err != nil { log.Fatalf("can't open syscall_darwin.go: %s", err) } arch := os.Args[1] - in2, err := ioutil.ReadFile(fmt.Sprintf("syscall_darwin_%s.go", arch)) + in2, err := os.ReadFile(fmt.Sprintf("syscall_darwin_%s.go", arch)) if err != nil { log.Fatalf("can't open syscall_darwin_%s.go: %s", arch, err) } - in3, err := ioutil.ReadFile(fmt.Sprintf("zsyscall_darwin_%s.go", arch)) + in3, err := os.ReadFile(fmt.Sprintf("zsyscall_darwin_%s.go", arch)) if err != nil { log.Fatalf("can't open zsyscall_darwin_%s.go: %s", arch, err) } @@ -51,7 +50,7 @@ func main() { fmt.Fprintf(&out, "\tJMP\t%s(SB)\n", fn) } } - err = ioutil.WriteFile(fmt.Sprintf("zsyscall_darwin_%s.s", arch), out.Bytes(), 0644) + err = os.WriteFile(fmt.Sprintf("zsyscall_darwin_%s.s", arch), out.Bytes(), 0644) if err != nil { log.Fatalf("can't write zsyscall_darwin_%s.s: %s", arch, err) } diff --git a/libgo/go/syscall/setuidgid_32_linux.go b/libgo/go/syscall/setuidgid_32_linux.go index 1fe7120..b0b7f61 100644 --- a/libgo/go/syscall/setuidgid_32_linux.go +++ b/libgo/go/syscall/setuidgid_32_linux.go @@ -12,4 +12,10 @@ const ( sys_SETGID = SYS_SETGID32 sys_SETUID = SYS_SETUID32 + + sys_SETREGID = SYS_SETREGID32 + sys_SETREUID = SYS_SETREUID32 + + sys_SETRESGID = SYS_SETRESGID32 + sys_SETRESUID = SYS_SETRESUID32 ) diff --git a/libgo/go/syscall/setuidgid_linux.go b/libgo/go/syscall/setuidgid_linux.go index 22fa334..38c83c9 100644 --- a/libgo/go/syscall/setuidgid_linux.go +++ b/libgo/go/syscall/setuidgid_linux.go @@ -12,4 +12,10 @@ const ( sys_SETGID = SYS_SETGID sys_SETUID = SYS_SETUID + + sys_SETREGID = SYS_SETREGID + sys_SETREUID = SYS_SETREUID + + sys_SETRESGID = SYS_SETRESGID + sys_SETRESUID = SYS_SETRESUID ) diff --git a/libgo/go/syscall/signame.c b/libgo/go/syscall/signame.c index dca92a9..e2e77cb 100644 --- a/libgo/go/syscall/signame.c +++ b/libgo/go/syscall/signame.c @@ -33,6 +33,9 @@ Signame (intgo sig) len = __builtin_strlen (s); data = runtime_mallocgc (len, nil, false); __builtin_memcpy (data, s, len); + // lowercase first letter: Bad -> bad, but STREAM -> STREAM. + if ('A' <= data[0] && data[0] <= 'Z' && 'a' <= data[1] && data[1] <= 'z') + data[0] += 'a' - 'A'; ret.str = data; ret.len = len; return ret; diff --git a/libgo/go/syscall/sockcmsg_unix_other.go b/libgo/go/syscall/sockcmsg_unix_other.go index 3e7afe4..f5deeb4 100644 --- a/libgo/go/syscall/sockcmsg_unix_other.go +++ b/libgo/go/syscall/sockcmsg_unix_other.go @@ -20,7 +20,7 @@ func cmsgAlignOf(salen int) int { case "aix": // There is no alignment on AIX. salign = 1 - case "darwin", "illumos", "solaris": + case "darwin", "ios", "illumos", "solaris": // NOTE: It seems like 64-bit Darwin, Illumos and Solaris // kernels still require 32-bit aligned access to network // subsystem. @@ -32,6 +32,10 @@ func cmsgAlignOf(salen int) int { if runtime.GOARCH == "arm" { salign = 8 } + // NetBSD aarch64 requires 128-bit alignment. + if runtime.GOOS == "netbsd" && runtime.GOARCH == "arm64" { + salign = 16 + } } return (salen + salign - 1) & ^(salign - 1) diff --git a/libgo/go/syscall/syscall.go b/libgo/go/syscall/syscall.go index 4e1187b..fb41bcc 100644 --- a/libgo/go/syscall/syscall.go +++ b/libgo/go/syscall/syscall.go @@ -28,7 +28,7 @@ package syscall import "unsafe" -//go:generate go run golang.org/x/sys/windows/mkwinsyscall -systemdll -output zsyscall_windows.go syscall_windows.go security_windows.go +//go:generate go run ./mksyscall_windows.go -systemdll -output zsyscall_windows.go syscall_windows.go security_windows.go // StringByteSlice converts a string to a NUL-terminated []byte, // If s contains a NUL byte this function panics instead of @@ -83,24 +83,22 @@ var dummy *byte const sizeofPtr uintptr = uintptr(unsafe.Sizeof(dummy)) -// Unix returns ts as the number of seconds and nanoseconds elapsed since the -// Unix epoch. +// Unix returns the time stored in ts as seconds plus nanoseconds. func (ts *Timespec) Unix() (sec int64, nsec int64) { return int64(ts.Sec), int64(ts.Nsec) } -// Unix returns tv as the number of seconds and nanoseconds elapsed since the -// Unix epoch. +// Unix returns the time stored in tv as seconds plus nanoseconds. func (tv *Timeval) Unix() (sec int64, nsec int64) { return int64(tv.Sec), int64(tv.Usec) * 1000 } -// Nano returns ts as the number of nanoseconds elapsed since the Unix epoch. +// Nano returns the time stored in ts as nanoseconds. func (ts *Timespec) Nano() int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } -// Nano returns tv as the number of nanoseconds elapsed since the Unix epoch. +// Nano returns the time stored in tv as nanoseconds. func (tv *Timeval) Nano() int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000 } diff --git a/libgo/go/syscall/syscall_errno.go b/libgo/go/syscall/syscall_errno.go index 0d781de..4df198e 100644 --- a/libgo/go/syscall/syscall_errno.go +++ b/libgo/go/syscall/syscall_errno.go @@ -7,7 +7,7 @@ package syscall import "internal/oserror" // An Errno is an unsigned number describing an error condition. -// It implements the error interface. The zero Errno is by convention +// It implements the error interface. The zero Errno is by convention // a non-error, so code to convert from Errno to error should use: // err = nil // if errno != 0 { @@ -18,7 +18,7 @@ import "internal/oserror" // using errors.Is. For example: // // _, _, err := syscall.Syscall(...) -// if errors.Is(err, os.ErrNotExist) ... +// if errors.Is(err, fs.ErrNotExist) ... type Errno uintptr func (e Errno) Error() string { diff --git a/libgo/go/syscall/syscall_js.go b/libgo/go/syscall/syscall_js.go index dfb4a27..0ab8c3f 100644 --- a/libgo/go/syscall/syscall_js.go +++ b/libgo/go/syscall/syscall_js.go @@ -49,7 +49,7 @@ const PathMax = 256 // using errors.Is. For example: // // _, _, err := syscall.Syscall(...) -// if errors.Is(err, os.ErrNotExist) ... +// if errors.Is(err, fs.ErrNotExist) ... type Errno uintptr func (e Errno) Error() string { diff --git a/libgo/go/syscall/syscall_linux.go b/libgo/go/syscall/syscall_linux.go new file mode 100644 index 0000000..3f32845 --- /dev/null +++ b/libgo/go/syscall/syscall_linux.go @@ -0,0 +1,34 @@ +// 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. + +package syscall + +// AllThreadsSyscall performs a syscall on each OS thread of the Go +// runtime. It first invokes the syscall on one thread. Should that +// invocation fail, it returns immediately with the error status. +// Otherwise, it invokes the syscall on all of the remaining threads +// in parallel. It will terminate the program if it observes any +// invoked syscall's return value differs from that of the first +// invocation. +// +// AllThreadsSyscall is intended for emulating simultaneous +// process-wide state changes that require consistently modifying +// per-thread state of the Go runtime. +// +// AllThreadsSyscall is unaware of any threads that are launched +// explicitly by cgo linked code, so the function always returns +// ENOTSUP in binaries that use cgo. +//go:uintptrescapes +func AllThreadsSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { + return minus1, minus1, ENOTSUP +} + +// AllThreadsSyscall6 is like AllThreadsSyscall, but extended to six +// arguments. +//go:uintptrescapes +func AllThreadsSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { + return minus1, minus1, ENOTSUP +} + +const minus1 = ^uintptr(0) diff --git a/libgo/go/syscall/syscall_linux_386.go b/libgo/go/syscall/syscall_linux_386.go index 9abcab9..5a9f6d9 100644 --- a/libgo/go/syscall/syscall_linux_386.go +++ b/libgo/go/syscall/syscall_linux_386.go @@ -4,9 +4,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP) -// so that go vet can check that they are correct. - package syscall import "unsafe" diff --git a/libgo/go/syscall/syscall_linux_test.go b/libgo/go/syscall/syscall_linux_test.go index c12df4c..91448fd 100644 --- a/libgo/go/syscall/syscall_linux_test.go +++ b/libgo/go/syscall/syscall_linux_test.go @@ -8,7 +8,7 @@ import ( "bufio" "fmt" "io" - "io/ioutil" + "io/fs" "os" "os/exec" "os/signal" @@ -29,7 +29,7 @@ func chtmpdir(t *testing.T) func() { if err != nil { t.Fatalf("chtmpdir: %v", err) } - d, err := ioutil.TempDir("", "test") + d, err := os.MkdirTemp("", "test") if err != nil { t.Fatalf("chtmpdir: %v", err) } @@ -159,7 +159,7 @@ func TestLinuxDeathSignal(t *testing.T) { // Copy the test binary to a location that a non-root user can read/execute // after we drop privileges - tempDir, err := ioutil.TempDir("", "TestDeathSignal") + tempDir, err := os.MkdirTemp("", "TestDeathSignal") if err != nil { t.Fatalf("cannot create temporary directory: %v", err) } @@ -324,7 +324,7 @@ func TestSyscallNoError(t *testing.T) { // Copy the test binary to a location that a non-root user can read/execute // after we drop privileges - tempDir, err := ioutil.TempDir("", "TestSyscallNoError") + tempDir, err := os.MkdirTemp("", "TestSyscallNoError") if err != nil { t.Fatalf("cannot create temporary directory: %v", err) } @@ -357,7 +357,7 @@ func TestSyscallNoError(t *testing.T) { t.Fatalf("failed to chown test binary %q, %v", tmpBinary, err) } - err = os.Chmod(tmpBinary, 0755|os.ModeSetuid) + err = os.Chmod(tmpBinary, 0755|fs.ModeSetuid) if err != nil { t.Fatalf("failed to set setuid bit on test binary %q, %v", tmpBinary, err) } @@ -402,3 +402,261 @@ func syscallNoError() { fmt.Println(uintptr(euid1), "/", int(e), "/", uintptr(euid2)) os.Exit(0) } + +// reference uapi/linux/prctl.h +const ( + PR_GET_KEEPCAPS uintptr = 7 + PR_SET_KEEPCAPS = 8 +) + +// TestAllThreadsSyscall tests that the go runtime can perform +// syscalls that execute on all OSThreads - with which to support +// POSIX semantics for security state changes. +func TestAllThreadsSyscall(t *testing.T) { + if _, _, err := syscall.AllThreadsSyscall(syscall.SYS_PRCTL, PR_SET_KEEPCAPS, 0, 0); err == syscall.ENOTSUP { + t.Skip("AllThreadsSyscall disabled with cgo") + } + + fns := []struct { + label string + fn func(uintptr) error + }{ + { + label: "prctl<3-args>", + fn: func(v uintptr) error { + _, _, e := syscall.AllThreadsSyscall(syscall.SYS_PRCTL, PR_SET_KEEPCAPS, v, 0) + if e != 0 { + return e + } + return nil + }, + }, + { + label: "prctl<6-args>", + fn: func(v uintptr) error { + _, _, e := syscall.AllThreadsSyscall6(syscall.SYS_PRCTL, PR_SET_KEEPCAPS, v, 0, 0, 0, 0) + if e != 0 { + return e + } + return nil + }, + }, + } + + waiter := func(q <-chan uintptr, r chan<- uintptr, once bool) { + for x := range q { + runtime.LockOSThread() + v, _, e := syscall.Syscall(syscall.SYS_PRCTL, PR_GET_KEEPCAPS, 0, 0) + if e != 0 { + t.Errorf("tid=%d prctl(PR_GET_KEEPCAPS) failed: %v", syscall.Gettid(), e) + } else if x != v { + t.Errorf("tid=%d prctl(PR_GET_KEEPCAPS) mismatch: got=%d want=%d", syscall.Gettid(), v, x) + } + r <- v + if once { + break + } + runtime.UnlockOSThread() + } + } + + // launches per fns member. + const launches = 11 + question := make(chan uintptr) + response := make(chan uintptr) + defer close(question) + + routines := 0 + for i, v := range fns { + for j := 0; j < launches; j++ { + // Add another goroutine - the closest thing + // we can do to encourage more OS thread + // creation - while the test is running. The + // actual thread creation may or may not be + // needed, based on the number of available + // unlocked OS threads at the time waiter + // calls runtime.LockOSThread(), but the goal + // of doing this every time through the loop + // is to race thread creation with v.fn(want) + // being executed. Via the once boolean we + // also encourage one in 5 waiters to return + // locked after participating in only one + // question response sequence. This allows the + // test to race thread destruction too. + once := routines%5 == 4 + go waiter(question, response, once) + + // Keep a count of how many goroutines are + // going to participate in the + // question/response test. This will count up + // towards 2*launches minus the count of + // routines that have been invoked with + // once=true. + routines++ + + // Decide what value we want to set the + // process-shared KEEPCAPS. Note, there is + // an explicit repeat of 0 when we change the + // variant of the syscall being used. + want := uintptr(j & 1) + + // Invoke the AllThreadsSyscall* variant. + if err := v.fn(want); err != nil { + t.Errorf("[%d,%d] %s(PR_SET_KEEPCAPS, %d, ...): %v", i, j, v.label, j&1, err) + } + + // At this point, we want all launched Go + // routines to confirm that they see the + // wanted value for KEEPCAPS. + for k := 0; k < routines; k++ { + question <- want + } + + // At this point, we should have a large + // number of locked OS threads all wanting to + // reply. + for k := 0; k < routines; k++ { + if got := <-response; got != want { + t.Errorf("[%d,%d,%d] waiter result got=%d, want=%d", i, j, k, got, want) + } + } + + // Provide an explicit opportunity for this Go + // routine to change Ms. + runtime.Gosched() + + if once { + // One waiter routine will have exited. + routines-- + } + + // Whatever M we are now running on, confirm + // we see the wanted value too. + if v, _, e := syscall.Syscall(syscall.SYS_PRCTL, PR_GET_KEEPCAPS, 0, 0); e != 0 { + t.Errorf("[%d,%d] prctl(PR_GET_KEEPCAPS) failed: %v", i, j, e) + } else if v != want { + t.Errorf("[%d,%d] prctl(PR_GET_KEEPCAPS) gave wrong value: got=%v, want=1", i, j, v) + } + } + } +} + +// compareStatus is used to confirm the contents of the thread +// specific status files match expectations. +func compareStatus(filter, expect string) error { + expected := filter + expect + pid := syscall.Getpid() + fs, err := os.ReadDir(fmt.Sprintf("/proc/%d/task", pid)) + if err != nil { + return fmt.Errorf("unable to find %d tasks: %v", pid, err) + } + expectedProc := fmt.Sprintf("Pid:\t%d", pid) + foundAThread := false + for _, f := range fs { + tf := fmt.Sprintf("/proc/%s/status", f.Name()) + d, err := os.ReadFile(tf) + if err != nil { + // There are a surprising number of ways this + // can error out on linux. We've seen all of + // the following, so treat any error here as + // equivalent to the "process is gone": + // os.IsNotExist(err), + // "... : no such process", + // "... : bad file descriptor. + continue + } + lines := strings.Split(string(d), "\n") + for _, line := range lines { + // Different kernel vintages pad differently. + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "Pid:\t") { + // On loaded systems, it is possible + // for a TID to be reused really + // quickly. As such, we need to + // validate that the thread status + // info we just read is a task of the + // same process PID as we are + // currently running, and not a + // recently terminated thread + // resurfaced in a different process. + if line != expectedProc { + break + } + // Fall through in the unlikely case + // that filter at some point is + // "Pid:\t". + } + if strings.HasPrefix(line, filter) { + if line != expected { + return fmt.Errorf("%q got:%q want:%q (bad) [pid=%d file:'%s' %v]\n", tf, line, expected, pid, string(d), expectedProc) + } + foundAThread = true + break + } + } + } + if !foundAThread { + return fmt.Errorf("found no thread /proc/<TID>/status files for process %q", expectedProc) + } + return nil +} + +// TestSetuidEtc performs tests on all of the wrapped system calls +// that mirror to the 9 glibc syscalls with POSIX semantics. The test +// here is considered authoritative and should compile and run +// CGO_ENABLED=0 or 1. Note, there is an extended copy of this same +// test in ../../misc/cgo/test/issue1435.go which requires +// CGO_ENABLED=1 and launches pthreads from C that run concurrently +// with the Go code of the test - and the test validates that these +// pthreads are also kept in sync with the security state changed with +// the syscalls. Care should be taken to mirror any enhancements to +// this test here in that file too. +func TestSetuidEtc(t *testing.T) { + if syscall.Getuid() != 0 { + t.Skip("skipping root only test") + } + vs := []struct { + call string + fn func() error + filter, expect string + }{ + {call: "Setegid(1)", fn: func() error { return syscall.Setegid(1) }, filter: "Gid:", expect: "\t0\t1\t0\t1"}, + {call: "Setegid(0)", fn: func() error { return syscall.Setegid(0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"}, + + {call: "Seteuid(1)", fn: func() error { return syscall.Seteuid(1) }, filter: "Uid:", expect: "\t0\t1\t0\t1"}, + {call: "Setuid(0)", fn: func() error { return syscall.Setuid(0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"}, + + {call: "Setgid(1)", fn: func() error { return syscall.Setgid(1) }, filter: "Gid:", expect: "\t1\t1\t1\t1"}, + {call: "Setgid(0)", fn: func() error { return syscall.Setgid(0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"}, + + {call: "Setgroups([]int{0,1,2,3})", fn: func() error { return syscall.Setgroups([]int{0, 1, 2, 3}) }, filter: "Groups:", expect: "\t0 1 2 3"}, + {call: "Setgroups(nil)", fn: func() error { return syscall.Setgroups(nil) }, filter: "Groups:", expect: ""}, + {call: "Setgroups([]int{0})", fn: func() error { return syscall.Setgroups([]int{0}) }, filter: "Groups:", expect: "\t0"}, + + {call: "Setregid(101,0)", fn: func() error { return syscall.Setregid(101, 0) }, filter: "Gid:", expect: "\t101\t0\t0\t0"}, + {call: "Setregid(0,102)", fn: func() error { return syscall.Setregid(0, 102) }, filter: "Gid:", expect: "\t0\t102\t102\t102"}, + {call: "Setregid(0,0)", fn: func() error { return syscall.Setregid(0, 0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"}, + + {call: "Setreuid(1,0)", fn: func() error { return syscall.Setreuid(1, 0) }, filter: "Uid:", expect: "\t1\t0\t0\t0"}, + {call: "Setreuid(0,2)", fn: func() error { return syscall.Setreuid(0, 2) }, filter: "Uid:", expect: "\t0\t2\t2\t2"}, + {call: "Setreuid(0,0)", fn: func() error { return syscall.Setreuid(0, 0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"}, + + {call: "Setresgid(101,0,102)", fn: func() error { return syscall.Setresgid(101, 0, 102) }, filter: "Gid:", expect: "\t101\t0\t102\t0"}, + {call: "Setresgid(0,102,101)", fn: func() error { return syscall.Setresgid(0, 102, 101) }, filter: "Gid:", expect: "\t0\t102\t101\t102"}, + {call: "Setresgid(0,0,0)", fn: func() error { return syscall.Setresgid(0, 0, 0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"}, + + {call: "Setresuid(1,0,2)", fn: func() error { return syscall.Setresuid(1, 0, 2) }, filter: "Uid:", expect: "\t1\t0\t2\t0"}, + {call: "Setresuid(0,2,1)", fn: func() error { return syscall.Setresuid(0, 2, 1) }, filter: "Uid:", expect: "\t0\t2\t1\t2"}, + {call: "Setresuid(0,0,0)", fn: func() error { return syscall.Setresuid(0, 0, 0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"}, + } + + for i, v := range vs { + if err := v.fn(); err != nil { + t.Errorf("[%d] %q failed: %v", i, v.call, err) + continue + } + if err := compareStatus(v.filter, v.expect); err != nil { + t.Errorf("[%d] %q comparison: %v", i, v.call, err) + } + } +} diff --git a/libgo/go/syscall/syscall_unix.go b/libgo/go/syscall/syscall_unix.go index 4d963fb..db6b3aa 100644 --- a/libgo/go/syscall/syscall_unix.go +++ b/libgo/go/syscall/syscall_unix.go @@ -21,7 +21,7 @@ var ( ) const ( - darwin64Bit = runtime.GOOS == "darwin" && sizeofPtr == 8 + darwin64Bit = (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && sizeofPtr == 8 netbsd32Bit = runtime.GOOS == "netbsd" && sizeofPtr == 4 ) diff --git a/libgo/go/syscall/syscall_unix_test.go b/libgo/go/syscall/syscall_unix_test.go index a6b70d4..e4b4171 100644 --- a/libgo/go/syscall/syscall_unix_test.go +++ b/libgo/go/syscall/syscall_unix_test.go @@ -11,7 +11,6 @@ import ( "fmt" "internal/testenv" "io" - "io/ioutil" "net" "os" "os/exec" @@ -72,7 +71,7 @@ func _() { // Thus this test also verifies that the Flock_t structure can be // roundtripped with F_SETLK and F_GETLK. func TestFcntlFlock(t *testing.T) { - if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" { + if runtime.GOOS == "ios" { t.Skip("skipping; no child processes allowed on iOS") } flock := syscall.Flock_t{ @@ -81,7 +80,7 @@ func TestFcntlFlock(t *testing.T) { } if os.Getenv("GO_WANT_HELPER_PROCESS") == "" { // parent - tempDir, err := ioutil.TempDir("", "TestFcntlFlock") + tempDir, err := os.MkdirTemp("", "TestFcntlFlock") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) } @@ -159,7 +158,7 @@ func TestPassFD(t *testing.T) { } - tempDir, err := ioutil.TempDir("", "TestPassFD") + tempDir, err := os.MkdirTemp("", "TestPassFD") if err != nil { t.Fatal(err) } @@ -227,7 +226,7 @@ func TestPassFD(t *testing.T) { f := os.NewFile(uintptr(gotFds[0]), "fd-from-child") defer f.Close() - got, err := ioutil.ReadAll(f) + got, err := io.ReadAll(f) want := "Hello from child process!\n" if string(got) != want { t.Errorf("child process ReadAll: %q, %v; want %q", got, err, want) @@ -259,7 +258,7 @@ func passFDChild() { // We make it in tempDir, which our parent will clean up. flag.Parse() tempDir := flag.Arg(0) - f, err := ioutil.TempFile(tempDir, "") + f, err := os.CreateTemp(tempDir, "") if err != nil { fmt.Printf("TempFile: %v", err) return @@ -338,11 +337,11 @@ func TestRlimit(t *testing.T) { } set := rlimit set.Cur = set.Max - 1 - if runtime.GOOS == "darwin" && set.Cur > 10240 { - // The max file limit is 10240, even though - // the max returned by Getrlimit is 1<<63-1. - // This is OPEN_MAX in sys/syslimits.h. - set.Cur = 10240 + if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && set.Cur > 4096 { + // rlim_min for RLIMIT_NOFILE should be equal to + // or lower than kern.maxfilesperproc, which on + // some machines are 4096. See #40564. + set.Cur = 4096 } err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &set) if err != nil { @@ -355,8 +354,8 @@ func TestRlimit(t *testing.T) { } set = rlimit set.Cur = set.Max - 1 - if runtime.GOOS == "darwin" && set.Cur > 10240 { - set.Cur = 10240 + if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && set.Cur > 4096 { + set.Cur = 4096 } if set != get { t.Fatalf("Rlimit: change failed: wanted %#v got %#v", set, get) diff --git a/libgo/go/syscall/timestruct.go b/libgo/go/syscall/timestruct.go index bd0b3de..42a743e 100644 --- a/libgo/go/syscall/timestruct.go +++ b/libgo/go/syscall/timestruct.go @@ -6,12 +6,10 @@ package syscall -// TimespecToNsec converts a Timespec value into a number of -// nanoseconds since the Unix epoch. +// TimespecToNSec returns the time stored in ts as nanoseconds. func TimespecToNsec(ts Timespec) int64 { return ts.Nano() } -// NsecToTimespec takes a number of nanoseconds since the Unix epoch -// and returns the corresponding Timespec value. +// NsecToTimespec converts a number of nanoseconds into a Timespec. func NsecToTimespec(nsec int64) Timespec { sec := nsec / 1e9 nsec = nsec % 1e9 @@ -22,12 +20,10 @@ func NsecToTimespec(nsec int64) Timespec { return setTimespec(sec, nsec) } -// TimevalToNsec converts a Timeval value into a number of nanoseconds -// since the Unix epoch. +// TimevalToNsec returns the time stored in tv as nanoseconds. func TimevalToNsec(tv Timeval) int64 { return tv.Nano() } -// NsecToTimeval takes a number of nanoseconds since the Unix epoch -// and returns the corresponding Timeval value. +// NsecToTimeval converts a number of nanoseconds into a Timeval. func NsecToTimeval(nsec int64) Timeval { nsec += 999 // round up to microsecond usec := nsec % 1e9 / 1e3 |