diff --git a/0099-Backport-cmd-go-disallow-package-directories-contain.patch b/0099-Backport-cmd-go-disallow-package-directories-contain.patch new file mode 100644 index 0000000000000000000000000000000000000000..8c97310e8f39aac9df8151c1656cc91289c3eb3d --- /dev/null +++ b/0099-Backport-cmd-go-disallow-package-directories-contain.patch @@ -0,0 +1,202 @@ +From f47e163b52e1987771c9165616cfedda9ea35fee Mon Sep 17 00:00:00 2001 +From: "Bryan C. Mills" +Date: Sat, 13 May 2023 02:15:16 +0800 +Subject: [PATCH 1/3] [Backport] cmd/go: disallow package directories + containing newlines + +Offering: Cloud Core Network +CVE: CVE-2023-29402 +Reference: https://go-review.googlesource.com/c/go/+/501218 + +Directory or file paths containing newlines may cause tools (such as +cmd/cgo) that emit "//line" or "#line" -directives to write part of +the path into non-comment lines in generated source code. If those +lines contain valid Go code, it may be injected into the resulting +binary. + +(Note that Go import paths and file paths within module zip files +already could not contain newlines.) + +Thanks to Juho Nurminen of Mattermost for reporting this issue. + +Updates #60167. +Fixes #60515. +Fixes CVE-2023-29402. + +Change-Id: If55d0400c02beb7a5da5eceac60f1abeac99f064 +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1882606 +Reviewed-by: Roland Shoemaker +Run-TryBot: Roland Shoemaker +Reviewed-by: Russ Cox +Reviewed-by: Damien Neil +(cherry picked from commit 41f9046495564fc728d6f98384ab7276450ac7e2) +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1902229 +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1904343 +Reviewed-by: Michael Knyszek +Reviewed-by: Bryan Mills +Reviewed-on: https://go-review.googlesource.com/c/go/+/501218 +Run-TryBot: David Chase +Auto-Submit: Michael Knyszek +TryBot-Result: Gopher Robot +Signed-off-by: Tang Xi tangxi6@huawei.com +--- + src/cmd/go/internal/load/pkg.go | 4 + + src/cmd/go/internal/work/exec.go | 6 ++ + src/cmd/go/script_test.go | 1 + + .../go/testdata/script/build_cwd_newline.txt | 100 ++++++++++++++++++ + 4 files changed, 111 insertions(+) + create mode 100644 src/cmd/go/testdata/script/build_cwd_newline.txt + +diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go +index 2b5fbb1c5b..07795a4c70 100644 +--- a/src/cmd/go/internal/load/pkg.go ++++ b/src/cmd/go/internal/load/pkg.go +@@ -1791,6 +1791,10 @@ func (p *Package) load(path string, stk *ImportStack, importPos []token.Position + setError(fmt.Errorf("invalid input directory name %q", name)) + return + } ++ if strings.ContainsAny(p.Dir, "\r\n") { ++ setError(fmt.Errorf("invalid package directory %q", p.Dir)) ++ return ++ } + + // Build list of imported packages and full dependency list. + imports := make([]*Package, 0, len(p.Imports)) +diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go +index eb1efd9f82..3745c688cb 100644 +--- a/src/cmd/go/internal/work/exec.go ++++ b/src/cmd/go/internal/work/exec.go +@@ -457,6 +457,12 @@ func (b *Builder) build(a *Action) (err error) { + b.Print(a.Package.ImportPath + "\n") + } + ++ if p.Error != nil { ++ // Don't try to build anything for packages with errors. There may be a ++ // problem with the inputs that makes the package unsafe to build. ++ return p.Error ++ } ++ + if a.Package.BinaryOnly { + p.Stale = true + p.StaleReason = "binary-only packages are no longer supported" +diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go +index 2e8f18a897..5c2a8d1409 100644 +--- a/src/cmd/go/script_test.go ++++ b/src/cmd/go/script_test.go +@@ -140,6 +140,7 @@ func (ts *testScript) setup() { + "devnull=" + os.DevNull, + "goversion=" + goVersion(ts), + ":=" + string(os.PathListSeparator), ++ "newline=\n", + } + + if runtime.GOOS == "plan9" { +diff --git a/src/cmd/go/testdata/script/build_cwd_newline.txt b/src/cmd/go/testdata/script/build_cwd_newline.txt +new file mode 100644 +index 0000000000..61c6966b02 +--- /dev/null ++++ b/src/cmd/go/testdata/script/build_cwd_newline.txt +@@ -0,0 +1,100 @@ ++[windows] skip 'filesystem normalizes / to \' ++[plan9] skip 'filesystem disallows \n in paths' ++ ++# If the directory path containing a package to be built includes a newline, ++# the go command should refuse to even try to build the package. ++ ++env DIR=$WORK${/}${newline}'package main'${newline}'func main() { panic("uh-oh")'${newline}'/*' ++ ++mkdir $DIR ++cd $DIR ++exec pwd ++cp $WORK/go.mod ./go.mod ++cp $WORK/main.go ./main.go ++cp $WORK/main_test.go ./main_test.go ++ ++! go build -o $devnull . ++stderr 'package example: invalid package directory .*uh-oh' ++ ++! go build -o $devnull main.go ++stderr 'package command-line-arguments: invalid package directory .*uh-oh' ++ ++! go run . ++stderr 'package example: invalid package directory .*uh-oh' ++ ++! go run main.go ++stderr 'package command-line-arguments: invalid package directory .*uh-oh' ++ ++! go test . ++stderr 'package example: invalid package directory .*uh-oh' ++ ++! go test -v main.go main_test.go ++stderr 'package command-line-arguments: invalid package directory .*uh-oh' ++ ++ ++# Since we do preserve $PWD (or set it appropriately) for commands, and we do ++# not resolve symlinks unnecessarily, referring to the contents of the unsafe ++# directory via a safe symlink should be ok, and should not inject the data from ++# the symlink target path. ++ ++[!symlink] stop 'remainder of test checks symlink behavior' ++[short] stop 'links and runs binaries' ++ ++symlink $WORK${/}link -> $DIR ++ ++go run $WORK${/}link${/}main.go ++! stdout panic ++! stderr panic ++stderr '^ok$' ++ ++go test -v $WORK${/}link${/}main.go $WORK${/}link${/}main_test.go ++! stdout panic ++! stderr panic ++stdout '^ok$' # 'go test' combines the test's stdout into stderr ++ ++cd $WORK/link ++ ++! go run $DIR${/}main.go ++stderr 'package command-line-arguments: invalid package directory .*uh-oh' ++ ++go run . ++! stdout panic ++! stderr panic ++stderr '^ok$' ++ ++go run main.go ++! stdout panic ++! stderr panic ++stderr '^ok$' ++ ++go test -v ++! stdout panic ++! stderr panic ++stdout '^ok$' # 'go test' combines the test's stdout into stderr ++ ++go test -v . ++! stdout panic ++! stderr panic ++stdout '^ok$' # 'go test' combines the test's stdout into stderr ++ ++ ++-- $WORK/go.mod -- ++module example ++go 1.19 ++-- $WORK/main.go -- ++package main ++ ++import "C" ++ ++func main() { ++ /* nothing here */ ++ println("ok") ++} ++-- $WORK/main_test.go -- ++package main ++ ++import "testing" ++ ++func TestMain(*testing.M) { ++ main() ++} +-- +2.33.0 + diff --git a/0100-Backport-cmd-go-enforce-flags-with-non-optional-argu.patch b/0100-Backport-cmd-go-enforce-flags-with-non-optional-argu.patch new file mode 100644 index 0000000000000000000000000000000000000000..98fc0e34d046656c54e07a3e3823c9a86089a01f --- /dev/null +++ b/0100-Backport-cmd-go-enforce-flags-with-non-optional-argu.patch @@ -0,0 +1,86 @@ +From 74a03ac220bb428161dcf4542a0298f367de1138 Mon Sep 17 00:00:00 2001 +From: Roland Shoemaker +Date: Fri, 5 May 2023 13:10:34 -0700 +Subject: [PATCH 2/3] [Backport] cmd/go: enforce flags with non-optional + arguments + +Offering: Cloud Core Network +CVE: CVE-2023-29404 +Reference: https://go-review.googlesource.com/c/go/+/501217 + +Enforce that linker flags which expect arguments get them, otherwise it +may be possible to smuggle unexpected flags through as the linker can +consume what looks like a flag as an argument to a preceding flag (i.e. +"-Wl,-O -Wl,-R,-bad-flag" is interpreted as "-O=-R -bad-flag"). Also be +somewhat more restrictive in the general format of some flags. + +Thanks to Juho Nurminen of Mattermost for reporting this issue. + +Updates #60305 +Fixes #60511 +Fixes CVE-2023-29404 + +Change-Id: Icdffef2c0f644da50261cace6f43742783931cff +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1876275 +Reviewed-by: Ian Lance Taylor +Reviewed-by: Damien Neil +(cherry picked from commit 896779503cf754cbdac24b61d4cc953b50fe2dde) +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1902225 +Run-TryBot: Roland Shoemaker +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1904342 +Reviewed-by: Michael Knyszek +Reviewed-on: https://go-review.googlesource.com/c/go/+/501217 +Auto-Submit: Michael Knyszek +Run-TryBot: David Chase +TryBot-Bypass: Michael Knyszek +Signed-off-by: Tang Xi tangxi6@huawei.com +--- + src/cmd/go/internal/work/security.go | 6 +++--- + src/cmd/go/internal/work/security_test.go | 5 +++++ + 2 files changed, 8 insertions(+), 3 deletions(-) + +diff --git a/src/cmd/go/internal/work/security.go b/src/cmd/go/internal/work/security.go +index 0d9628241f..2a0479ca58 100644 +--- a/src/cmd/go/internal/work/security.go ++++ b/src/cmd/go/internal/work/security.go +@@ -177,10 +177,10 @@ var validLinkerFlags = []*lazyregexp.Regexp{ + re(`-Wl,-Bdynamic`), + re(`-Wl,-berok`), + re(`-Wl,-Bstatic`), +- re(`-WL,-O([^@,\-][^,]*)?`), ++ re(`-Wl,-O[0-9]+`), + re(`-Wl,-d[ny]`), + re(`-Wl,--disable-new-dtags`), +- re(`-Wl,-e[=,][a-zA-Z0-9]*`), ++ re(`-Wl,-e[=,][a-zA-Z0-9]+`), + re(`-Wl,--enable-new-dtags`), + re(`-Wl,--end-group`), + re(`-Wl,--(no-)?export-dynamic`), +@@ -189,7 +189,7 @@ var validLinkerFlags = []*lazyregexp.Regexp{ + re(`-Wl,--hash-style=(sysv|gnu|both)`), + re(`-Wl,-headerpad_max_install_names`), + re(`-Wl,--no-undefined`), +- re(`-Wl,-R([^@\-][^,@]*$)`), ++ re(`-Wl,-R,?([^@\-,][^,@]*$)`), + re(`-Wl,--just-symbols[=,]([^,@\-][^,@]+)`), + re(`-Wl,-rpath(-link)?[=,]([^,@\-][^,]+)`), + re(`-Wl,-s`), +diff --git a/src/cmd/go/internal/work/security_test.go b/src/cmd/go/internal/work/security_test.go +index aec9789185..591287c875 100644 +--- a/src/cmd/go/internal/work/security_test.go ++++ b/src/cmd/go/internal/work/security_test.go +@@ -222,6 +222,11 @@ var badLinkerFlags = [][]string{ + {"-Wl,-R,@foo"}, + {"-Wl,--just-symbols,@foo"}, + {"../x.o"}, ++ {"-Wl,-R,"}, ++ {"-Wl,-O"}, ++ {"-Wl,-e="}, ++ {"-Wl,-e,"}, ++ {"-Wl,-R,-flag"}, + } + + func TestCheckLinkerFlags(t *testing.T) { +-- +2.33.0 + diff --git a/0101-Backport-cmd-go-cmd-cgo-in-_cgo_flags-use-one-line-p.patch b/0101-Backport-cmd-go-cmd-cgo-in-_cgo_flags-use-one-line-p.patch new file mode 100644 index 0000000000000000000000000000000000000000..e467c964ee7ff8da14c869994aff3c5b00906953 --- /dev/null +++ b/0101-Backport-cmd-go-cmd-cgo-in-_cgo_flags-use-one-line-p.patch @@ -0,0 +1,119 @@ +From 1c552fe2fd6152be3e1fc5f26635286fbd45a5c7 Mon Sep 17 00:00:00 2001 +From: Ian Lance Taylor +Date: Thu, 4 May 2023 14:06:39 -0700 +Subject: [PATCH 3/3] [Backport] cmd/go,cmd/cgo: in _cgo_flags use one line per + flag + +Offering: Cloud Core Network +CVE: CVE-2023-29405 +Reference: https://go-review.googlesource.com/c/go/+/501216 + +The flags that we recorded in _cgo_flags did not use any quoting, +so a flag containing embedded spaces was mishandled. +Change the _cgo_flags format to put each flag on a separate line. +That is a simple format that does not require any quoting. + +As far as I can tell only cmd/go uses _cgo_flags, and it is only +used for gccgo. If this patch doesn't cause any trouble, then +in the next release we can change to only using _cgo_flags for gccgo. + +Thanks to Juho Nurminen of Mattermost for reporting this issue. + +Note: Merged with CL: https://go-review.googlesource.com/c/go/+/501297 + +Edited-by: tangxi t00586138 + +Updates #60306 +Fixes #60513 +Fixes CVE-2023-29405 + +Change-Id: Id738a737ecae47babb34c4b4fc4d65336cf0c0f3 +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1875094 +Reviewed-by: Damien Neil +Reviewed-by: Roland Shoemaker +(cherry picked from commit bcdfcadd5612212089d958bc352a6f6c90742dcc) +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1902227 +Run-TryBot: Roland Shoemaker +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1904341 +Reviewed-by: Michael Knyszek +Reviewed-on: https://go-review.googlesource.com/c/go/+/501216 +Auto-Submit: Michael Knyszek +Run-TryBot: David Chase +TryBot-Bypass: David Chase +Signed-off-by: Tang Xi tangxi6@huawei.com +--- + src/cmd/cgo/out.go | 4 +++- + src/cmd/go/internal/work/gccgo.go | 14 ++++++------- + .../go/testdata/script/gccgo_link_ldflags.txt | 20 +++++++++++++++++++ + 3 files changed, 29 insertions(+), 9 deletions(-) + create mode 100644 src/cmd/go/testdata/script/gccgo_link_ldflags.txt + +diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go +index be4f6ad2d5..21607411c8 100644 +--- a/src/cmd/cgo/out.go ++++ b/src/cmd/cgo/out.go +@@ -47,7 +47,9 @@ func (p *Package) writeDefs() { + + fflg := creat(*objDir + "_cgo_flags") + for k, v := range p.CgoFlags { +- fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, strings.Join(v, " ")) ++ for _, arg := range v { ++ fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, arg) ++ } + if k == "LDFLAGS" && !*gccgo { + for _, arg := range v { + fmt.Fprintf(fgo2, "//go:cgo_ldflag %q\n", arg) +diff --git a/src/cmd/go/internal/work/gccgo.go b/src/cmd/go/internal/work/gccgo.go +index 2f5d5d6283..75a0b7d815 100644 +--- a/src/cmd/go/internal/work/gccgo.go ++++ b/src/cmd/go/internal/work/gccgo.go +@@ -251,14 +251,12 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string + const ldflagsPrefix = "_CGO_LDFLAGS=" + for _, line := range strings.Split(string(flags), "\n") { + if strings.HasPrefix(line, ldflagsPrefix) { +- newFlags := strings.Fields(line[len(ldflagsPrefix):]) +- for _, flag := range newFlags { +- // Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS +- // but they don't mean anything to the linker so filter +- // them out. +- if flag != "-g" && !strings.HasPrefix(flag, "-O") { +- cgoldflags = append(cgoldflags, flag) +- } ++ flag := line[len(ldflagsPrefix):] ++ // Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS ++ // but they don't mean anything to the linker so filter ++ // them out. ++ if flag != "-g" && !strings.HasPrefix(flag, "-O") { ++ cgoldflags = append(cgoldflags, flag) + } + } + } +diff --git a/src/cmd/go/testdata/script/gccgo_link_ldflags.txt b/src/cmd/go/testdata/script/gccgo_link_ldflags.txt +new file mode 100644 +index 0000000000..4e91ae5650 +--- /dev/null ++++ b/src/cmd/go/testdata/script/gccgo_link_ldflags.txt +@@ -0,0 +1,20 @@ ++# Test that #cgo LDFLAGS are properly quoted. ++# The #cgo LDFLAGS below should pass a string with spaces to -L, ++# as though searching a directory with a space in its name. ++# It should not pass --nosuchoption to the external linker. ++ ++[!cgo] skip ++ ++go build ++ ++[!exec:gccgo] skip ++ ++go build -compiler gccgo ++ ++-- go.mod -- ++module m ++-- cgo.go -- ++package main ++// #cgo LDFLAGS: -L "./ -Wl,--nosuchoption" ++import "C" ++func main() {} +-- +2.33.0 + diff --git a/0102-Backport-runtime-implement-SUID-SGID-protections.patch b/0102-Backport-runtime-implement-SUID-SGID-protections.patch new file mode 100644 index 0000000000000000000000000000000000000000..9ba471c894d5fa031e841d56909223d82b68a9a6 --- /dev/null +++ b/0102-Backport-runtime-implement-SUID-SGID-protections.patch @@ -0,0 +1,1586 @@ +From fbac3ddaf0d7f33a11ce18844d431cdf455b482e Mon Sep 17 00:00:00 2001 +From: Michael Pratt +Date: Tue, 8 Feb 2022 16:45:14 -0500 +Subject: [PATCH] [Backport] runtime: implement SUID/SGID protections + +Offering: Cloud Core Network +CVE: CVE-2023-29403 +Reference: https://go-review.googlesource.com/c/go/+/501228 + +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. + +Note: When the runtime determines the binary is setuid/setgid-like and a signal is received that terminates the program, set inFatal to true. The buildtag unix was only introduced in go 1.19, so you need to change //go:build unix to the following: + +//go:build aix || android || darwin || dragonfly || freebsd || hurd || illumos || ios || linux || netbsd || openbsd || solaris + +Edited-by: tangxi t00586138 + +Updates #60272 +Fixes #60517 +Fixes CVE-2023-29403 + +Change-Id: I057fa7153d29cf26515e7f49fed86e4f8bedd0f0 +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1878434 +Reviewed-by: Damien Neil +Reviewed-by: Ian Lance Taylor +Run-TryBot: Roland Shoemaker +Reviewed-by: Russ Cox +(cherry picked from commit 87065663ea6d89cd54f65a515d8f2ed0ef285c19) +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1902231 +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1904340 +Reviewed-by: Michael Knyszek +Reviewed-on: https://go-review.googlesource.com/c/go/+/501228 +Auto-Submit: Michael Knyszek +TryBot-Result: Gopher Robot +Run-TryBot: David Chase +Signed-off-by: Tang Xi tangxi6@huawei.com +--- + src/cmd/compile/internal/gc/racewalk.go | 3 +- + src/go/build/deps_test.go | 1 + + src/runtime/export_darwin_test.go | 7 - + src/runtime/export_unix_test.go | 1 + + src/runtime/extern.go | 19 +++ + src/runtime/internal/syscall/asm_linux_386.s | 34 +++++ + .../internal/syscall/asm_linux_amd64.s | 33 ++++ + src/runtime/internal/syscall/asm_linux_arm.s | 32 ++++ + .../internal/syscall/asm_linux_arm64.s | 29 ++++ + .../internal/syscall/defs_linux_386.go | 7 + + .../internal/syscall/defs_linux_amd64.go | 7 + + .../internal/syscall/defs_linux_arm.go | 7 + + .../internal/syscall/defs_linux_arm64.go | 7 + + src/runtime/internal/syscall/syscall_linux.go | 12 ++ + src/runtime/nbpipe_fcntl_libc_test.go | 18 --- + src/runtime/nbpipe_fcntl_unix_test.go | 17 --- + src/runtime/nbpipe_test.go | 26 ++-- + src/runtime/os2_aix.go | 12 ++ + src/runtime/os_aix.go | 40 +++++ + src/runtime/os_dragonfly.go | 2 + + src/runtime/os_freebsd.go | 2 + + src/runtime/os_linux.go | 25 +++ + src/runtime/os_netbsd.go | 2 + + src/runtime/os_solaris.go | 4 + + src/runtime/panic.go | 3 + + src/runtime/proc.go | 1 + + src/runtime/security_aix.go | 17 +++ + src/runtime/security_issetugid.go | 20 +++ + src/runtime/security_linux.go | 15 ++ + src/runtime/security_nonunix.go | 14 ++ + src/runtime/security_test.go | 144 ++++++++++++++++++ + src/runtime/security_unix.go | 73 +++++++++ + src/runtime/signal_unix.go | 4 + + src/runtime/sys_darwin.go | 27 +++- + src/runtime/sys_darwin_amd64.s | 7 + + src/runtime/sys_darwin_arm64.s | 4 + + src/runtime/sys_dragonfly_amd64.s | 10 ++ + src/runtime/sys_freebsd_386.s | 7 + + src/runtime/sys_freebsd_amd64.s | 10 ++ + src/runtime/sys_freebsd_arm.s | 8 + + src/runtime/sys_freebsd_arm64.s | 8 + + src/runtime/sys_netbsd_386.s | 8 + + src/runtime/sys_netbsd_amd64.s | 11 ++ + src/runtime/sys_netbsd_arm.s | 7 + + src/runtime/sys_netbsd_arm64.s | 7 + + src/runtime/sys_openbsd_386.s | 9 ++ + src/runtime/sys_openbsd_amd64.s | 6 + + src/runtime/sys_openbsd_arm.s | 9 ++ + src/runtime/sys_openbsd_arm64.s | 6 + + src/runtime/syscall2_solaris.go | 2 + + src/runtime/syscall_solaris.go | 1 + + src/runtime/testdata/testsuid/main.go | 25 +++ + 52 files changed, 751 insertions(+), 59 deletions(-) + create mode 100644 src/runtime/internal/syscall/asm_linux_386.s + create mode 100644 src/runtime/internal/syscall/asm_linux_amd64.s + create mode 100644 src/runtime/internal/syscall/asm_linux_arm.s + create mode 100644 src/runtime/internal/syscall/asm_linux_arm64.s + create mode 100644 src/runtime/internal/syscall/defs_linux_386.go + create mode 100644 src/runtime/internal/syscall/defs_linux_amd64.go + create mode 100644 src/runtime/internal/syscall/defs_linux_arm.go + create mode 100644 src/runtime/internal/syscall/defs_linux_arm64.go + create mode 100644 src/runtime/internal/syscall/syscall_linux.go + delete mode 100644 src/runtime/nbpipe_fcntl_libc_test.go + delete mode 100644 src/runtime/nbpipe_fcntl_unix_test.go + create mode 100644 src/runtime/security_aix.go + create mode 100644 src/runtime/security_issetugid.go + create mode 100644 src/runtime/security_linux.go + create mode 100644 src/runtime/security_nonunix.go + create mode 100644 src/runtime/security_test.go + create mode 100644 src/runtime/security_unix.go + create mode 100644 src/runtime/testdata/testsuid/main.go + +diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go +index 6f251377c9..710c97a84b 100644 +--- a/src/cmd/compile/internal/gc/racewalk.go ++++ b/src/cmd/compile/internal/gc/racewalk.go +@@ -34,8 +34,9 @@ import ( + // at best instrumentation would cause infinite recursion. + var omit_pkgs = []string{ + "runtime/internal/atomic", +- "runtime/internal/sys", + "runtime/internal/math", ++ "runtime/internal/sys", ++ "runtime/internal/syscall", + "runtime", + "runtime/race", + "runtime/msan", +diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go +index 875acebf9c..c134417567 100644 +--- a/src/go/build/deps_test.go ++++ b/src/go/build/deps_test.go +@@ -81,6 +81,7 @@ var depsRules = ` + < internal/bytealg + < internal/unsafeheader + < runtime/internal/sys ++ < runtime/internal/syscall + < runtime/internal/atomic + < runtime/internal/math + < runtime +diff --git a/src/runtime/export_darwin_test.go b/src/runtime/export_darwin_test.go +index e9b6eb36da..034c52d603 100644 +--- a/src/runtime/export_darwin_test.go ++++ b/src/runtime/export_darwin_test.go +@@ -4,10 +4,3 @@ + + package runtime + +-func Fcntl(fd, cmd, arg uintptr) (uintptr, uintptr) { +- r := fcntl(int32(fd), int32(cmd), int32(arg)) +- if r < 0 { +- return ^uintptr(0), uintptr(-r) +- } +- return uintptr(r), 0 +-} +diff --git a/src/runtime/export_unix_test.go b/src/runtime/export_unix_test.go +index 621488eaba..b1cf15f035 100644 +--- a/src/runtime/export_unix_test.go ++++ b/src/runtime/export_unix_test.go +@@ -12,6 +12,7 @@ var NonblockingPipe = nonblockingPipe + var Pipe = pipe + var SetNonblock = setNonblock + var Closeonexec = closeonexec ++var Fcntl = fcntl + + func sigismember(mask *sigset, i int) bool { + clear := *mask +diff --git a/src/runtime/extern.go b/src/runtime/extern.go +index 7316503ed2..056465cccb 100644 +--- a/src/runtime/extern.go ++++ b/src/runtime/extern.go +@@ -172,6 +172,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 + +diff --git a/src/runtime/internal/syscall/asm_linux_386.s b/src/runtime/internal/syscall/asm_linux_386.s +new file mode 100644 +index 0000000000..15aae4d8bd +--- /dev/null ++++ b/src/runtime/internal/syscall/asm_linux_386.s +@@ -0,0 +1,34 @@ ++// Copyright 2022 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. ++ ++#include "textflag.h" ++ ++// See ../sys_linux_386.s for the reason why we always use int 0x80 ++// instead of the glibc-specific "CALL 0x10(GS)". ++#define INVOKE_SYSCALL INT $0x80 ++ ++// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) ++// ++// Syscall # in AX, args in BX CX DX SI DI BP, return in AX ++TEXT ·Syscall6(SB),NOSPLIT,$0-40 ++ MOVL num+0(FP), AX // syscall entry ++ MOVL a1+4(FP), BX ++ MOVL a2+8(FP), CX ++ MOVL a3+12(FP), DX ++ MOVL a4+16(FP), SI ++ MOVL a5+20(FP), DI ++ MOVL a6+24(FP), BP ++ INVOKE_SYSCALL ++ CMPL AX, $0xfffff001 ++ JLS ok ++ MOVL $-1, r1+28(FP) ++ MOVL $0, r2+32(FP) ++ NEGL AX ++ MOVL AX, errno+36(FP) ++ RET ++ok: ++ MOVL AX, r1+28(FP) ++ MOVL DX, r2+32(FP) ++ MOVL $0, errno+36(FP) ++ RET +diff --git a/src/runtime/internal/syscall/asm_linux_amd64.s b/src/runtime/internal/syscall/asm_linux_amd64.s +new file mode 100644 +index 0000000000..961d9bd640 +--- /dev/null ++++ b/src/runtime/internal/syscall/asm_linux_amd64.s +@@ -0,0 +1,33 @@ ++// Copyright 2022 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. ++ ++#include "textflag.h" ++ ++// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) ++// ++// Syscall # in AX, args in DI SI DX R10 R8 R9, return in AX DX. ++// ++// Note that this differs from "standard" ABI convention, which would pass 4th ++// arg in CX, not R10. ++TEXT ·Syscall6(SB),NOSPLIT,$0-80 ++ MOVQ num+0(FP), AX // syscall entry ++ MOVQ a1+8(FP), DI ++ MOVQ a2+16(FP), SI ++ MOVQ a3+24(FP), DX ++ MOVQ a4+32(FP), R10 ++ MOVQ a5+40(FP), R8 ++ MOVQ a6+48(FP), R9 ++ SYSCALL ++ CMPQ AX, $0xfffffffffffff001 ++ JLS ok ++ MOVQ $-1, r1+56(FP) ++ MOVQ $0, r2+64(FP) ++ NEGQ AX ++ MOVQ AX, errno+72(FP) ++ RET ++ok: ++ MOVQ AX, r1+56(FP) ++ MOVQ DX, r2+64(FP) ++ MOVQ $0, errno+72(FP) ++ RET +diff --git a/src/runtime/internal/syscall/asm_linux_arm.s b/src/runtime/internal/syscall/asm_linux_arm.s +new file mode 100644 +index 0000000000..dbf1826d94 +--- /dev/null ++++ b/src/runtime/internal/syscall/asm_linux_arm.s +@@ -0,0 +1,32 @@ ++// Copyright 2022 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. ++ ++#include "textflag.h" ++ ++// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) ++TEXT ·Syscall6(SB),NOSPLIT,$0-40 ++ MOVW num+0(FP), R7 // syscall entry ++ MOVW a1+4(FP), R0 ++ MOVW a2+8(FP), R1 ++ MOVW a3+12(FP), R2 ++ MOVW a4+16(FP), R3 ++ MOVW a5+20(FP), R4 ++ MOVW a6+24(FP), R5 ++ SWI $0 ++ MOVW $0xfffff001, R6 ++ CMP R6, R0 ++ BLS ok ++ MOVW $-1, R1 ++ MOVW R1, r1+28(FP) ++ MOVW $0, R2 ++ MOVW R2, r2+32(FP) ++ RSB $0, R0, R0 ++ MOVW R0, errno+36(FP) ++ RET ++ok: ++ MOVW R0, r1+28(FP) ++ MOVW R1, r2+32(FP) ++ MOVW $0, R0 ++ MOVW R0, errno+36(FP) ++ RET +diff --git a/src/runtime/internal/syscall/asm_linux_arm64.s b/src/runtime/internal/syscall/asm_linux_arm64.s +new file mode 100644 +index 0000000000..83e862ff72 +--- /dev/null ++++ b/src/runtime/internal/syscall/asm_linux_arm64.s +@@ -0,0 +1,29 @@ ++// Copyright 2022 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. ++ ++#include "textflag.h" ++ ++// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) ++TEXT ·Syscall6(SB),NOSPLIT,$0-80 ++ MOVD num+0(FP), R8 // syscall entry ++ MOVD a1+8(FP), R0 ++ MOVD a2+16(FP), R1 ++ MOVD a3+24(FP), R2 ++ MOVD a4+32(FP), R3 ++ MOVD a5+40(FP), R4 ++ MOVD a6+48(FP), R5 ++ SVC ++ CMN $4095, R0 ++ BCC ok ++ MOVD $-1, R4 ++ MOVD R4, r1+56(FP) ++ MOVD ZR, r2+64(FP) ++ NEG R0, R0 ++ MOVD R0, errno+72(FP) ++ RET ++ok: ++ MOVD R0, r1+56(FP) ++ MOVD R1, r2+64(FP) ++ MOVD ZR, errno+72(FP) ++ RET +diff --git a/src/runtime/internal/syscall/defs_linux_386.go b/src/runtime/internal/syscall/defs_linux_386.go +new file mode 100644 +index 0000000000..31d704e235 +--- /dev/null ++++ b/src/runtime/internal/syscall/defs_linux_386.go +@@ -0,0 +1,7 @@ ++// 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 syscall ++ ++const SYS_FCNTL = 55 +diff --git a/src/runtime/internal/syscall/defs_linux_amd64.go b/src/runtime/internal/syscall/defs_linux_amd64.go +new file mode 100644 +index 0000000000..2368eb03b4 +--- /dev/null ++++ b/src/runtime/internal/syscall/defs_linux_amd64.go +@@ -0,0 +1,7 @@ ++// 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 syscall ++ ++const SYS_FCNTL = 72 +diff --git a/src/runtime/internal/syscall/defs_linux_arm.go b/src/runtime/internal/syscall/defs_linux_arm.go +new file mode 100644 +index 0000000000..31d704e235 +--- /dev/null ++++ b/src/runtime/internal/syscall/defs_linux_arm.go +@@ -0,0 +1,7 @@ ++// 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 syscall ++ ++const SYS_FCNTL = 55 +diff --git a/src/runtime/internal/syscall/defs_linux_arm64.go b/src/runtime/internal/syscall/defs_linux_arm64.go +new file mode 100644 +index 0000000000..6292c90af5 +--- /dev/null ++++ b/src/runtime/internal/syscall/defs_linux_arm64.go +@@ -0,0 +1,7 @@ ++// 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 syscall ++ ++const SYS_FCNTL = 25 +diff --git a/src/runtime/internal/syscall/syscall_linux.go b/src/runtime/internal/syscall/syscall_linux.go +new file mode 100644 +index 0000000000..06d5f21e7c +--- /dev/null ++++ b/src/runtime/internal/syscall/syscall_linux.go +@@ -0,0 +1,12 @@ ++// Copyright 2022 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 provides the syscall primitives required for the runtime. ++package syscall ++ ++// TODO(https://go.dev/issue/51087): This package is incomplete and currently ++// only contains very minimal support for Linux. ++ ++// Syscall6 calls system call number 'num' with arguments a1-6. ++func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) +diff --git a/src/runtime/nbpipe_fcntl_libc_test.go b/src/runtime/nbpipe_fcntl_libc_test.go +deleted file mode 100644 +index b38c58399b..0000000000 +--- a/src/runtime/nbpipe_fcntl_libc_test.go ++++ /dev/null +@@ -1,18 +0,0 @@ +-// Copyright 2019 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 aix darwin solaris +- +-package runtime_test +- +-import ( +- "runtime" +- "syscall" +-) +- +-// Call fcntl libc function rather than calling syscall. +-func fcntl(fd uintptr, cmd int, arg uintptr) (uintptr, syscall.Errno) { +- res, errno := runtime.Fcntl(fd, uintptr(cmd), arg) +- return res, syscall.Errno(errno) +-} +diff --git a/src/runtime/nbpipe_fcntl_unix_test.go b/src/runtime/nbpipe_fcntl_unix_test.go +deleted file mode 100644 +index 75acdb62dd..0000000000 +--- a/src/runtime/nbpipe_fcntl_unix_test.go ++++ /dev/null +@@ -1,17 +0,0 @@ +-// Copyright 2019 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 dragonfly freebsd linux netbsd openbsd +- +-package runtime_test +- +-import ( +- "internal/syscall/unix" +- "syscall" +-) +- +-func fcntl(fd uintptr, cmd int, arg uintptr) (uintptr, syscall.Errno) { +- res, _, err := syscall.Syscall(unix.FcntlSyscall, fd, uintptr(cmd), arg) +- return res, err +-} +diff --git a/src/runtime/nbpipe_test.go b/src/runtime/nbpipe_test.go +index d739f57864..9496f83a44 100644 +--- a/src/runtime/nbpipe_test.go ++++ b/src/runtime/nbpipe_test.go +@@ -14,23 +14,29 @@ import ( + ) + + func TestNonblockingPipe(t *testing.T) { +- t.Parallel() +- + // NonblockingPipe is the test name for nonblockingPipe. + r, w, errno := runtime.NonblockingPipe() + if errno != 0 { + t.Fatal(syscall.Errno(errno)) + } +- defer func() { +- runtime.Close(r) +- runtime.Close(w) +- }() ++ defer runtime.Close(w) + + checkIsPipe(t, r, w) + checkNonblocking(t, r, "reader") + checkCloseonexec(t, r, "reader") + checkNonblocking(t, w, "writer") + checkCloseonexec(t, w, "writer") ++ ++ // Test that fcntl returns an error as expected. ++ if runtime.Close(r) != 0 { ++ t.Fatalf("Close(%d) failed", r) ++ } ++ val, errno := runtime.Fcntl(r, syscall.F_GETFD, 0) ++ if val != -1 { ++ t.Errorf("Fcntl succeeded unexpectedly") ++ } else if syscall.Errno(errno) != syscall.EBADF { ++ t.Errorf("Fcntl failed with error %v, expected %v", syscall.Errno(errno), syscall.EBADF) ++ } + } + + func checkIsPipe(t *testing.T, r, w int32) { +@@ -49,8 +55,8 @@ func checkIsPipe(t *testing.T, r, w int32) { + + func checkNonblocking(t *testing.T, fd int32, name string) { + t.Helper() +- flags, errno := fcntl(uintptr(fd), syscall.F_GETFL, 0) +- if errno != 0 { ++ flags, errno := runtime.Fcntl(fd, syscall.F_GETFL, 0) ++ if flags == -1 { + t.Errorf("fcntl(%s, F_GETFL) failed: %v", name, syscall.Errno(errno)) + } else if flags&syscall.O_NONBLOCK == 0 { + t.Errorf("O_NONBLOCK not set in %s flags %#x", name, flags) +@@ -59,8 +65,8 @@ func checkNonblocking(t *testing.T, fd int32, name string) { + + func checkCloseonexec(t *testing.T, fd int32, name string) { + t.Helper() +- flags, errno := fcntl(uintptr(fd), syscall.F_GETFD, 0) +- if errno != 0 { ++ flags, errno := runtime.Fcntl(fd, syscall.F_GETFD, 0) ++ if flags == -1 { + t.Errorf("fcntl(%s, F_GETFD) failed: %v", name, syscall.Errno(errno)) + } else if flags&syscall.FD_CLOEXEC == 0 { + t.Errorf("FD_CLOEXEC not set in %s flags %#x", name, flags) +diff --git a/src/runtime/os2_aix.go b/src/runtime/os2_aix.go +index 31ac6ddf79..8b609aaf90 100644 +--- a/src/runtime/os2_aix.go ++++ b/src/runtime/os2_aix.go +@@ -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, +diff --git a/src/runtime/os_aix.go b/src/runtime/os_aix.go +index 9a6b8aec7c..7fb7aafa74 100644 +--- a/src/runtime/os_aix.go ++++ b/src/runtime/os_aix.go +@@ -375,3 +375,43 @@ func setNonblock(fd int32) { + flags := fcntl(fd, _F_GETFL, 0) + fcntl(fd, _F_SETFL, flags|_O_NONBLOCK) + } ++ ++//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) ++} +diff --git a/src/runtime/os_dragonfly.go b/src/runtime/os_dragonfly.go +index 6578fcbeb1..df0ff3828f 100644 +--- a/src/runtime/os_dragonfly.go ++++ b/src/runtime/os_dragonfly.go +@@ -62,6 +62,8 @@ func pipe() (r, w int32, errno int32) + + const stackSystem = 0 + ++func issetugid() int32 ++ + // From DragonFly's + const ( + _CTL_HW = 6 +diff --git a/src/runtime/os_freebsd.go b/src/runtime/os_freebsd.go +index 730973a202..35242c3894 100644 +--- a/src/runtime/os_freebsd.go ++++ b/src/runtime/os_freebsd.go +@@ -46,6 +46,8 @@ func pipe2(flags int32) (r, w int32, errno int32) + func closeonexec(fd int32) + func setNonblock(fd int32) + ++func issetugid() int32 ++ + // From FreeBSD's + const ( + _CTL_HW = 6 +diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go +index 7b95ff2428..e83b6df048 100644 +--- a/src/runtime/os_linux.go ++++ b/src/runtime/os_linux.go +@@ -7,6 +7,7 @@ package runtime + import ( + "runtime/internal/atomic" + "runtime/internal/sys" ++ "runtime/internal/syscall" + "unsafe" + ) + +@@ -31,9 +32,12 @@ const ( + ) + + // Atomically, ++// + // if(*addr == val) sleep ++// + // Might be woken up spuriously; that's allowed. + // Don't sleep longer than ns; ns < 0 means forever. ++// + //go:nosplit + func futexsleep(addr *uint32, val uint32, ns int64) { + // Some Linux kernels have a bug where futex of +@@ -52,6 +56,7 @@ func futexsleep(addr *uint32, val uint32, ns int64) { + } + + // If any procs are sleeping on addr, wake up at most cnt. ++// + //go:nosplit + func futexwakeup(addr *uint32, cnt uint32) { + ret := futex(unsafe.Pointer(addr), _FUTEX_WAKE_PRIVATE, cnt, nil, nil, 0) +@@ -136,6 +141,7 @@ const ( + func clone(flags int32, stk, mp, gp, fn unsafe.Pointer) int32 + + // May run with m.p==nil, so write barriers are not allowed. ++// + //go:nowritebarrier + func newosproc(mp *m) { + stk := unsafe.Pointer(mp.g0.stack.hi) +@@ -163,6 +169,7 @@ func newosproc(mp *m) { + } + + // Version of newosproc that doesn't require a valid G. ++// + //go:nosplit + func newosproc0(stacksize uintptr, fn unsafe.Pointer) { + stack := sysAlloc(stacksize, &memstats.stacks_sys) +@@ -184,6 +191,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 + ) +@@ -249,6 +257,9 @@ func sysargs(argc int32, argv **byte) { + sysauxv(buf[:]) + } + ++// 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 { +@@ -261,6 +272,9 @@ func sysauxv(auxv []uintptr) int { + + case _AT_PAGESZ: + physPageSize = val ++ ++ case _AT_SECURE: ++ secureMode = val == 1 + } + + archauxv(tag, val) +@@ -322,6 +336,7 @@ func goenvs() { + // Called to do synchronous initialization of Go code built with + // -buildmode=c-archive or -buildmode=c-shared. + // None of the Go runtime is initialized. ++// + //go:nosplit + //go:nowritebarrierrec + func libpreinit() { +@@ -358,6 +373,7 @@ func minit() { + } + + // Called from dropm to undo the effect of an minit. ++// + //go:nosplit + func unminit() { + unminitSignals() +@@ -397,6 +413,12 @@ func pipe() (r, w int32, errno int32) + func pipe2(flags int32) (r, w int32, errno int32) + func setNonblock(fd int32) + ++//go:nosplit ++func fcntl(fd, cmd, arg int32) (ret int32, errno int32) { ++ r, _, err := syscall.Syscall6(syscall.SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg), 0, 0, 0) ++ return int32(r), int32(err) ++} ++ + //go:nosplit + //go:nowritebarrierrec + func setsig(i uint32, fn uintptr) { +@@ -441,6 +463,7 @@ func getsig(i uint32) uintptr { + } + + // setSignaltstackSP sets the ss_sp field of a stackt. ++// + //go:nosplit + func setSignalstackSP(s *stackt, sp uintptr) { + *(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp +@@ -451,6 +474,7 @@ func (c *sigctxt) fixsigcode(sig uint32) { + } + + // sysSigaction calls the rt_sigaction system call. ++// + //go:nosplit + func sysSigaction(sig uint32, new, old *sigactiont) { + if rt_sigaction(uintptr(sig), new, old, unsafe.Sizeof(sigactiont{}.sa_mask)) != 0 { +@@ -475,6 +499,7 @@ func sysSigaction(sig uint32, new, old *sigactiont) { + } + + // rt_sigaction is implemented in assembly. ++// + //go:noescape + func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32 + +diff --git a/src/runtime/os_netbsd.go b/src/runtime/os_netbsd.go +index 97106c7b9d..748e324f6f 100644 +--- a/src/runtime/os_netbsd.go ++++ b/src/runtime/os_netbsd.go +@@ -77,6 +77,8 @@ func pipe2(flags int32) (r, w int32, errno int32) + func closeonexec(fd int32) + func setNonblock(fd int32) + ++func issetugid() int32 ++ + const ( + _ESRCH = 3 + _ETIMEDOUT = 60 +diff --git a/src/runtime/os_solaris.go b/src/runtime/os_solaris.go +index 89129e5f1a..a9c081d292 100644 +--- a/src/runtime/os_solaris.go ++++ b/src/runtime/os_solaris.go +@@ -264,3 +264,7 @@ func sysvicall6(fn *libcFunc, a1, a2, a3, a4, a5, a6 uintptr) uintptr { + } + return libcall.r1 + } ++ ++func issetugid() int32 { ++ return int32(sysvicall0(&libc_issetugid)) ++} +diff --git a/src/runtime/panic.go b/src/runtime/panic.go +index 615249f33c..e6d787d20d 100644 +--- a/src/runtime/panic.go ++++ b/src/runtime/panic.go +@@ -1166,6 +1166,9 @@ func fatalthrow() { + // 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) { +diff --git a/src/runtime/proc.go b/src/runtime/proc.go +index e1aafffc93..c0b961f13e 100644 +--- a/src/runtime/proc.go ++++ b/src/runtime/proc.go +@@ -634,6 +634,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 +index 0000000000..c11b9c3f01 +--- /dev/null ++++ b/src/runtime/security_aix.go +@@ -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 +index 0000000000..856f50a504 +--- /dev/null ++++ b/src/runtime/security_issetugid.go +@@ -0,0 +1,20 @@ ++// 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 ++// +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 +index 0000000000..181f3a184e +--- /dev/null ++++ b/src/runtime/security_linux.go +@@ -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 +index 0000000000..42c3bf408a +--- /dev/null ++++ b/src/runtime/security_nonunix.go +@@ -0,0 +1,14 @@ ++// 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 !aix && !android && !darwin && !dragonfly && !freebsd && !hurd && !illumos && !ios && !linux && !netbsd && !openbsd && !solaris ++// +build !aix,!android,!darwin,!dragonfly,!freebsd,!hurd,!illumos,!ios,!linux,!netbsd,!openbsd,!solaris ++ ++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 +index 0000000000..7e0c7ad245 +--- /dev/null ++++ b/src/runtime/security_test.go +@@ -0,0 +1,144 @@ ++// 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 aix || android || darwin || dragonfly || freebsd || hurd || illumos || ios || linux || netbsd || openbsd || solaris ++// +build aix android darwin dragonfly freebsd hurd illumos ios linux netbsd openbsd solaris ++ ++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 +index 0000000000..791590b2a6 +--- /dev/null ++++ b/src/runtime/security_unix.go +@@ -0,0 +1,73 @@ ++// 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 aix || android || darwin || dragonfly || freebsd || hurd || illumos || ios || linux || netbsd || openbsd || solaris ++// +build aix android darwin dragonfly freebsd hurd illumos ios linux netbsd openbsd solaris ++ ++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") ++ } ++ } ++} +diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go +index 003c7b0bc8..c13ede9d8a 100644 +--- a/src/runtime/signal_unix.go ++++ b/src/runtime/signal_unix.go +@@ -633,6 +633,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=", _g_.m.id, " sigcode=", c.sigcode(), "\n") + if _g_.m.lockedg != 0 && _g_.m.ncgo > 0 && gp == _g_.m.g0 { + print("signal arrived during cgo execution\n") +diff --git a/src/runtime/sys_darwin.go b/src/runtime/sys_darwin.go +index 06474434c9..6c3527d4ae 100644 +--- a/src/runtime/sys_darwin.go ++++ b/src/runtime/sys_darwin.go +@@ -10,6 +10,7 @@ import "unsafe" + // fn is the raw pc value of the entry point of the desired function. + // Switches to the system stack, if not already there. + // Preserves the calling point as the location where a profiler traceback will begin. ++// + //go:nosplit + func libcCall(fn, arg unsafe.Pointer) int32 { + // Leave caller's PC/SP/G around for traceback. +@@ -247,10 +248,10 @@ func closefd(fd int32) int32 { + } + func close_trampoline() + ++// This is exported via linkname to assembly in runtime/cgo. ++// + //go:nosplit + //go:cgo_unsafe_args +-// +-// This is exported via linkname to assembly in runtime/cgo. + //go:linkname exit + func exit(code int32) { + libcCall(unsafe.Pointer(funcPC(exit_trampoline)), unsafe.Pointer(&code)) +@@ -360,8 +361,13 @@ func sysctl_trampoline() + + //go:nosplit + //go:cgo_unsafe_args +-func fcntl(fd, cmd, arg int32) int32 { +- return libcCall(unsafe.Pointer(funcPC(fcntl_trampoline)), unsafe.Pointer(&fd)) ++func fcntl(fd, cmd, arg int32) (ret int32, errno int32) { ++ args := struct { ++ fd, cmd, arg int32 ++ ret, errno int32 ++ }{fd, cmd, arg, 0, 0} ++ libcCall(unsafe.Pointer(funcPC(fcntl_trampoline)), unsafe.Pointer(&args)) ++ return args.ret, args.errno + } + func fcntl_trampoline() + +@@ -440,10 +446,17 @@ func closeonexec(fd int32) { + + //go:nosplit + func setNonblock(fd int32) { +- flags := fcntl(fd, _F_GETFL, 0) +- fcntl(fd, _F_SETFL, flags|_O_NONBLOCK) ++ flags, _ := fcntl(fd, _F_GETFL, 0) ++ if flags != -1 { ++ fcntl(fd, _F_SETFL, flags|_O_NONBLOCK) ++ } + } + ++func issetugid() int32 { ++ return libcCall(unsafe.Pointer(funcPC(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. + +@@ -495,3 +508,5 @@ func setNonblock(fd int32) { + //go:cgo_import_dynamic _ _ "/usr/lib/libSystem.B.dylib" + //go:cgo_import_dynamic _ _ "/System/Library/Frameworks/Security.framework/Versions/A/Security" + //go:cgo_import_dynamic _ _ "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation" ++ ++//go:cgo_import_dynamic libc_issetugid issetugid "/usr/lib/libSystem.B.dylib" +diff --git a/src/runtime/sys_darwin_amd64.s b/src/runtime/sys_darwin_amd64.s +index 825852d673..85d0e40873 100644 +--- a/src/runtime/sys_darwin_amd64.s ++++ b/src/runtime/sys_darwin_amd64.s +@@ -851,3 +851,10 @@ TEXT runtime·syscallNoErr(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 +diff --git a/src/runtime/sys_darwin_arm64.s b/src/runtime/sys_darwin_arm64.s +index 585d4f2c64..fa3f9d8579 100644 +--- a/src/runtime/sys_darwin_arm64.s ++++ b/src/runtime/sys_darwin_arm64.s +@@ -693,3 +693,7 @@ TEXT runtime·syscall6X(SB),NOSPLIT,$0 + MOVD R0, 72(R2) // save err + ok: + RET ++ ++TEXT runtime·issetugid_trampoline(SB),NOSPLIT,$0 ++ BL libc_issetugid(SB) ++ RET +\ No newline at end of file +diff --git a/src/runtime/sys_dragonfly_amd64.s b/src/runtime/sys_dragonfly_amd64.s +index 580633af55..ea04d0eae7 100644 +--- a/src/runtime/sys_dragonfly_amd64.s ++++ b/src/runtime/sys_dragonfly_amd64.s +@@ -405,3 +405,13 @@ TEXT runtime·setNonblock(SB),NOSPLIT,$0-4 + 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 +diff --git a/src/runtime/sys_freebsd_386.s b/src/runtime/sys_freebsd_386.s +index c346e719e1..f725d2b291 100644 +--- a/src/runtime/sys_freebsd_386.s ++++ b/src/runtime/sys_freebsd_386.s +@@ -464,3 +464,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 $253, AX ++ INT $0x80 ++ MOVL AX, ret+0(FP) ++ RET +diff --git a/src/runtime/sys_freebsd_amd64.s b/src/runtime/sys_freebsd_amd64.s +index 010b2ec4d4..0eb0a03458 100644 +--- a/src/runtime/sys_freebsd_amd64.s ++++ b/src/runtime/sys_freebsd_amd64.s +@@ -502,3 +502,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 $253, AX ++ SYSCALL ++ MOVL AX, ret+0(FP) ++ RET +diff --git a/src/runtime/sys_freebsd_arm.s b/src/runtime/sys_freebsd_arm.s +index 1e12f9cfcb..c31b77cb99 100644 +--- a/src/runtime/sys_freebsd_arm.s ++++ b/src/runtime/sys_freebsd_arm.s +@@ -28,6 +28,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) +@@ -470,3 +471,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 +diff --git a/src/runtime/sys_freebsd_arm64.s b/src/runtime/sys_freebsd_arm64.s +index 2330f2ffe2..af87220976 100644 +--- a/src/runtime/sys_freebsd_arm64.s ++++ b/src/runtime/sys_freebsd_arm64.s +@@ -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 +@@ -536,3 +537,10 @@ TEXT runtime·getpfr0(SB),NOSPLIT,$0 + MRS ID_AA64PFR0_EL1, R0 + MOVD R0, ret+0(FP) + RET ++ ++// func issetugid() int32 ++TEXT runtime·issetugid(SB),NOSPLIT|NOFRAME,$0 ++ MOVD $SYS_issetugid, R8 ++ SVC ++ MOVW R0, ret+0(FP) ++ RET +diff --git a/src/runtime/sys_netbsd_386.s b/src/runtime/sys_netbsd_386.s +index d0c470c457..59f43cfcc9 100644 +--- a/src/runtime/sys_netbsd_386.s ++++ b/src/runtime/sys_netbsd_386.s +@@ -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 +@@ -497,3 +498,10 @@ TEXT runtime·setNonblock(SB),NOSPLIT,$16-4 + MOVL $92, AX // fcntl + INT $0x80 + RET ++ ++// func issetugid() int32 ++TEXT runtime·issetugid(SB),NOSPLIT,$0 ++ MOVL $SYS_issetugid, AX ++ INT $0x80 ++ MOVL AX, ret+0(FP) ++ RET +diff --git a/src/runtime/sys_netbsd_amd64.s b/src/runtime/sys_netbsd_amd64.s +index dc9bd127d2..9e780147f4 100644 +--- a/src/runtime/sys_netbsd_amd64.s ++++ b/src/runtime/sys_netbsd_amd64.s +@@ -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 +@@ -466,3 +467,13 @@ TEXT runtime·setNonblock(SB),NOSPLIT,$0-4 + 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 $SYS_issetugid, AX ++ SYSCALL ++ MOVL AX, ret+0(FP) ++ RET +diff --git a/src/runtime/sys_netbsd_arm.s b/src/runtime/sys_netbsd_arm.s +index 678dea57c6..91fb22f08c 100644 +--- a/src/runtime/sys_netbsd_arm.s ++++ b/src/runtime/sys_netbsd_arm.s +@@ -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 +@@ -439,3 +440,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 +diff --git a/src/runtime/sys_netbsd_arm64.s b/src/runtime/sys_netbsd_arm64.s +index e70be0fa74..b263662079 100644 +--- a/src/runtime/sys_netbsd_arm64.s ++++ b/src/runtime/sys_netbsd_arm64.s +@@ -32,6 +32,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 +@@ -474,3 +475,9 @@ TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4 + MOVD $F_SETFL, R1 // arg 2 - cmd + SVC $SYS_fcntl + RET ++ ++// func issetugid() int32 ++TEXT runtime·issetugid(SB),NOSPLIT|NOFRAME,$0 ++ SVC $SYS_issetugid ++ MOVW R0, ret+0(FP) ++ RET +diff --git a/src/runtime/sys_openbsd_386.s b/src/runtime/sys_openbsd_386.s +index 24fbfd6266..72637d19a5 100644 +--- a/src/runtime/sys_openbsd_386.s ++++ b/src/runtime/sys_openbsd_386.s +@@ -459,3 +459,12 @@ TEXT runtime·setNonblock(SB),NOSPLIT,$16-4 + RET + + GLOBL runtime·tlsoffset(SB),NOPTR,$4 ++ ++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 +diff --git a/src/runtime/sys_openbsd_amd64.s b/src/runtime/sys_openbsd_amd64.s +index 37d70ab9aa..9cb49a6229 100644 +--- a/src/runtime/sys_openbsd_amd64.s ++++ b/src/runtime/sys_openbsd_amd64.s +@@ -414,3 +414,9 @@ TEXT runtime·setNonblock(SB),NOSPLIT,$0-4 + MOVL $92, AX // fcntl + SYSCALL + 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 +diff --git a/src/runtime/sys_openbsd_arm.s b/src/runtime/sys_openbsd_arm.s +index 9e18ce0e16..5467cf83b4 100644 +--- a/src/runtime/sys_openbsd_arm.s ++++ b/src/runtime/sys_openbsd_arm.s +@@ -433,3 +433,12 @@ TEXT runtime·read_tls_fallback(SB),NOSPLIT|NOFRAME,$0 + INVOKE_SYSCALL + MOVM.IAW (R13), [R1, R2, R3, R12] + 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 +diff --git a/src/runtime/sys_openbsd_arm64.s b/src/runtime/sys_openbsd_arm64.s +index 621b1b1a42..00b7864ada 100644 +--- a/src/runtime/sys_openbsd_arm64.s ++++ b/src/runtime/sys_openbsd_arm64.s +@@ -446,3 +446,9 @@ TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4 + MOVD $92, R8 // sys_fcntl + INVOKE_SYSCALL + 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 +diff --git a/src/runtime/syscall2_solaris.go b/src/runtime/syscall2_solaris.go +index e098e8006a..426481bab5 100644 +--- a/src/runtime/syscall2_solaris.go ++++ b/src/runtime/syscall2_solaris.go +@@ -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 +diff --git a/src/runtime/syscall_solaris.go b/src/runtime/syscall_solaris.go +index 094516927f..aff1504489 100644 +--- a/src/runtime/syscall_solaris.go ++++ b/src/runtime/syscall_solaris.go +@@ -22,6 +22,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 +index 0000000000..1949d2d666 +--- /dev/null ++++ b/src/runtime/testdata/testsuid/main.go +@@ -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") ++} +-- +2.33.0 + diff --git a/golang.spec b/golang.spec index d5a256711b0968be455ffe2573c04182d544447b..6dc7966db3d693b742937b363817994a6714fcce 100644 --- a/golang.spec +++ b/golang.spec @@ -58,7 +58,7 @@ Name: golang Version: 1.15.7 -Release: 27 +Release: 28 Summary: The Go Programming Language License: BSD and Public Domain URL: https://golang.org/ @@ -240,6 +240,10 @@ Patch6095: 0095-runtime-encapsulate-access-to-allgs.patch Patch6096: 0096-Backport-html-template-emit-filterFailsafe-for-empty.patch Patch6097: 0097-Backport-html-template-handle-all-JS-whitespace-char.patch Patch6098: 0098-Backport-html-template-disallow-angle-brackets-in-CS.patch +Patch6099: 0099-Backport-cmd-go-disallow-package-directories-contain.patch +Patch6100: 0100-Backport-cmd-go-enforce-flags-with-non-optional-argu.patch +Patch6101: 0101-Backport-cmd-go-cmd-cgo-in-_cgo_flags-use-one-line-p.patch +#Patch6102: 0102-Backport-runtime-implement-SUID-SGID-protections.patch Patch9001: 0001-drop-hard-code-cert.patch Patch9002: 0002-fix-patch-cmd-go-internal-modfetch-do-not-sho.patch @@ -479,6 +483,12 @@ fi %files devel -f go-tests.list -f go-misc.list -f go-src.list %changelog +* Wed Jun 28 2023 hanchao - 1.15.7-28 +- Type:CVE +- CVE:CVE-2023-29402,CVE-2023-29404,CVE-2023-29405,CVE-2023-29403 +- SUG:NA +- DESC: fix CVE-2023-29402,CVE-2023-29404,CVE-2023-29405,CVE-2023-29403 + * Mon May 22 2023 hanchao - 1.15.7-27 - Type:CVE - CVE:CVE-2023-29400,CVE-2023-24539,CVE-2023-24540 @@ -527,7 +537,7 @@ fi - SUG:NA - DESC:fix bad %goroot}/lib/ macro -* Thu Spe 15 2022 hanchao - 1.15.7-19 +* Thu Sep 15 2022 hanchao - 1.15.7-19 - Type:CVE - CVE:CVE-2022-27664 - SUG:NA