diff --git a/0043-Backport-runtime-implement-SUID-SGID-protections.patch b/0043-Backport-runtime-implement-SUID-SGID-protections.patch new file mode 100644 index 0000000000000000000000000000000000000000..422b9ee34dbcecd42bbc751b166d9e84e7130756 --- /dev/null +++ b/0043-Backport-runtime-implement-SUID-SGID-protections.patch @@ -0,0 +1,1545 @@ +From 192832317100ea3e708d15c7ddcc0144ac75a1b4 Mon Sep 17 00:00:00 2001 +From: Michael Pratt +Date: Tue, 8 Feb 2022 16:45:14 -0500 +Subject: [PATCH 1/4] [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/base/base.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 | 19 --- + src/runtime/nbpipe_fcntl_unix_test.go | 18 --- + 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 | 14 ++ + src/runtime/os_netbsd.go | 2 + + src/runtime/os_openbsd_syscall2.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 | 22 ++- + 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_openbsd2.go | 10 ++ + 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/sys_openbsd_mips64.s | 7 + + src/runtime/syscall2_solaris.go | 2 + + src/runtime/syscall_solaris.go | 1 + + src/runtime/testdata/testsuid/main.go | 25 +++ + 55 files changed, 756 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/base/base.go b/src/cmd/compile/internal/base/base.go +index 4c2516f60e..5fb718571f 100644 +--- a/src/cmd/compile/internal/base/base.go ++++ b/src/cmd/compile/internal/base/base.go +@@ -62,8 +62,9 @@ func Compiling(pkgs []string) bool { + // at best instrumentation would cause infinite recursion. + var NoInstrumentPkgs = []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 45e2f25df7..610fba2da9 100644 +--- a/src/go/build/deps_test.go ++++ b/src/go/build/deps_test.go +@@ -86,6 +86,7 @@ var depsRules = ` + < internal/itoa + < 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 215e234286..b5841acc66 100644 +--- a/src/runtime/export_unix_test.go ++++ b/src/runtime/export_unix_test.go +@@ -12,6 +12,7 @@ import "unsafe" + var NonblockingPipe = nonblockingPipe + 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 48e1e6603b..a4c6f86f57 100644 +--- a/src/runtime/extern.go ++++ b/src/runtime/extern.go +@@ -183,6 +183,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 076a722eb7..0000000000 +--- a/src/runtime/nbpipe_fcntl_libc_test.go ++++ /dev/null +@@ -1,19 +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. +- +-//go:build aix || darwin || solaris +-// +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 6d5e4ff021..0000000000 +--- a/src/runtime/nbpipe_fcntl_unix_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. +- +-//go:build dragonfly || freebsd || linux || netbsd || openbsd +-// +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 1d6a9b525c..97ed730991 100644 +--- a/src/runtime/nbpipe_test.go ++++ b/src/runtime/nbpipe_test.go +@@ -15,23 +15,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) { +@@ -50,8 +56,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) +@@ -60,8 +66,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 4d77f0de6d..08de0cc54f 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 4fb1c8e845..fe5c6cbc31 100644 +--- a/src/runtime/os_aix.go ++++ b/src/runtime/os_aix.go +@@ -360,3 +360,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 5c688a3109..c0224702cb 100644 +--- a/src/runtime/os_dragonfly.go ++++ b/src/runtime/os_dragonfly.go +@@ -66,6 +66,8 @@ func pipe2(flags int32) (r, w int32, errno int32) + func closeonexec(fd int32) + func setNonblock(fd int32) + ++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 09dd50ce59..ab9915bec1 100644 +--- a/src/runtime/os_freebsd.go ++++ b/src/runtime/os_freebsd.go +@@ -51,6 +51,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 c8b29e396c..266a7d4b8d 100644 +--- a/src/runtime/os_linux.go ++++ b/src/runtime/os_linux.go +@@ -6,6 +6,7 @@ package runtime + + import ( + "runtime/internal/sys" ++ "runtime/internal/syscall" + "unsafe" + ) + +@@ -183,6 +184,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 + ) +@@ -252,6 +254,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 { +@@ -264,6 +269,9 @@ func sysauxv(auxv []uintptr) int { + + case _AT_PAGESZ: + physPageSize = val ++ ++ case _AT_SECURE: ++ secureMode = val == 1 + } + + archauxv(tag, val) +@@ -419,6 +427,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) { +diff --git a/src/runtime/os_netbsd.go b/src/runtime/os_netbsd.go +index 6fbb3aa694..a38eed01bc 100644 +--- a/src/runtime/os_netbsd.go ++++ b/src/runtime/os_netbsd.go +@@ -82,6 +82,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_openbsd_syscall2.go b/src/runtime/os_openbsd_syscall2.go +index af1997131f..00af61734a 100644 +--- a/src/runtime/os_openbsd_syscall2.go ++++ b/src/runtime/os_openbsd_syscall2.go +@@ -99,3 +99,5 @@ func closeonexec(fd int32) + func setNonblock(fd int32) + + func walltime() (sec int64, nsec int32) ++ ++func issetugid() int32 +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 f6c38aafcc..542ccb1989 100644 +--- a/src/runtime/panic.go ++++ b/src/runtime/panic.go +@@ -1248,6 +1248,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 a144b36ec8..e1fe26b9a7 100644 +--- a/src/runtime/proc.go ++++ b/src/runtime/proc.go +@@ -705,6 +705,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 6096760b50..ca171ac2b8 100644 +--- a/src/runtime/signal_unix.go ++++ b/src/runtime/signal_unix.go +@@ -626,6 +626,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 0f91685d6c..2c45329c79 100644 +--- a/src/runtime/sys_darwin.go ++++ b/src/runtime/sys_darwin.go +@@ -339,8 +339,13 @@ func sysctlbyname_trampoline() + + //go:nosplit + //go:cgo_unsafe_args +-func fcntl(fd, cmd, arg int32) int32 { +- return libcCall(unsafe.Pointer(abi.FuncPCABI0(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(abi.FuncPCABI0(fcntl_trampoline)), unsafe.Pointer(&args)) ++ return args.ret, args.errno + } + func fcntl_trampoline() + +@@ -419,10 +424,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(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. + +@@ -470,3 +482,5 @@ func setNonblock(fd int32) { + //go:cgo_import_dynamic libc_pthread_cond_wait pthread_cond_wait "/usr/lib/libSystem.B.dylib" + //go:cgo_import_dynamic libc_pthread_cond_timedwait_relative_np pthread_cond_timedwait_relative_np "/usr/lib/libSystem.B.dylib" + //go:cgo_import_dynamic libc_pthread_cond_signal pthread_cond_signal "/usr/lib/libSystem.B.dylib" ++ ++//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 3bd027f982..0616fc3f0d 100644 +--- a/src/runtime/sys_darwin_amd64.s ++++ b/src/runtime/sys_darwin_amd64.s +@@ -839,3 +839,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 96d2ed1076..f7b4440f68 100644 +--- a/src/runtime/sys_darwin_arm64.s ++++ b/src/runtime/sys_darwin_arm64.s +@@ -755,3 +755,7 @@ TEXT runtime·syscallNoErr(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 +diff --git a/src/runtime/sys_dragonfly_amd64.s b/src/runtime/sys_dragonfly_amd64.s +index d57bc2a7a4..5912cbe501 100644 +--- a/src/runtime/sys_dragonfly_amd64.s ++++ b/src/runtime/sys_dragonfly_amd64.s +@@ -417,3 +417,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 97e6d9ab36..bd42d96871 100644 +--- a/src/runtime/sys_freebsd_386.s ++++ b/src/runtime/sys_freebsd_386.s +@@ -470,3 +470,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 71a60cae65..fd02aae3d6 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 b12e47c576..bfd176556a 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) +@@ -473,3 +474,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 1aa09e87ca..dcbeafd895 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 +@@ -521,3 +522,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 +diff --git a/src/runtime/sys_netbsd_386.s b/src/runtime/sys_netbsd_386.s +index 8a33894892..bb7d8318bb 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 +@@ -501,3 +502,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 02f5b4ba3b..1fc3f00aa7 100644 +--- a/src/runtime/sys_netbsd_amd64.s ++++ b/src/runtime/sys_netbsd_amd64.s +@@ -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 +@@ -464,3 +465,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 3a763b2a6a..97e615dbbf 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 +@@ -443,3 +444,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 2d0b894d47..cc60d2410d 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 +@@ -479,3 +480,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_openbsd2.go b/src/runtime/sys_openbsd2.go +index cd1a4e879f..a858ae675e 100644 +--- a/src/runtime/sys_openbsd2.go ++++ b/src/runtime/sys_openbsd2.go +@@ -222,6 +222,14 @@ func setNonblock(fd int32) { + fcntl(fd, _F_SETFL, flags|_O_NONBLOCK) + } + ++//go:nosplit ++//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. + +@@ -254,4 +262,6 @@ func setNonblock(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" +diff --git a/src/runtime/sys_openbsd_386.s b/src/runtime/sys_openbsd_386.s +index 7830b61b7d..310ae24ab5 100644 +--- a/src/runtime/sys_openbsd_386.s ++++ b/src/runtime/sys_openbsd_386.s +@@ -966,3 +966,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 +diff --git a/src/runtime/sys_openbsd_amd64.s b/src/runtime/sys_openbsd_amd64.s +index 522e98cf4f..2f04da3ba8 100644 +--- a/src/runtime/sys_openbsd_amd64.s ++++ b/src/runtime/sys_openbsd_amd64.s +@@ -763,3 +763,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 +diff --git a/src/runtime/sys_openbsd_arm.s b/src/runtime/sys_openbsd_arm.s +index 143fcf0518..eddb20529d 100644 +--- a/src/runtime/sys_openbsd_arm.s ++++ b/src/runtime/sys_openbsd_arm.s +@@ -804,3 +804,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 +diff --git a/src/runtime/sys_openbsd_arm64.s b/src/runtime/sys_openbsd_arm64.s +index 9b4acc90a5..ce68c2778d 100644 +--- a/src/runtime/sys_openbsd_arm64.s ++++ b/src/runtime/sys_openbsd_arm64.s +@@ -698,3 +698,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 +diff --git a/src/runtime/sys_openbsd_mips64.s b/src/runtime/sys_openbsd_mips64.s +index f8ae8e7c30..491d356a1b 100644 +--- a/src/runtime/sys_openbsd_mips64.s ++++ b/src/runtime/sys_openbsd_mips64.s +@@ -398,3 +398,10 @@ TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4 + 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 +\ No newline at end of file +diff --git a/src/runtime/syscall2_solaris.go b/src/runtime/syscall2_solaris.go +index 3310489202..0b5ebfda02 100644 +--- a/src/runtime/syscall2_solaris.go ++++ b/src/runtime/syscall2_solaris.go +@@ -22,6 +22,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 +@@ -39,3 +40,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/0044-Backport-cmd-go-disallow-package-directories-contain.patch b/0044-Backport-cmd-go-disallow-package-directories-contain.patch new file mode 100644 index 0000000000000000000000000000000000000000..a6c30e5b3e95ba2c563773be04eb7750acb3e0b1 --- /dev/null +++ b/0044-Backport-cmd-go-disallow-package-directories-contain.patch @@ -0,0 +1,208 @@ +From 8eddf81239a58bbdbb700c8cc46e8e671b20108c Mon Sep 17 00:00:00 2001 +From: "Bryan C. Mills" +Date: Sat, 13 May 2023 02:15:16 +0800 +Subject: [PATCH 2/4] [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. + +Note: The upstream does not submit this change to go1.17 according to the rules of MinorReleases. +Corego3.x are based on go1.17.8. Therefore, it need to submit the change to corego3.x. + +Edited-by: tangxi t00586138 + +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 a83cc9a812..d4da86dbe8 100644 +--- a/src/cmd/go/internal/load/pkg.go ++++ b/src/cmd/go/internal/load/pkg.go +@@ -1897,6 +1897,10 @@ func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk * + 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 5a225fb9f1..2eb8bd681c 100644 +--- a/src/cmd/go/internal/work/exec.go ++++ b/src/cmd/go/internal/work/exec.go +@@ -503,6 +503,12 @@ func (b *Builder) build(ctx context.Context, 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 639e907db0..80b93566ea 100644 +--- a/src/cmd/go/script_test.go ++++ b/src/cmd/go/script_test.go +@@ -181,6 +181,7 @@ func (ts *testScript) setup() { + "devnull=" + os.DevNull, + "goversion=" + goVersion(ts), + ":=" + string(os.PathListSeparator), ++ "newline=\n", + } + if !testenv.HasExternalNetwork() { + ts.env = append(ts.env, "TESTGONETWORK=panic", "TESTGOVCS=panic") +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/0045-Backport-cmd-go-enforce-flags-with-non-optional-argu.patch b/0045-Backport-cmd-go-enforce-flags-with-non-optional-argu.patch new file mode 100644 index 0000000000000000000000000000000000000000..af39afee052e79b50ff416be759771281781cf54 --- /dev/null +++ b/0045-Backport-cmd-go-enforce-flags-with-non-optional-argu.patch @@ -0,0 +1,87 @@ +From 8b67461159daa77c536d2d0a0cbf7f67c742a3e5 Mon Sep 17 00:00:00 2001 +From: Roland Shoemaker +Date: Fri, 5 May 2023 13:10:34 -0700 +Subject: [PATCH 3/4] [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 e9b9f6c6c0..91e6e4c86b 100644 +--- a/src/cmd/go/internal/work/security.go ++++ b/src/cmd/go/internal/work/security.go +@@ -179,10 +179,10 @@ var validLinkerFlags = []*lazyregexp.Regexp{ + re(`-Wl,-berok`), + re(`-Wl,-Bstatic`), + re(`-Wl,-Bsymbolic-functions`), +- 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`), +@@ -191,7 +191,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 8d4be0abfc..3616548d1b 100644 +--- a/src/cmd/go/internal/work/security_test.go ++++ b/src/cmd/go/internal/work/security_test.go +@@ -227,6 +227,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/0046-Backport-cmd-go-cmd-cgo-in-_cgo_flags-use-one-line-p.patch b/0046-Backport-cmd-go-cmd-cgo-in-_cgo_flags-use-one-line-p.patch new file mode 100644 index 0000000000000000000000000000000000000000..46b47309a71cac423a3db02fe15a78f112772151 --- /dev/null +++ b/0046-Backport-cmd-go-cmd-cgo-in-_cgo_flags-use-one-line-p.patch @@ -0,0 +1,120 @@ +From 646a6420b6cdf9dbea6d9e16b67021a7942be8fc Mon Sep 17 00:00:00 2001 +From: Ian Lance Taylor +Date: Thu, 4 May 2023 14:06:39 -0700 +Subject: [PATCH 4/4] [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 94152f4278..62e6528f60 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 1499536932..bb4be2fd10 100644 +--- a/src/cmd/go/internal/work/gccgo.go ++++ b/src/cmd/go/internal/work/gccgo.go +@@ -283,14 +283,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/golang.spec b/golang.spec index 03848d7068b65ded12448640c97afd1ed71e9594..83a1a18b18dc5e0ad37ec18ecb14567f2306996c 100644 --- a/golang.spec +++ b/golang.spec @@ -66,7 +66,7 @@ Name: golang Version: 1.17.3 -Release: 18 +Release: 19 Summary: The Go Programming Language License: BSD and Public Domain URL: https://golang.org/ @@ -200,6 +200,10 @@ Patch6039: 0039-release-branch.go1.19-mime-multipart-limit-parsed-mi.patch Patch6040: 0040-Backport-html-template-emit-filterFailsafe-for-empty.patch Patch6041: 0041-Backport-html-template-handle-all-JS-whitespace-char.patch Patch6042: 0042-Backport-html-template-disallow-angle-brackets-in-CS.patch +Patch6043: 0043-Backport-runtime-implement-SUID-SGID-protections.patch +Patch6044: 0044-Backport-cmd-go-disallow-package-directories-contain.patch +Patch6045: 0045-Backport-cmd-go-enforce-flags-with-non-optional-argu.patch +Patch6046: 0046-Backport-cmd-go-cmd-cgo-in-_cgo_flags-use-one-line-p.patch ExclusiveArch: %{golang_arches} @@ -445,6 +449,12 @@ fi %files devel -f go-tests.list -f go-misc.list -f go-src.list %changelog +* Wed Jun 21 2023 hanchao - 1.17.3-19 +- Type:CVE +- CVE:CVE-2023-29402,CVE-2023-29403,CVE-2023-29404,CVE-2023-29405 +- SUG:NA +- DESC:fix CVE-2023-29402,CVE-2023-29403,CVE-2023-29404,CVE-2023-29405 + * Mon May 22 2023 hanchao - 1.17.3-18 - Type:CVE - CVE:CVE-2023-29400,CVE-2023-24539,CVE-2023-24540