1546 lines
49 KiB
Diff
1546 lines
49 KiB
Diff
From 192832317100ea3e708d15c7ddcc0144ac75a1b4 Mon Sep 17 00:00:00 2001
|
|
From: Michael Pratt <mpratt@google.com>
|
|
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 <dneil@google.com>
|
|
Reviewed-by: Ian Lance Taylor <iant@google.com>
|
|
Run-TryBot: Roland Shoemaker <bracewell@google.com>
|
|
Reviewed-by: Russ Cox <rsc@google.com>
|
|
(cherry picked from commit 87065663ea6d89cd54f65a515d8f2ed0ef285c19)
|
|
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1902231
|
|
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1904340
|
|
Reviewed-by: Michael Knyszek <mknyszek@google.com>
|
|
Reviewed-on: https://go-review.googlesource.com/c/go/+/501228
|
|
Auto-Submit: Michael Knyszek <mknyszek@google.com>
|
|
TryBot-Result: Gopher Robot <gobot@golang.org>
|
|
Run-TryBot: David Chase <drchase@google.com>
|
|
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 <sys/sysctl.h>
|
|
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 <sys/sysctl.h>
|
|
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
|
|
|