]> Cypherpunks.ru repositories - gostls13.git/commitdiff
[release-branch.go1.20] runtime: implement SUID/SGID protections
authorRoland Shoemaker <bracewell@google.com>
Tue, 9 May 2023 18:47:57 +0000 (11:47 -0700)
committerGopher Robot <gobot@golang.org>
Tue, 6 Jun 2023 17:01:31 +0000 (17:01 +0000)
On Unix platforms, the runtime previously did nothing special when a
program was run with either the SUID or SGID bits set. This can be
dangerous in certain cases, such as when dumping memory state, or
assuming the status of standard i/o file descriptors.

Taking cues from glibc, this change implements a set of protections when
a binary is run with SUID or SGID bits set (or is SUID/SGID-like). On
Linux, whether to enable these protections is determined by whether the
AT_SECURE flag is passed in the auxiliary vector. On platforms which
have the issetugid syscall (the BSDs, darwin, and Solaris/Illumos), that
is used. On the remaining platforms (currently only AIX) we check
!(getuid() == geteuid() && getgid == getegid()).

Currently when we determine a binary is "tainted" (using the glibc
terminology), we implement two specific protections:
  1. we check if the file descriptors 0, 1, and 2 are open, and if they
     are not, we open them, pointing at /dev/null (or fail).
  2. we force GOTRACKBACK=none, and generally prevent dumping of
     trackbacks and registers when a program panics/aborts.

In the future we may add additional protections.

This change requires implementing issetugid on the platforms which
support it, and implementing getuid, geteuid, getgid, and getegid on
AIX.

Thanks to Vincent Dehors from Synacktiv for reporting this issue.

Updates #60272
Fixes #60518
Fixes CVE-2023-29403

Change-Id: Icb620f3f8755791d51b02b5c07fb24f40e19cb80
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1878434
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Roland Shoemaker <bracewell@google.com>
Reviewed-by: Russ Cox <rsc@google.com>
(cherry picked from commit 87065663ea6d89cd54f65a515d8f2ed0ef285c19)
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1902232
TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com>
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1904344
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-on: https://go-review.googlesource.com/c/go/+/501227
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: David Chase <drchase@google.com>
Auto-Submit: Michael Knyszek <mknyszek@google.com>

40 files changed:
src/runtime/extern.go
src/runtime/os2_aix.go
src/runtime/os_aix.go
src/runtime/os_dragonfly.go
src/runtime/os_freebsd.go
src/runtime/os_linux.go
src/runtime/os_netbsd.go
src/runtime/os_openbsd_syscall2.go
src/runtime/os_solaris.go
src/runtime/panic.go
src/runtime/proc.go
src/runtime/security_aix.go [new file with mode: 0644]
src/runtime/security_issetugid.go [new file with mode: 0644]
src/runtime/security_linux.go [new file with mode: 0644]
src/runtime/security_nonunix.go [new file with mode: 0644]
src/runtime/security_test.go [new file with mode: 0644]
src/runtime/security_unix.go [new file with mode: 0644]
src/runtime/signal_unix.go
src/runtime/sys_darwin.go
src/runtime/sys_darwin_amd64.s
src/runtime/sys_darwin_arm64.s
src/runtime/sys_dragonfly_amd64.s
src/runtime/sys_freebsd_386.s
src/runtime/sys_freebsd_amd64.s
src/runtime/sys_freebsd_arm.s
src/runtime/sys_freebsd_arm64.s
src/runtime/sys_freebsd_riscv64.s
src/runtime/sys_netbsd_386.s
src/runtime/sys_netbsd_amd64.s
src/runtime/sys_netbsd_arm.s
src/runtime/sys_netbsd_arm64.s
src/runtime/sys_openbsd2.go
src/runtime/sys_openbsd_386.s
src/runtime/sys_openbsd_amd64.s
src/runtime/sys_openbsd_arm.s
src/runtime/sys_openbsd_arm64.s
src/runtime/sys_openbsd_mips64.s
src/runtime/syscall2_solaris.go
src/runtime/syscall_solaris.go
src/runtime/testdata/testsuid/main.go [new file with mode: 0644]

index 6c41c626945a93d01000eafc71486f7713075628..afadc3d17ec341f267750eb02491f2a33d063b50 100644 (file)
@@ -216,6 +216,25 @@ the set of Go environment variables. They influence the building of Go programs
 GOARCH, GOOS, and GOROOT are recorded at compile time and made available by
 constants or functions in this package, but they do not influence the execution
 of the run-time system.
+
+# Security
+
+On Unix platforms, Go's runtime system behaves slightly differently when a
+binary is setuid/setgid or executed with setuid/setgid-like properties, in order
+to prevent dangerous behaviors. On Linux this is determined by checking for the
+AT_SECURE flag in the auxiliary vector, on the BSDs and Solaris/Illumos it is
+determined by checking the issetugid syscall, and on AIX it is determined by
+checking if the uid/gid match the effective uid/gid.
+
+When the runtime determines the binary is setuid/setgid-like, it does three main
+things:
+  - The standard input/output file descriptors (0, 1, 2) are checked to be open.
+    If any of them are closed, they are opened pointing at /dev/null.
+  - The value of the GOTRACEBACK environment variable is set to 'none'.
+  - When a signal is received that terminates the program, or the program
+    encounters an unrecoverable panic that would otherwise override the value
+    of GOTRACEBACK, the goroutine stack, registers, and other memory related
+    information are omitted.
 */
 package runtime
 
index 2efc56554cddcceddbb418e91c5fc7a9d7df9289..0e39b85c42d459ceca1210fc5d7bbbea18431373 100644 (file)
@@ -55,6 +55,10 @@ var (
 //go:cgo_import_dynamic libc_sysconf sysconf "libc.a/shr_64.o"
 //go:cgo_import_dynamic libc_usleep usleep "libc.a/shr_64.o"
 //go:cgo_import_dynamic libc_write write "libc.a/shr_64.o"
+//go:cgo_import_dynamic libc_getuid getuid "libc.a/shr_64.o"
+//go:cgo_import_dynamic libc_geteuid geteuid "libc.a/shr_64.o"
+//go:cgo_import_dynamic libc_getgid getgid "libc.a/shr_64.o"
+//go:cgo_import_dynamic libc_getegid getegid "libc.a/shr_64.o"
 
 //go:cgo_import_dynamic libpthread___pth_init __pth_init "libpthread.a/shr_xpg5_64.o"
 //go:cgo_import_dynamic libpthread_attr_destroy pthread_attr_destroy "libpthread.a/shr_xpg5_64.o"
@@ -95,6 +99,10 @@ var (
 //go:linkname libc_sysconf libc_sysconf
 //go:linkname libc_usleep libc_usleep
 //go:linkname libc_write libc_write
+//go:linkname libc_getuid libc_getuid
+//go:linkname libc_geteuid libc_geteuid
+//go:linkname libc_getgid libc_getgid
+//go:linkname libc_getegid libc_getegid
 
 //go:linkname libpthread___pth_init libpthread___pth_init
 //go:linkname libpthread_attr_destroy libpthread_attr_destroy
@@ -137,6 +145,10 @@ var (
        libc_sysconf,
        libc_usleep,
        libc_write,
+       libc_getuid,
+       libc_geteuid,
+       libc_getgid,
+       libc_getegid,
        //libpthread
        libpthread___pth_init,
        libpthread_attr_destroy,
index 7845de1470776f38fc4cbd8213ff0a65276d3635..ad96ac36f8070bb88d4eb19d02c40eff86b31075 100644 (file)
@@ -378,3 +378,43 @@ const sigPerThreadSyscall = 1 << 31
 func runPerThreadSyscall() {
        throw("runPerThreadSyscall only valid on linux")
 }
+
+//go:nosplit
+func getuid() int32 {
+       r, errno := syscall0(&libc_getuid)
+       if errno != 0 {
+               print("getuid failed ", errno)
+               throw("getuid")
+       }
+       return int32(r)
+}
+
+//go:nosplit
+func geteuid() int32 {
+       r, errno := syscall0(&libc_geteuid)
+       if errno != 0 {
+               print("geteuid failed ", errno)
+               throw("geteuid")
+       }
+       return int32(r)
+}
+
+//go:nosplit
+func getgid() int32 {
+       r, errno := syscall0(&libc_getgid)
+       if errno != 0 {
+               print("getgid failed ", errno)
+               throw("getgid")
+       }
+       return int32(r)
+}
+
+//go:nosplit
+func getegid() int32 {
+       r, errno := syscall0(&libc_getegid)
+       if errno != 0 {
+               print("getegid failed ", errno)
+               throw("getegid")
+       }
+       return int32(r)
+}
index 460dc88abec4467987656c04d1c191d889143130..c14d904e7f0280c86c5139368cdcade170d7396c 100644 (file)
@@ -66,6 +66,8 @@ func pipe2(flags int32) (r, w int32, errno int32)
 func fcntl(fd, cmd, arg int32) (ret int32, errno int32)
 func closeonexec(fd int32)
 
+func issetugid() int32
+
 // From DragonFly's <sys/sysctl.h>
 const (
        _CTL_HW      = 6
index e77507b70d1cc1d6b3f89fbaee29879d7da4a965..a7288c5653d8660da2a127c7eb96ac505a557005 100644 (file)
@@ -51,6 +51,8 @@ func pipe2(flags int32) (r, w int32, errno int32)
 func fcntl(fd, cmd, arg int32) (ret int32, errno int32)
 func closeonexec(fd int32)
 
+func issetugid() int32
+
 // From FreeBSD's <sys/sysctl.h>
 const (
        _CTL_HW      = 6
index e2581fba6548b35c2f9cf58da1b679b802156afb..26db4a0cd944590820e976708e02aac964aeb9a2 100644 (file)
@@ -216,6 +216,7 @@ const (
        _AT_NULL   = 0  // End of vector
        _AT_PAGESZ = 6  // System physical page size
        _AT_HWCAP  = 16 // hardware capability bit vector
+       _AT_SECURE = 23 // secure mode boolean
        _AT_RANDOM = 25 // introduced in 2.6.29
        _AT_HWCAP2 = 26 // hardware capability bit vector 2
 )
@@ -285,6 +286,9 @@ func sysargs(argc int32, argv **byte) {
 // the ELF AT_RANDOM auxiliary vector.
 var startupRandomData []byte
 
+// secureMode holds the value of AT_SECURE passed in the auxiliary vector.
+var secureMode bool
+
 func sysauxv(auxv []uintptr) int {
        var i int
        for ; auxv[i] != _AT_NULL; i += 2 {
@@ -297,6 +301,9 @@ func sysauxv(auxv []uintptr) int {
 
                case _AT_PAGESZ:
                        physPageSize = val
+
+               case _AT_SECURE:
+                       secureMode = val == 1
                }
 
                archauxv(tag, val)
index d77855ec0cdd80796dd15be8f99cb9867b4e6bd5..d3ae1f810aa10acda1274f3d92406ebfb2140409 100644 (file)
@@ -82,6 +82,8 @@ func pipe2(flags int32) (r, w int32, errno int32)
 func fcntl(fd, cmd, arg int32) (ret int32, errno int32)
 func closeonexec(fd int32)
 
+func issetugid() int32
+
 const (
        _ESRCH     = 3
        _ETIMEDOUT = 60
index dcf3b09e6780a86724e96aeb3d072c94cfceb320..8e4859389da2c16b55257d943d6a42b4ed9a5c25 100644 (file)
@@ -99,3 +99,5 @@ func fcntl(fd, cmd, arg int32) (ret int32, errno int32)
 func closeonexec(fd int32)
 
 func walltime() (sec int64, nsec int32)
+
+func issetugid() int32
index 9e13c2903afe37c5c49a1870111603a93b28f07a..47edda15978f97e6e53d223215bbf3f3bde20845 100644 (file)
@@ -267,3 +267,7 @@ func sysvicall6(fn *libcFunc, a1, a2, a3, a4, a5, a6 uintptr) uintptr {
        }
        return libcall.r1
 }
+
+func issetugid() int32 {
+       return int32(sysvicall0(&libc_issetugid))
+}
index 26618db7ceb470473bacdfa2d8969f63b258779f..6a6437de249964ec7a2b48d628621809400030ea 100644 (file)
@@ -1118,6 +1118,10 @@ func fatalthrow(t throwType) {
        // Switch to the system stack to avoid any stack growth, which may make
        // things worse if the runtime is in a bad state.
        systemstack(func() {
+               if isSecureMode() {
+                       exit(2)
+               }
+
                startpanic_m()
 
                if dopanic_m(gp, pc, sp) {
index 554a60d7473eb2a9fdd9411978847f08a879b681..7cd6898a9352a691b6dfaf0b3ccd1d0e6a2e80fe 100644 (file)
@@ -726,6 +726,7 @@ func schedinit() {
 
        goargs()
        goenvs()
+       secure()
        parsedebugvars()
        gcinit()
 
diff --git a/src/runtime/security_aix.go b/src/runtime/security_aix.go
new file mode 100644 (file)
index 0000000..c11b9c3
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2023 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 runtime
+
+// secureMode is only ever mutated in schedinit, so we don't need to worry about
+// synchronization primitives.
+var secureMode bool
+
+func initSecureMode() {
+       secureMode = !(getuid() == geteuid() && getgid() == getegid())
+}
+
+func isSecureMode() bool {
+       return secureMode
+}
diff --git a/src/runtime/security_issetugid.go b/src/runtime/security_issetugid.go
new file mode 100644 (file)
index 0000000..5048632
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright 2023 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.
+
+//go:build darwin || dragonfly || freebsd || illumos || netbsd || openbsd || solaris
+
+package runtime
+
+// secureMode is only ever mutated in schedinit, so we don't need to worry about
+// synchronization primitives.
+var secureMode bool
+
+func initSecureMode() {
+       secureMode = issetugid() == 1
+}
+
+func isSecureMode() bool {
+       return secureMode
+}
diff --git a/src/runtime/security_linux.go b/src/runtime/security_linux.go
new file mode 100644 (file)
index 0000000..181f3a1
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2023 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 runtime
+
+import _ "unsafe"
+
+func initSecureMode() {
+       // We have already initialized the secureMode bool in sysauxv.
+}
+
+func isSecureMode() bool {
+       return secureMode
+}
diff --git a/src/runtime/security_nonunix.go b/src/runtime/security_nonunix.go
new file mode 100644 (file)
index 0000000..fc9571c
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2023 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.
+
+//go:build !unix
+
+package runtime
+
+func isSecureMode() bool {
+       return false
+}
+
+func secure() {}
diff --git a/src/runtime/security_test.go b/src/runtime/security_test.go
new file mode 100644 (file)
index 0000000..1d30411
--- /dev/null
@@ -0,0 +1,143 @@
+// Copyright 2023 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.
+
+//go:build unix
+
+package runtime_test
+
+import (
+       "bytes"
+       "context"
+       "fmt"
+       "internal/testenv"
+       "io"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "runtime"
+       "strings"
+       "testing"
+       "time"
+)
+
+func privesc(command string, args ...string) error {
+       ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
+       defer cancel()
+       var cmd *exec.Cmd
+       if runtime.GOOS == "darwin" {
+               cmd = exec.CommandContext(ctx, "sudo", append([]string{"-n", command}, args...)...)
+       } else {
+               cmd = exec.CommandContext(ctx, "su", highPrivUser, "-c", fmt.Sprintf("%s %s", command, strings.Join(args, " ")))
+       }
+       _, err := cmd.CombinedOutput()
+       return err
+}
+
+const highPrivUser = "root"
+
+func setSetuid(t *testing.T, user, bin string) {
+       t.Helper()
+       // We escalate privileges here even if we are root, because for some reason on some builders
+       // (at least freebsd-amd64-13_0) the default PATH doesn't include /usr/sbin, which is where
+       // chown lives, but using 'su root -c' gives us the correct PATH.
+
+       // buildTestProg uses os.MkdirTemp which creates directories with 0700, which prevents
+       // setuid binaries from executing because of the missing g+rx, so we need to set the parent
+       // directory to better permissions before anything else. We created this directory, so we
+       // shouldn't need to do any privilege trickery.
+       if err := privesc("chmod", "0777", filepath.Dir(bin)); err != nil {
+               t.Skipf("unable to set permissions on %q, likely no passwordless sudo/su: %s", filepath.Dir(bin), err)
+       }
+
+       if err := privesc("chown", user, bin); err != nil {
+               t.Skipf("unable to set permissions on test binary, likely no passwordless sudo/su: %s", err)
+       }
+       if err := privesc("chmod", "u+s", bin); err != nil {
+               t.Skipf("unable to set permissions on test binary, likely no passwordless sudo/su: %s", err)
+       }
+}
+
+func TestSUID(t *testing.T) {
+       // This test is relatively simple, we build a test program which opens a
+       // file passed via the TEST_OUTPUT envvar, prints the value of the
+       // GOTRACEBACK envvar to stdout, and prints "hello" to stderr. We then chown
+       // the program to "nobody" and set u+s on it. We execute the program, only
+       // passing it two files, for stdin and stdout, and passing
+       // GOTRACEBACK=system in the env.
+       //
+       // We expect that the program will trigger the SUID protections, resetting
+       // the value of GOTRACEBACK, and opening the missing stderr descriptor, such
+       // that the program prints "GOTRACEBACK=none" to stdout, and nothing gets
+       // written to the file pointed at by TEST_OUTPUT.
+
+       if *flagQuick {
+               t.Skip("-quick")
+       }
+
+       testenv.MustHaveGoBuild(t)
+
+       helloBin, err := buildTestProg(t, "testsuid")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       f, err := os.CreateTemp(t.TempDir(), "suid-output")
+       if err != nil {
+               t.Fatal(err)
+       }
+       tempfilePath := f.Name()
+       f.Close()
+
+       lowPrivUser := "nobody"
+       setSetuid(t, lowPrivUser, helloBin)
+
+       b := bytes.NewBuffer(nil)
+       pr, pw, err := os.Pipe()
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       proc, err := os.StartProcess(helloBin, []string{helloBin}, &os.ProcAttr{
+               Env:   []string{"GOTRACEBACK=system", "TEST_OUTPUT=" + tempfilePath},
+               Files: []*os.File{os.Stdin, pw},
+       })
+       if err != nil {
+               if os.IsPermission(err) {
+                       t.Skip("don't have execute permission on setuid binary, possibly directory permission issue?")
+               }
+               t.Fatal(err)
+       }
+       done := make(chan bool, 1)
+       go func() {
+               io.Copy(b, pr)
+               pr.Close()
+               done <- true
+       }()
+       ps, err := proc.Wait()
+       if err != nil {
+               t.Fatal(err)
+       }
+       pw.Close()
+       <-done
+       output := b.String()
+
+       if ps.ExitCode() == 99 {
+               t.Skip("binary wasn't setuid (uid == euid), unable to effectively test")
+       }
+
+       expected := "GOTRACEBACK=none\n"
+       if output != expected {
+               t.Errorf("unexpected output, got: %q, want %q", output, expected)
+       }
+
+       fc, err := os.ReadFile(tempfilePath)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if string(fc) != "" {
+               t.Errorf("unexpected file content, got: %q", string(fc))
+       }
+
+       // TODO: check the registers aren't leaked?
+}
diff --git a/src/runtime/security_unix.go b/src/runtime/security_unix.go
new file mode 100644 (file)
index 0000000..16fc87e
--- /dev/null
@@ -0,0 +1,72 @@
+// Copyright 2023 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.
+
+//go:build unix
+
+package runtime
+
+func secure() {
+       initSecureMode()
+
+       if !isSecureMode() {
+               return
+       }
+
+       // When secure mode is enabled, we do two things:
+       //   1. ensure the file descriptors 0, 1, and 2 are open, and if not open them,
+       //      pointing at /dev/null (or fail)
+       //   2. enforce specific environment variable values (currently we only force
+       //              GOTRACEBACK=none)
+       //
+       // Other packages may also disable specific functionality when secure mode
+       // is enabled (determined by using linkname to call isSecureMode).
+       //
+       // NOTE: we may eventually want to enforce (1) regardless of whether secure
+       // mode is enabled or not.
+
+       secureFDs()
+       secureEnv()
+}
+
+func secureEnv() {
+       var hasTraceback bool
+       for i := 0; i < len(envs); i++ {
+               if hasPrefix(envs[i], "GOTRACEBACK=") {
+                       hasTraceback = true
+                       envs[i] = "GOTRACEBACK=none"
+               }
+       }
+       if !hasTraceback {
+               envs = append(envs, "GOTRACEBACK=none")
+       }
+}
+
+func secureFDs() {
+       const (
+               // F_GETFD and EBADF are standard across all unixes, define
+               // them here rather than in each of the OS specific files
+               F_GETFD = 0x01
+               EBADF   = 0x09
+       )
+
+       devNull := []byte("/dev/null\x00")
+       for i := 0; i < 3; i++ {
+               ret, errno := fcntl(int32(i), F_GETFD, 0)
+               if ret >= 0 {
+                       continue
+               }
+               if errno != EBADF {
+                       print("runtime: unexpected error while checking standard file descriptor ", i, ", errno=", errno, "\n")
+                       throw("cannot secure fds")
+               }
+
+               if ret := open(&devNull[0], 2 /* O_RDWR */, 0); ret < 0 {
+                       print("runtime: standard file descriptor ", i, " closed, unable to open /dev/null, errno=", errno, "\n")
+                       throw("cannot secure fds")
+               } else if ret != int32(i) {
+                       print("runtime: opened unexpected file descriptor ", ret, " when attempting to open ", i, "\n")
+                       throw("cannot secure fds")
+               }
+       }
+}
index c401fc1b7a572ba95f10c6ad5833c1799ac6bec0..c1abe62cb3ca30fc225873f2a471c0648ad4653a 100644 (file)
@@ -725,6 +725,10 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
                print("Signal ", sig, "\n")
        }
 
+       if isSecureMode() {
+               exit(2)
+       }
+
        print("PC=", hex(c.sigpc()), " m=", mp.id, " sigcode=", c.sigcode(), "\n")
        if mp.incgo && gp == mp.g0 && mp.curg != nil {
                print("signal arrived during cgo execution\n")
index a56a9373b95cbedbb3d0c376c0f3af923714e077..64d7523f247c8641c8886cdc74a2d66af5ab0a0d 100644 (file)
@@ -549,6 +549,11 @@ func setNonblock(fd int32) {
        }
 }
 
+func issetugid() int32 {
+       return libcCall(unsafe.Pointer(abi.FuncPCABI0(issetugid_trampoline)), nil)
+}
+func issetugid_trampoline()
+
 // Tell the linker that the libc_* functions are to be found
 // in a system library, with the libc_ prefix missing.
 
@@ -599,3 +604,5 @@ func setNonblock(fd int32) {
 
 //go:cgo_import_dynamic libc_notify_is_valid_token notify_is_valid_token "/usr/lib/libSystem.B.dylib"
 //go:cgo_import_dynamic libc_xpc_date_create_from_current xpc_date_create_from_current "/usr/lib/libSystem.B.dylib"
+
+//go:cgo_import_dynamic libc_issetugid issetugid "/usr/lib/libSystem.B.dylib"
index 2a2aeff77d19f437944da3316c608170ba1984ae..de7ecdfc950dc2335e35c51cc3bba7c995871f90 100644 (file)
@@ -943,3 +943,10 @@ TEXT runtime·syscall_x509(SB),NOSPLIT,$0
        MOVQ    BP, SP
        POPQ    BP
        RET
+
+TEXT runtime·issetugid_trampoline(SB),NOSPLIT,$0
+       PUSHQ   BP
+       MOVQ    SP, BP
+       CALL    libc_issetugid(SB)
+       POPQ    BP
+       RET
index e1c61fae55ba105ef110dc67052ce0ec390b5ed1..dc6caf873b1d6a4fb2e6fe09f72cd74e2149f42c 100644 (file)
@@ -763,3 +763,7 @@ TEXT runtime·syscall_x509(SB),NOSPLIT,$0
        ADD     $16, RSP
        MOVD    R0, 56(R2)      // save r1
        RET
+
+TEXT runtime·issetugid_trampoline(SB),NOSPLIT,$0
+       BL      libc_issetugid(SB)
+       RET
index 958d712cd3d707ff45ae3ddbcbe6e315b4ab3d79..08f99ca963d78b650ade6fb0ac25c253f5ce5639 100644 (file)
@@ -411,3 +411,13 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
        MOVL    $92, AX         // fcntl
        SYSCALL
        RET
+
+// func issetugid() int32
+TEXT runtime·issetugid(SB),NOSPLIT,$0
+       MOVQ    $0, DI
+       MOVQ    $0, SI
+       MOVQ    $0, DX
+       MOVL    $253, AX
+       SYSCALL
+       MOVL    AX, ret+0(FP)
+       RET
index a5a668cb7003c521d8a11109e7065d0b1c280eee..df0c0739067b68af7d429a6532e589af48ee76ff 100644 (file)
@@ -31,6 +31,7 @@
 #define SYS___sysctl           202
 #define SYS_clock_gettime      232
 #define SYS_nanosleep          240
+#define SYS_issetugid          253
 #define SYS_sched_yield                331
 #define SYS_sigprocmask                340
 #define SYS_kqueue             362
@@ -487,3 +488,10 @@ TEXT runtime·cpuset_getaffinity(SB), NOSPLIT, $0-28
        RET
 
 GLOBL runtime·tlsoffset(SB),NOPTR,$4
+
+// func issetugid() int32
+TEXT runtime·issetugid(SB),NOSPLIT,$0
+       MOVL    $SYS_issetugid, AX
+       INT     $0x80
+       MOVL    AX, ret+0(FP)
+       RET
index bd3c60ec19cc514c1be005fde8bb54e1e9b6247c..95bf07e4f0fbcf15366ef66d567942c8d4d11467 100644 (file)
@@ -33,6 +33,7 @@
 #define SYS___sysctl           202
 #define SYS_clock_gettime      232
 #define SYS_nanosleep          240
+#define SYS_issetugid          253
 #define SYS_sched_yield                331
 #define SYS_sigprocmask                340
 #define SYS_kqueue             362
@@ -588,3 +589,13 @@ TEXT runtime·cpuset_getaffinity(SB), NOSPLIT, $0-44
        NEGQ    AX
        MOVL    AX, ret+40(FP)
        RET
+
+// func issetugid() int32
+TEXT runtime·issetugid(SB),NOSPLIT,$0
+       MOVQ    $0, DI
+       MOVQ    $0, SI
+       MOVQ    $0, DX
+       MOVL    $SYS_issetugid, AX
+       SYSCALL
+       MOVL    AX, ret+0(FP)
+       RET
index 9b09d9d349fec6dd1ebc52ff1b52218779efd994..bd2e7058aa08b39506b52aae27dd6d649cf7cfb3 100644 (file)
@@ -27,6 +27,7 @@
 #define SYS_fcntl (SYS_BASE + 92)
 #define SYS___sysctl (SYS_BASE + 202)
 #define SYS_nanosleep (SYS_BASE + 240)
+#define SYS_issetugid (SYS_BASE + 253)
 #define SYS_clock_gettime (SYS_BASE + 232)
 #define SYS_sched_yield (SYS_BASE + 331)
 #define SYS_sigprocmask (SYS_BASE + 340)
@@ -455,3 +456,10 @@ TEXT runtime·getCntxct(SB),NOSPLIT|NOFRAME,$0-8
 
        MOVW    R0, ret+4(FP)
        RET
+
+// func issetugid() int32
+TEXT runtime·issetugid(SB),NOSPLIT,$0
+       MOVW $SYS_issetugid, R7
+       SWI $0
+       MOVW    R0, ret+0(FP)
+       RET
index 36c106088e0d712a051b9a53dad8c8a79c572280..fe430622703a482350786365d80fe8bb9d4e6db4 100644 (file)
@@ -34,6 +34,7 @@
 #define SYS_fcntl              92
 #define SYS___sysctl           202
 #define SYS_nanosleep          240
+#define SYS_issetugid          253
 #define SYS_clock_gettime      232
 #define SYS_sched_yield                331
 #define SYS_sigprocmask                340
@@ -485,3 +486,10 @@ TEXT runtime·getCntxct(SB),NOSPLIT,$0
 
        MOVW    R0, ret+8(FP)
        RET
+
+// func issetugid() int32
+TEXT runtime·issetugid(SB),NOSPLIT|NOFRAME,$0
+       MOVD $SYS_issetugid, R8
+       SVC
+       MOVW    R0, ret+0(FP)
+       RET
index 58173c2cb64cd00ae3e72a6294c232959895cca3..4f581f569b7375c177020fba135bdd230ea03cbd 100644 (file)
@@ -33,6 +33,7 @@
 #define SYS_fcntl              92
 #define SYS___sysctl           202
 #define SYS_nanosleep          240
+#define SYS_issetugid          253
 #define SYS_clock_gettime      232
 #define SYS_sched_yield                331
 #define SYS_sigprocmask                340
@@ -451,3 +452,11 @@ TEXT runtime·getCntxct(SB),NOSPLIT|NOFRAME,$0
        RDTIME  A0
        MOVW    A0, ret+0(FP)
        RET
+
+// func issetugid() int32
+TEXT runtime·issetugid(SB),NOSPLIT|NOFRAME,$0
+       MOV $SYS_issetugid, T0
+       ECALL
+       MOVW    A0, ret+0(FP)
+       RET
+
index e649fb13cbbbb9f762883aa739dc0344b97a3604..67a04d7a0d7c5365e3ca8599231b0bbb5c1be267 100644 (file)
@@ -29,6 +29,7 @@
 #define SYS___sysctl                   202
 #define SYS___sigaltstack14            281
 #define SYS___sigprocmask14            293
+#define SYS_issetugid                  305
 #define SYS_getcontext                 307
 #define SYS_setcontext                 308
 #define SYS__lwp_create                        309
@@ -482,3 +483,10 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$32
        JAE     2(PC)
        NEGL    AX
        RET
+
+// func issetugid() int32
+TEXT runtime·issetugid(SB),NOSPLIT,$0
+       MOVL    $SYS_issetugid, AX
+       INT     $0x80
+       MOVL    AX, ret+0(FP)
+       RET
index 2c2d97c10545187f0379da518d7b4ccc6e97741d..24b30410061ceb00b8d17c9c843f6a931502e0e4 100644 (file)
@@ -30,6 +30,7 @@
 #define SYS___sysctl                   202
 #define SYS___sigaltstack14            281
 #define SYS___sigprocmask14            293
+#define SYS_issetugid                  305
 #define SYS_getcontext                 307
 #define SYS_setcontext                 308
 #define SYS__lwp_create                        309
@@ -458,3 +459,13 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
        MOVL    $SYS_fcntl, AX
        SYSCALL
        RET
+
+// func issetugid() int32
+TEXT runtime·issetugid(SB),NOSPLIT,$0
+       MOVQ    $0, DI
+       MOVQ    $0, SI
+       MOVQ    $0, DX
+       MOVL    $SYS_issetugid, AX
+       SYSCALL
+       MOVL    AX, ret+0(FP)
+       RET
index 9d969592c47da949a1ac8809ea0887bc58853de4..263c3f0c8d1ef8c945faaf1bcdd9ca4c2aaf117c 100644 (file)
@@ -30,6 +30,7 @@
 #define SYS___sysctl                   SWI_OS_NETBSD | 202
 #define SYS___sigaltstack14            SWI_OS_NETBSD | 281
 #define SYS___sigprocmask14            SWI_OS_NETBSD | 293
+#define SYS_issetugid                  SWI_OS_NETBSD | 305
 #define SYS_getcontext                 SWI_OS_NETBSD | 307
 #define SYS_setcontext                 SWI_OS_NETBSD | 308
 #define SYS__lwp_create                        SWI_OS_NETBSD | 309
@@ -428,3 +429,9 @@ TEXT runtime·read_tls_fallback(SB),NOSPLIT|NOFRAME,$0
        SWI $SYS__lwp_getprivate
        MOVM.IAW    (R13), [R1, R2, R3, R12]
        RET
+
+// func issetugid() int32
+TEXT runtime·issetugid(SB),NOSPLIT,$0
+       SWI $SYS_issetugid
+       MOVW    R0, ret+0(FP)
+       RET
index 0cd9262750f8fb6ac3d0ddb590cea2032144c5ed..c302adb5f9941c30ef5b677943ec73e19929f6ce 100644 (file)
@@ -33,6 +33,7 @@
 #define SYS___sysctl                   202
 #define SYS___sigaltstack14            281
 #define SYS___sigprocmask14            293
+#define SYS_issetugid                  305
 #define SYS_getcontext                 307
 #define SYS_setcontext                 308
 #define SYS__lwp_create                        309
@@ -444,3 +445,9 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
        MOVW    $FD_CLOEXEC, R2
        SVC     $SYS_fcntl
        RET
+
+// func issetugid() int32
+TEXT runtime·issetugid(SB),NOSPLIT|NOFRAME,$0
+       SVC $SYS_issetugid
+       MOVW    R0, ret+0(FP)
+       RET
index 12a53dfc6d95ecd2eee5f1768b604cdeb4d2d461..c7efeaf6e87fdab82a1c31dde78169cbbc1cd435 100644 (file)
@@ -263,6 +263,13 @@ func closeonexec(fd int32) {
        fcntl(fd, _F_SETFD, _FD_CLOEXEC)
 }
 
+//go:cgo_unsafe_args
+func issetugid() (ret int32) {
+       libcCall(unsafe.Pointer(abi.FuncPCABI0(issetugid_trampoline)), unsafe.Pointer(&ret))
+       return
+}
+func issetugid_trampoline()
+
 // Tell the linker that the libc_* functions are to be found
 // in a system library, with the libc_ prefix missing.
 
@@ -295,4 +302,6 @@ func closeonexec(fd int32) {
 //go:cgo_import_dynamic libc_sigaction sigaction "libc.so"
 //go:cgo_import_dynamic libc_sigaltstack sigaltstack "libc.so"
 
+//go:cgo_import_dynamic libc_issetugid issetugid "libc.so"
+
 //go:cgo_import_dynamic _ _ "libc.so"
index d0d9926ff91ade3ef48b6031f99feb0ee7e5a559..6005c106f9c9a7ec3aa121749da5ccdfa996c3aa 100644 (file)
@@ -979,3 +979,12 @@ ok:
        MOVL    BP, SP
        POPL    BP
        RET
+
+TEXT runtime·issetugid_trampoline(SB),NOSPLIT,$0
+       PUSHL   BP
+       CALL    libc_issetugid(SB)
+       NOP     SP                      // tell vet SP changed - stop checking offsets
+       MOVL    8(SP), DX               // pointer to return value
+       MOVL    AX, 0(DX)
+       POPL    BP
+       RET
index 893a92e26f16733c0d360d9dac4127dc0f3b3de0..1177bc1baa22afa4df03beb4bd750b9267f95f00 100644 (file)
@@ -784,3 +784,9 @@ ok:
        MOVQ    BP, SP
        POPQ    BP
        RET
+
+TEXT runtime·issetugid_trampoline(SB),NOSPLIT,$0
+       MOVQ    DI, BX                  // BX is caller-save
+       CALL    libc_issetugid(SB)
+       MOVL    AX, 0(BX)               // return value
+       RET
index fc04cf11a4c094cae570f187bbbab491379e75af..61b901bd5277a4d106303885a287fc8d7a059141 100644 (file)
@@ -816,3 +816,12 @@ ok:
        MOVW    $0, R0          // no error (it's ignored anyway)
        MOVW    R9, R13
        RET
+
+TEXT runtime·issetugid_trampoline(SB),NOSPLIT,$0
+       MOVW    R13, R9
+       MOVW    R0, R8
+       BIC     $0x7, R13               // align for ELF ABI
+       BL      libc_issetugid(SB)
+       MOVW    R0, 0(R8)
+       MOVW    R9, R13
+       RET
index c40889861a8ad7dd17aa6fb659881e8db2bad7e4..563b88f7cc6bf0f26f5e12c24e2dbb44379d37c6 100644 (file)
@@ -649,3 +649,9 @@ TEXT runtime·syscall10X(SB),NOSPLIT,$0
 
 ok:
        RET
+
+TEXT runtime·issetugid_trampoline(SB),NOSPLIT,$0
+       MOVD    R0, R19                 // pointer to args
+       CALL    libc_issetugid(SB)
+       MOVW    R0, 0(R19)              // return value
+       RET
index 9238e7d0b0efa0c6b96238085779760b31f38d41..0a45a07323d4d31a84c10ef7e700cc15bec7043c 100644 (file)
@@ -388,3 +388,10 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
        MOVV    $92, R2                 // sys_fcntl
        SYSCALL
        RET
+
+// func issetugid() int32
+TEXT runtime·issetugid(SB),NOSPLIT,$0
+       MOVV    $253, R2        // sys_issetugid
+       SYSCALL
+       MOVW    R2, ret+0(FP)
+       RET
index d464f284bcb2f42de47286b65537bbddd30349fc..10a4fa07cef42746aae04456cdbc13bb3022dac9 100644 (file)
@@ -23,6 +23,7 @@ import _ "unsafe" // for go:linkname
 //go:cgo_import_dynamic libc_setpgid setpgid "libc.so"
 //go:cgo_import_dynamic libc_syscall syscall "libc.so"
 //go:cgo_import_dynamic libc_wait4 wait4 "libc.so"
+//go:cgo_import_dynamic libc_issetugid issetugid "libc.so"
 
 //go:linkname libc_chdir libc_chdir
 //go:linkname libc_chroot libc_chroot
@@ -41,3 +42,4 @@ import _ "unsafe" // for go:linkname
 //go:linkname libc_setpgid libc_setpgid
 //go:linkname libc_syscall libc_syscall
 //go:linkname libc_wait4 libc_wait4
+//go:linkname libc_issetugid libc_issetugid
index 9faee9ec468952a506253e2f3bb57c9bce602433..11b9c2aadec84d92c5b42a7ee699dfe221e87cbd 100644 (file)
@@ -23,6 +23,7 @@ var (
        libc_setuid,
        libc_setpgid,
        libc_syscall,
+       libc_issetugid,
        libc_wait4 libcFunc
 )
 
diff --git a/src/runtime/testdata/testsuid/main.go b/src/runtime/testdata/testsuid/main.go
new file mode 100644 (file)
index 0000000..1949d2d
--- /dev/null
@@ -0,0 +1,25 @@
+// Copyright 2023 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 main
+
+import (
+       "fmt"
+       "log"
+       "os"
+)
+
+func main() {
+       if os.Geteuid() == os.Getuid() {
+               os.Exit(99)
+       }
+
+       fmt.Fprintf(os.Stdout, "GOTRACEBACK=%s\n", os.Getenv("GOTRACEBACK"))
+       f, err := os.OpenFile(os.Getenv("TEST_OUTPUT"), os.O_CREATE|os.O_RDWR, 0600)
+       if err != nil {
+               log.Fatalf("os.Open failed: %s", err)
+       }
+       defer f.Close()
+       fmt.Fprintf(os.Stderr, "hello\n")
+}