1585 lines
50 KiB
Diff
1585 lines
50 KiB
Diff
From 5da979b4033759d952d64bbc727d1fc45bb785f0 Mon Sep 17 00:00:00 2001
|
|
From: Michael Pratt <mpratt@google.com>
|
|
Date: Tue, 8 Feb 2022 16:45:14 -0500
|
|
Subject: [PATCH] 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/gc/racewalk.go | 3 +-
|
|
src/go/build/deps_test.go | 1 +
|
|
src/runtime/export_darwin_test.go | 7 -
|
|
src/runtime/export_unix_test.go | 1 +
|
|
src/runtime/extern.go | 19 +++
|
|
src/runtime/internal/syscall/asm_linux_386.s | 34 ++++
|
|
.../internal/syscall/asm_linux_amd64.s | 33 ++++
|
|
src/runtime/internal/syscall/asm_linux_arm.s | 32 ++++
|
|
.../internal/syscall/asm_linux_arm64.s | 29 ++++
|
|
.../internal/syscall/defs_linux_386.go | 7 +
|
|
.../internal/syscall/defs_linux_amd64.go | 7 +
|
|
.../internal/syscall/defs_linux_arm.go | 7 +
|
|
.../internal/syscall/defs_linux_arm64.go | 7 +
|
|
src/runtime/internal/syscall/syscall_linux.go | 12 ++
|
|
src/runtime/nbpipe_fcntl_libc_test.go | 18 ---
|
|
src/runtime/nbpipe_fcntl_unix_test.go | 17 --
|
|
src/runtime/nbpipe_test.go | 26 ++--
|
|
src/runtime/os2_aix.go | 12 ++
|
|
src/runtime/os_aix.go | 40 +++++
|
|
src/runtime/os_dragonfly.go | 2 +
|
|
src/runtime/os_freebsd.go | 2 +
|
|
src/runtime/os_linux.go | 25 +++
|
|
src/runtime/os_netbsd.go | 2 +
|
|
src/runtime/os_solaris.go | 4 +
|
|
src/runtime/panic.go | 3 +
|
|
src/runtime/proc.go | 1 +
|
|
src/runtime/security_aix.go | 17 ++
|
|
src/runtime/security_issetugid.go | 19 +++
|
|
src/runtime/security_linux.go | 15 ++
|
|
src/runtime/security_nonunix.go | 13 ++
|
|
src/runtime/security_test.go | 145 ++++++++++++++++++
|
|
src/runtime/security_unix.go | 72 +++++++++
|
|
src/runtime/signal_unix.go | 4 +
|
|
src/runtime/sys_darwin.go | 27 +++-
|
|
src/runtime/sys_darwin_amd64.s | 7 +
|
|
src/runtime/sys_darwin_arm64.s | 4 +
|
|
src/runtime/sys_dragonfly_amd64.s | 10 ++
|
|
src/runtime/sys_freebsd_386.s | 7 +
|
|
src/runtime/sys_freebsd_amd64.s | 10 ++
|
|
src/runtime/sys_freebsd_arm.s | 8 +
|
|
src/runtime/sys_freebsd_arm64.s | 8 +
|
|
src/runtime/sys_netbsd_386.s | 8 +
|
|
src/runtime/sys_netbsd_amd64.s | 11 ++
|
|
src/runtime/sys_netbsd_arm.s | 7 +
|
|
src/runtime/sys_netbsd_arm64.s | 7 +
|
|
src/runtime/sys_openbsd_386.s | 9 ++
|
|
src/runtime/sys_openbsd_amd64.s | 6 +
|
|
src/runtime/sys_openbsd_arm.s | 9 ++
|
|
src/runtime/sys_openbsd_arm64.s | 6 +
|
|
src/runtime/syscall2_solaris.go | 2 +
|
|
src/runtime/syscall_solaris.go | 1 +
|
|
src/runtime/testdata/testsuid/main.go | 25 +++
|
|
52 files changed, 749 insertions(+), 59 deletions(-)
|
|
create mode 100644 src/runtime/internal/syscall/asm_linux_386.s
|
|
create mode 100644 src/runtime/internal/syscall/asm_linux_amd64.s
|
|
create mode 100644 src/runtime/internal/syscall/asm_linux_arm.s
|
|
create mode 100644 src/runtime/internal/syscall/asm_linux_arm64.s
|
|
create mode 100644 src/runtime/internal/syscall/defs_linux_386.go
|
|
create mode 100644 src/runtime/internal/syscall/defs_linux_amd64.go
|
|
create mode 100644 src/runtime/internal/syscall/defs_linux_arm.go
|
|
create mode 100644 src/runtime/internal/syscall/defs_linux_arm64.go
|
|
create mode 100644 src/runtime/internal/syscall/syscall_linux.go
|
|
delete mode 100644 src/runtime/nbpipe_fcntl_libc_test.go
|
|
delete mode 100644 src/runtime/nbpipe_fcntl_unix_test.go
|
|
create mode 100644 src/runtime/security_aix.go
|
|
create mode 100644 src/runtime/security_issetugid.go
|
|
create mode 100644 src/runtime/security_linux.go
|
|
create mode 100644 src/runtime/security_nonunix.go
|
|
create mode 100644 src/runtime/security_test.go
|
|
create mode 100644 src/runtime/security_unix.go
|
|
create mode 100644 src/runtime/testdata/testsuid/main.go
|
|
|
|
diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go
|
|
index 6f25137..710c97a 100644
|
|
--- a/src/cmd/compile/internal/gc/racewalk.go
|
|
+++ b/src/cmd/compile/internal/gc/racewalk.go
|
|
@@ -34,8 +34,9 @@ import (
|
|
// at best instrumentation would cause infinite recursion.
|
|
var omit_pkgs = []string{
|
|
"runtime/internal/atomic",
|
|
- "runtime/internal/sys",
|
|
"runtime/internal/math",
|
|
+ "runtime/internal/sys",
|
|
+ "runtime/internal/syscall",
|
|
"runtime",
|
|
"runtime/race",
|
|
"runtime/msan",
|
|
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
|
|
index 875aceb..c134417 100644
|
|
--- a/src/go/build/deps_test.go
|
|
+++ b/src/go/build/deps_test.go
|
|
@@ -81,6 +81,7 @@ var depsRules = `
|
|
< internal/bytealg
|
|
< internal/unsafeheader
|
|
< runtime/internal/sys
|
|
+ < runtime/internal/syscall
|
|
< runtime/internal/atomic
|
|
< runtime/internal/math
|
|
< runtime
|
|
diff --git a/src/runtime/export_darwin_test.go b/src/runtime/export_darwin_test.go
|
|
index e9b6eb3..034c52d 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 621488e..b1cf15f 100644
|
|
--- a/src/runtime/export_unix_test.go
|
|
+++ b/src/runtime/export_unix_test.go
|
|
@@ -12,6 +12,7 @@ var NonblockingPipe = nonblockingPipe
|
|
var Pipe = pipe
|
|
var SetNonblock = setNonblock
|
|
var Closeonexec = closeonexec
|
|
+var Fcntl = fcntl
|
|
|
|
func sigismember(mask *sigset, i int) bool {
|
|
clear := *mask
|
|
diff --git a/src/runtime/extern.go b/src/runtime/extern.go
|
|
index 7316503..056465c 100644
|
|
--- a/src/runtime/extern.go
|
|
+++ b/src/runtime/extern.go
|
|
@@ -172,6 +172,25 @@ the set of Go environment variables. They influence the building of Go programs
|
|
GOARCH, GOOS, and GOROOT are recorded at compile time and made available by
|
|
constants or functions in this package, but they do not influence the execution
|
|
of the run-time system.
|
|
+
|
|
+# Security
|
|
+
|
|
+On Unix platforms, Go's runtime system behaves slightly differently when a
|
|
+binary is setuid/setgid or executed with setuid/setgid-like properties, in order
|
|
+to prevent dangerous behaviors. On Linux this is determined by checking for the
|
|
+AT_SECURE flag in the auxiliary vector, on the BSDs and Solaris/Illumos it is
|
|
+determined by checking the issetugid syscall, and on AIX it is determined by
|
|
+checking if the uid/gid match the effective uid/gid.
|
|
+
|
|
+When the runtime determines the binary is setuid/setgid-like, it does three main
|
|
+things:
|
|
+ - The standard input/output file descriptors (0, 1, 2) are checked to be open.
|
|
+ If any of them are closed, they are opened pointing at /dev/null.
|
|
+ - The value of the GOTRACEBACK environment variable is set to 'none'.
|
|
+ - When a signal is received that terminates the program, or the program
|
|
+ encounters an unrecoverable panic that would otherwise override the value
|
|
+ of GOTRACEBACK, the goroutine stack, registers, and other memory related
|
|
+ information are omitted.
|
|
*/
|
|
package runtime
|
|
|
|
diff --git a/src/runtime/internal/syscall/asm_linux_386.s b/src/runtime/internal/syscall/asm_linux_386.s
|
|
new file mode 100644
|
|
index 0000000..15aae4d
|
|
--- /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 0000000..961d9bd
|
|
--- /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 0000000..dbf1826
|
|
--- /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 0000000..83e862f
|
|
--- /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 0000000..31d704e
|
|
--- /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 0000000..2368eb0
|
|
--- /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 0000000..31d704e
|
|
--- /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 0000000..6292c90
|
|
--- /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 0000000..06d5f21
|
|
--- /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 b38c583..0000000
|
|
--- a/src/runtime/nbpipe_fcntl_libc_test.go
|
|
+++ /dev/null
|
|
@@ -1,18 +0,0 @@
|
|
-// Copyright 2019 The Go Authors. All rights reserved.
|
|
-// Use of this source code is governed by a BSD-style
|
|
-// license that can be found in the LICENSE file.
|
|
-
|
|
-// +build aix darwin solaris
|
|
-
|
|
-package runtime_test
|
|
-
|
|
-import (
|
|
- "runtime"
|
|
- "syscall"
|
|
-)
|
|
-
|
|
-// Call fcntl libc function rather than calling syscall.
|
|
-func fcntl(fd uintptr, cmd int, arg uintptr) (uintptr, syscall.Errno) {
|
|
- res, errno := runtime.Fcntl(fd, uintptr(cmd), arg)
|
|
- return res, syscall.Errno(errno)
|
|
-}
|
|
diff --git a/src/runtime/nbpipe_fcntl_unix_test.go b/src/runtime/nbpipe_fcntl_unix_test.go
|
|
deleted file mode 100644
|
|
index 75acdb6..0000000
|
|
--- a/src/runtime/nbpipe_fcntl_unix_test.go
|
|
+++ /dev/null
|
|
@@ -1,17 +0,0 @@
|
|
-// Copyright 2019 The Go Authors. All rights reserved.
|
|
-// Use of this source code is governed by a BSD-style
|
|
-// license that can be found in the LICENSE file.
|
|
-
|
|
-// +build dragonfly freebsd linux netbsd openbsd
|
|
-
|
|
-package runtime_test
|
|
-
|
|
-import (
|
|
- "internal/syscall/unix"
|
|
- "syscall"
|
|
-)
|
|
-
|
|
-func fcntl(fd uintptr, cmd int, arg uintptr) (uintptr, syscall.Errno) {
|
|
- res, _, err := syscall.Syscall(unix.FcntlSyscall, fd, uintptr(cmd), arg)
|
|
- return res, err
|
|
-}
|
|
diff --git a/src/runtime/nbpipe_test.go b/src/runtime/nbpipe_test.go
|
|
index d739f57..9496f83 100644
|
|
--- a/src/runtime/nbpipe_test.go
|
|
+++ b/src/runtime/nbpipe_test.go
|
|
@@ -14,23 +14,29 @@ import (
|
|
)
|
|
|
|
func TestNonblockingPipe(t *testing.T) {
|
|
- t.Parallel()
|
|
-
|
|
// NonblockingPipe is the test name for nonblockingPipe.
|
|
r, w, errno := runtime.NonblockingPipe()
|
|
if errno != 0 {
|
|
t.Fatal(syscall.Errno(errno))
|
|
}
|
|
- defer func() {
|
|
- runtime.Close(r)
|
|
- runtime.Close(w)
|
|
- }()
|
|
+ defer runtime.Close(w)
|
|
|
|
checkIsPipe(t, r, w)
|
|
checkNonblocking(t, r, "reader")
|
|
checkCloseonexec(t, r, "reader")
|
|
checkNonblocking(t, w, "writer")
|
|
checkCloseonexec(t, w, "writer")
|
|
+
|
|
+ // Test that fcntl returns an error as expected.
|
|
+ if runtime.Close(r) != 0 {
|
|
+ t.Fatalf("Close(%d) failed", r)
|
|
+ }
|
|
+ val, errno := runtime.Fcntl(r, syscall.F_GETFD, 0)
|
|
+ if val != -1 {
|
|
+ t.Errorf("Fcntl succeeded unexpectedly")
|
|
+ } else if syscall.Errno(errno) != syscall.EBADF {
|
|
+ t.Errorf("Fcntl failed with error %v, expected %v", syscall.Errno(errno), syscall.EBADF)
|
|
+ }
|
|
}
|
|
|
|
func checkIsPipe(t *testing.T, r, w int32) {
|
|
@@ -49,8 +55,8 @@ func checkIsPipe(t *testing.T, r, w int32) {
|
|
|
|
func checkNonblocking(t *testing.T, fd int32, name string) {
|
|
t.Helper()
|
|
- flags, errno := fcntl(uintptr(fd), syscall.F_GETFL, 0)
|
|
- if errno != 0 {
|
|
+ flags, errno := runtime.Fcntl(fd, syscall.F_GETFL, 0)
|
|
+ if flags == -1 {
|
|
t.Errorf("fcntl(%s, F_GETFL) failed: %v", name, syscall.Errno(errno))
|
|
} else if flags&syscall.O_NONBLOCK == 0 {
|
|
t.Errorf("O_NONBLOCK not set in %s flags %#x", name, flags)
|
|
@@ -59,8 +65,8 @@ func checkNonblocking(t *testing.T, fd int32, name string) {
|
|
|
|
func checkCloseonexec(t *testing.T, fd int32, name string) {
|
|
t.Helper()
|
|
- flags, errno := fcntl(uintptr(fd), syscall.F_GETFD, 0)
|
|
- if errno != 0 {
|
|
+ flags, errno := runtime.Fcntl(fd, syscall.F_GETFD, 0)
|
|
+ if flags == -1 {
|
|
t.Errorf("fcntl(%s, F_GETFD) failed: %v", name, syscall.Errno(errno))
|
|
} else if flags&syscall.FD_CLOEXEC == 0 {
|
|
t.Errorf("FD_CLOEXEC not set in %s flags %#x", name, flags)
|
|
diff --git a/src/runtime/os2_aix.go b/src/runtime/os2_aix.go
|
|
index 31ac6dd..8b609aa 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 9a6b8ae..7fb7aaf 100644
|
|
--- a/src/runtime/os_aix.go
|
|
+++ b/src/runtime/os_aix.go
|
|
@@ -375,3 +375,43 @@ func setNonblock(fd int32) {
|
|
flags := fcntl(fd, _F_GETFL, 0)
|
|
fcntl(fd, _F_SETFL, flags|_O_NONBLOCK)
|
|
}
|
|
+
|
|
+//go:nosplit
|
|
+func getuid() int32 {
|
|
+ r, errno := syscall0(&libc_getuid)
|
|
+ if errno != 0 {
|
|
+ print("getuid failed ", errno)
|
|
+ throw("getuid")
|
|
+ }
|
|
+ return int32(r)
|
|
+}
|
|
+
|
|
+//go:nosplit
|
|
+func geteuid() int32 {
|
|
+ r, errno := syscall0(&libc_geteuid)
|
|
+ if errno != 0 {
|
|
+ print("geteuid failed ", errno)
|
|
+ throw("geteuid")
|
|
+ }
|
|
+ return int32(r)
|
|
+}
|
|
+
|
|
+//go:nosplit
|
|
+func getgid() int32 {
|
|
+ r, errno := syscall0(&libc_getgid)
|
|
+ if errno != 0 {
|
|
+ print("getgid failed ", errno)
|
|
+ throw("getgid")
|
|
+ }
|
|
+ return int32(r)
|
|
+}
|
|
+
|
|
+//go:nosplit
|
|
+func getegid() int32 {
|
|
+ r, errno := syscall0(&libc_getegid)
|
|
+ if errno != 0 {
|
|
+ print("getegid failed ", errno)
|
|
+ throw("getegid")
|
|
+ }
|
|
+ return int32(r)
|
|
+}
|
|
diff --git a/src/runtime/os_dragonfly.go b/src/runtime/os_dragonfly.go
|
|
index 6578fcb..df0ff38 100644
|
|
--- a/src/runtime/os_dragonfly.go
|
|
+++ b/src/runtime/os_dragonfly.go
|
|
@@ -62,6 +62,8 @@ func pipe() (r, w int32, errno int32)
|
|
|
|
const stackSystem = 0
|
|
|
|
+func issetugid() int32
|
|
+
|
|
// From DragonFly's <sys/sysctl.h>
|
|
const (
|
|
_CTL_HW = 6
|
|
diff --git a/src/runtime/os_freebsd.go b/src/runtime/os_freebsd.go
|
|
index 730973a..35242c3 100644
|
|
--- a/src/runtime/os_freebsd.go
|
|
+++ b/src/runtime/os_freebsd.go
|
|
@@ -46,6 +46,8 @@ func pipe2(flags int32) (r, w int32, errno int32)
|
|
func closeonexec(fd int32)
|
|
func setNonblock(fd int32)
|
|
|
|
+func issetugid() int32
|
|
+
|
|
// From FreeBSD's <sys/sysctl.h>
|
|
const (
|
|
_CTL_HW = 6
|
|
diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go
|
|
index 7b95ff2..e83b6df 100644
|
|
--- a/src/runtime/os_linux.go
|
|
+++ b/src/runtime/os_linux.go
|
|
@@ -7,6 +7,7 @@ package runtime
|
|
import (
|
|
"runtime/internal/atomic"
|
|
"runtime/internal/sys"
|
|
+ "runtime/internal/syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
@@ -31,9 +32,12 @@ const (
|
|
)
|
|
|
|
// Atomically,
|
|
+//
|
|
// if(*addr == val) sleep
|
|
+//
|
|
// Might be woken up spuriously; that's allowed.
|
|
// Don't sleep longer than ns; ns < 0 means forever.
|
|
+//
|
|
//go:nosplit
|
|
func futexsleep(addr *uint32, val uint32, ns int64) {
|
|
// Some Linux kernels have a bug where futex of
|
|
@@ -52,6 +56,7 @@ func futexsleep(addr *uint32, val uint32, ns int64) {
|
|
}
|
|
|
|
// If any procs are sleeping on addr, wake up at most cnt.
|
|
+//
|
|
//go:nosplit
|
|
func futexwakeup(addr *uint32, cnt uint32) {
|
|
ret := futex(unsafe.Pointer(addr), _FUTEX_WAKE_PRIVATE, cnt, nil, nil, 0)
|
|
@@ -136,6 +141,7 @@ const (
|
|
func clone(flags int32, stk, mp, gp, fn unsafe.Pointer) int32
|
|
|
|
// May run with m.p==nil, so write barriers are not allowed.
|
|
+//
|
|
//go:nowritebarrier
|
|
func newosproc(mp *m) {
|
|
stk := unsafe.Pointer(mp.g0.stack.hi)
|
|
@@ -163,6 +169,7 @@ func newosproc(mp *m) {
|
|
}
|
|
|
|
// Version of newosproc that doesn't require a valid G.
|
|
+//
|
|
//go:nosplit
|
|
func newosproc0(stacksize uintptr, fn unsafe.Pointer) {
|
|
stack := sysAlloc(stacksize, &memstats.stacks_sys)
|
|
@@ -184,6 +191,7 @@ const (
|
|
_AT_NULL = 0 // End of vector
|
|
_AT_PAGESZ = 6 // System physical page size
|
|
_AT_HWCAP = 16 // hardware capability bit vector
|
|
+ _AT_SECURE = 23 // secure mode boolean
|
|
_AT_RANDOM = 25 // introduced in 2.6.29
|
|
_AT_HWCAP2 = 26 // hardware capability bit vector 2
|
|
)
|
|
@@ -249,6 +257,9 @@ func sysargs(argc int32, argv **byte) {
|
|
sysauxv(buf[:])
|
|
}
|
|
|
|
+// secureMode holds the value of AT_SECURE passed in the auxiliary vector.
|
|
+var secureMode bool
|
|
+
|
|
func sysauxv(auxv []uintptr) int {
|
|
var i int
|
|
for ; auxv[i] != _AT_NULL; i += 2 {
|
|
@@ -261,6 +272,9 @@ func sysauxv(auxv []uintptr) int {
|
|
|
|
case _AT_PAGESZ:
|
|
physPageSize = val
|
|
+
|
|
+ case _AT_SECURE:
|
|
+ secureMode = val == 1
|
|
}
|
|
|
|
archauxv(tag, val)
|
|
@@ -322,6 +336,7 @@ func goenvs() {
|
|
// Called to do synchronous initialization of Go code built with
|
|
// -buildmode=c-archive or -buildmode=c-shared.
|
|
// None of the Go runtime is initialized.
|
|
+//
|
|
//go:nosplit
|
|
//go:nowritebarrierrec
|
|
func libpreinit() {
|
|
@@ -358,6 +373,7 @@ func minit() {
|
|
}
|
|
|
|
// Called from dropm to undo the effect of an minit.
|
|
+//
|
|
//go:nosplit
|
|
func unminit() {
|
|
unminitSignals()
|
|
@@ -397,6 +413,12 @@ func pipe() (r, w int32, errno int32)
|
|
func pipe2(flags int32) (r, w int32, errno int32)
|
|
func setNonblock(fd int32)
|
|
|
|
+//go:nosplit
|
|
+func fcntl(fd, cmd, arg int32) (ret int32, errno int32) {
|
|
+ r, _, err := syscall.Syscall6(syscall.SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg), 0, 0, 0)
|
|
+ return int32(r), int32(err)
|
|
+}
|
|
+
|
|
//go:nosplit
|
|
//go:nowritebarrierrec
|
|
func setsig(i uint32, fn uintptr) {
|
|
@@ -441,6 +463,7 @@ func getsig(i uint32) uintptr {
|
|
}
|
|
|
|
// setSignaltstackSP sets the ss_sp field of a stackt.
|
|
+//
|
|
//go:nosplit
|
|
func setSignalstackSP(s *stackt, sp uintptr) {
|
|
*(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp
|
|
@@ -451,6 +474,7 @@ func (c *sigctxt) fixsigcode(sig uint32) {
|
|
}
|
|
|
|
// sysSigaction calls the rt_sigaction system call.
|
|
+//
|
|
//go:nosplit
|
|
func sysSigaction(sig uint32, new, old *sigactiont) {
|
|
if rt_sigaction(uintptr(sig), new, old, unsafe.Sizeof(sigactiont{}.sa_mask)) != 0 {
|
|
@@ -475,6 +499,7 @@ func sysSigaction(sig uint32, new, old *sigactiont) {
|
|
}
|
|
|
|
// rt_sigaction is implemented in assembly.
|
|
+//
|
|
//go:noescape
|
|
func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32
|
|
|
|
diff --git a/src/runtime/os_netbsd.go b/src/runtime/os_netbsd.go
|
|
index 97106c7..748e324 100644
|
|
--- a/src/runtime/os_netbsd.go
|
|
+++ b/src/runtime/os_netbsd.go
|
|
@@ -77,6 +77,8 @@ func pipe2(flags int32) (r, w int32, errno int32)
|
|
func closeonexec(fd int32)
|
|
func setNonblock(fd int32)
|
|
|
|
+func issetugid() int32
|
|
+
|
|
const (
|
|
_ESRCH = 3
|
|
_ETIMEDOUT = 60
|
|
diff --git a/src/runtime/os_solaris.go b/src/runtime/os_solaris.go
|
|
index 89129e5..a9c081d 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 615249f..e6d787d 100644
|
|
--- a/src/runtime/panic.go
|
|
+++ b/src/runtime/panic.go
|
|
@@ -1166,6 +1166,9 @@ func fatalthrow() {
|
|
// Switch to the system stack to avoid any stack growth, which
|
|
// may make things worse if the runtime is in a bad state.
|
|
systemstack(func() {
|
|
+ if isSecureMode() {
|
|
+ exit(2)
|
|
+ }
|
|
startpanic_m()
|
|
|
|
if dopanic_m(gp, pc, sp) {
|
|
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
|
|
index e1aafff..c0b961f 100644
|
|
--- a/src/runtime/proc.go
|
|
+++ b/src/runtime/proc.go
|
|
@@ -634,6 +634,7 @@ func schedinit() {
|
|
|
|
goargs()
|
|
goenvs()
|
|
+ secure()
|
|
parsedebugvars()
|
|
gcinit()
|
|
|
|
diff --git a/src/runtime/security_aix.go b/src/runtime/security_aix.go
|
|
new file mode 100644
|
|
index 0000000..c11b9c3
|
|
--- /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 0000000..ee1aa67
|
|
--- /dev/null
|
|
+++ b/src/runtime/security_issetugid.go
|
|
@@ -0,0 +1,19 @@
|
|
+// Copyright 2023 The Go Authors. All rights reserved.
|
|
+// Use of this source code is governed by a BSD-style
|
|
+// license that can be found in the LICENSE file.
|
|
+
|
|
+// +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 0000000..181f3a1
|
|
--- /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 0000000..19d16ea
|
|
--- /dev/null
|
|
+++ b/src/runtime/security_nonunix.go
|
|
@@ -0,0 +1,13 @@
|
|
+// Copyright 2023 The Go Authors. All rights reserved.
|
|
+// Use of this source code is governed by a BSD-style
|
|
+// license that can be found in the LICENSE file.
|
|
+
|
|
+// +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 0000000..5653e4f
|
|
--- /dev/null
|
|
+++ b/src/runtime/security_test.go
|
|
@@ -0,0 +1,145 @@
|
|
+// 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"
|
|
+ "io/ioutil"
|
|
+ "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.Create(filepath.Join(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 := ioutil.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 0000000..724471c
|
|
--- /dev/null
|
|
+++ b/src/runtime/security_unix.go
|
|
@@ -0,0 +1,72 @@
|
|
+// Copyright 2023 The Go Authors. All rights reserved.
|
|
+// Use of this source code is governed by a BSD-style
|
|
+// license that can be found in the LICENSE file.
|
|
+
|
|
+// +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 003c7b0..c13ede9 100644
|
|
--- a/src/runtime/signal_unix.go
|
|
+++ b/src/runtime/signal_unix.go
|
|
@@ -633,6 +633,10 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
|
|
print("Signal ", sig, "\n")
|
|
}
|
|
|
|
+ if isSecureMode() {
|
|
+ exit(2)
|
|
+ }
|
|
+
|
|
print("PC=", hex(c.sigpc()), " m=", _g_.m.id, " sigcode=", c.sigcode(), "\n")
|
|
if _g_.m.lockedg != 0 && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
|
|
print("signal arrived during cgo execution\n")
|
|
diff --git a/src/runtime/sys_darwin.go b/src/runtime/sys_darwin.go
|
|
index 0647443..6c3527d 100644
|
|
--- a/src/runtime/sys_darwin.go
|
|
+++ b/src/runtime/sys_darwin.go
|
|
@@ -10,6 +10,7 @@ import "unsafe"
|
|
// fn is the raw pc value of the entry point of the desired function.
|
|
// Switches to the system stack, if not already there.
|
|
// Preserves the calling point as the location where a profiler traceback will begin.
|
|
+//
|
|
//go:nosplit
|
|
func libcCall(fn, arg unsafe.Pointer) int32 {
|
|
// Leave caller's PC/SP/G around for traceback.
|
|
@@ -247,10 +248,10 @@ func closefd(fd int32) int32 {
|
|
}
|
|
func close_trampoline()
|
|
|
|
+// This is exported via linkname to assembly in runtime/cgo.
|
|
+//
|
|
//go:nosplit
|
|
//go:cgo_unsafe_args
|
|
-//
|
|
-// This is exported via linkname to assembly in runtime/cgo.
|
|
//go:linkname exit
|
|
func exit(code int32) {
|
|
libcCall(unsafe.Pointer(funcPC(exit_trampoline)), unsafe.Pointer(&code))
|
|
@@ -360,8 +361,13 @@ func sysctl_trampoline()
|
|
|
|
//go:nosplit
|
|
//go:cgo_unsafe_args
|
|
-func fcntl(fd, cmd, arg int32) int32 {
|
|
- return libcCall(unsafe.Pointer(funcPC(fcntl_trampoline)), unsafe.Pointer(&fd))
|
|
+func fcntl(fd, cmd, arg int32) (ret int32, errno int32) {
|
|
+ args := struct {
|
|
+ fd, cmd, arg int32
|
|
+ ret, errno int32
|
|
+ }{fd, cmd, arg, 0, 0}
|
|
+ libcCall(unsafe.Pointer(funcPC(fcntl_trampoline)), unsafe.Pointer(&args))
|
|
+ return args.ret, args.errno
|
|
}
|
|
func fcntl_trampoline()
|
|
|
|
@@ -440,10 +446,17 @@ func closeonexec(fd int32) {
|
|
|
|
//go:nosplit
|
|
func setNonblock(fd int32) {
|
|
- flags := fcntl(fd, _F_GETFL, 0)
|
|
- fcntl(fd, _F_SETFL, flags|_O_NONBLOCK)
|
|
+ flags, _ := fcntl(fd, _F_GETFL, 0)
|
|
+ if flags != -1 {
|
|
+ fcntl(fd, _F_SETFL, flags|_O_NONBLOCK)
|
|
+ }
|
|
}
|
|
|
|
+func issetugid() int32 {
|
|
+ return libcCall(unsafe.Pointer(funcPC(issetugid_trampoline)), nil)
|
|
+}
|
|
+func issetugid_trampoline()
|
|
+
|
|
// Tell the linker that the libc_* functions are to be found
|
|
// in a system library, with the libc_ prefix missing.
|
|
|
|
@@ -495,3 +508,5 @@ func setNonblock(fd int32) {
|
|
//go:cgo_import_dynamic _ _ "/usr/lib/libSystem.B.dylib"
|
|
//go:cgo_import_dynamic _ _ "/System/Library/Frameworks/Security.framework/Versions/A/Security"
|
|
//go:cgo_import_dynamic _ _ "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
|
|
+
|
|
+//go:cgo_import_dynamic libc_issetugid issetugid "/usr/lib/libSystem.B.dylib"
|
|
diff --git a/src/runtime/sys_darwin_amd64.s b/src/runtime/sys_darwin_amd64.s
|
|
index 825852d..85d0e40 100644
|
|
--- a/src/runtime/sys_darwin_amd64.s
|
|
+++ b/src/runtime/sys_darwin_amd64.s
|
|
@@ -851,3 +851,10 @@ TEXT runtime·syscallNoErr(SB),NOSPLIT,$0
|
|
MOVQ BP, SP
|
|
POPQ BP
|
|
RET
|
|
+
|
|
+TEXT runtime·issetugid_trampoline(SB),NOSPLIT,$0
|
|
+ PUSHQ BP
|
|
+ MOVQ SP, BP
|
|
+ CALL libc_issetugid(SB)
|
|
+ POPQ BP
|
|
+ RET
|
|
diff --git a/src/runtime/sys_darwin_arm64.s b/src/runtime/sys_darwin_arm64.s
|
|
index 585d4f2..fa3f9d8 100644
|
|
--- a/src/runtime/sys_darwin_arm64.s
|
|
+++ b/src/runtime/sys_darwin_arm64.s
|
|
@@ -693,3 +693,7 @@ TEXT runtime·syscall6X(SB),NOSPLIT,$0
|
|
MOVD R0, 72(R2) // save err
|
|
ok:
|
|
RET
|
|
+
|
|
+TEXT runtime·issetugid_trampoline(SB),NOSPLIT,$0
|
|
+ BL libc_issetugid(SB)
|
|
+ RET
|
|
\ No newline at end of file
|
|
diff --git a/src/runtime/sys_dragonfly_amd64.s b/src/runtime/sys_dragonfly_amd64.s
|
|
index 580633a..ea04d0e 100644
|
|
--- a/src/runtime/sys_dragonfly_amd64.s
|
|
+++ b/src/runtime/sys_dragonfly_amd64.s
|
|
@@ -405,3 +405,13 @@ TEXT runtime·setNonblock(SB),NOSPLIT,$0-4
|
|
MOVL $92, AX // fcntl
|
|
SYSCALL
|
|
RET
|
|
+
|
|
+// func issetugid() int32
|
|
+TEXT runtime·issetugid(SB),NOSPLIT,$0
|
|
+ MOVQ $0, DI
|
|
+ MOVQ $0, SI
|
|
+ MOVQ $0, DX
|
|
+ MOVL $253, AX
|
|
+ SYSCALL
|
|
+ MOVL AX, ret+0(FP)
|
|
+ RET
|
|
diff --git a/src/runtime/sys_freebsd_386.s b/src/runtime/sys_freebsd_386.s
|
|
index c346e71..f725d2b 100644
|
|
--- a/src/runtime/sys_freebsd_386.s
|
|
+++ b/src/runtime/sys_freebsd_386.s
|
|
@@ -464,3 +464,10 @@ TEXT runtime·cpuset_getaffinity(SB), NOSPLIT, $0-28
|
|
RET
|
|
|
|
GLOBL runtime·tlsoffset(SB),NOPTR,$4
|
|
+
|
|
+// func issetugid() int32
|
|
+TEXT runtime·issetugid(SB),NOSPLIT,$0
|
|
+ MOVL $253, AX
|
|
+ INT $0x80
|
|
+ MOVL AX, ret+0(FP)
|
|
+ RET
|
|
diff --git a/src/runtime/sys_freebsd_amd64.s b/src/runtime/sys_freebsd_amd64.s
|
|
index 010b2ec..0eb0a03 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 1e12f9c..c31b77c 100644
|
|
--- a/src/runtime/sys_freebsd_arm.s
|
|
+++ b/src/runtime/sys_freebsd_arm.s
|
|
@@ -28,6 +28,7 @@
|
|
#define SYS_fcntl (SYS_BASE + 92)
|
|
#define SYS___sysctl (SYS_BASE + 202)
|
|
#define SYS_nanosleep (SYS_BASE + 240)
|
|
+#define SYS_issetugid (SYS_BASE + 253)
|
|
#define SYS_clock_gettime (SYS_BASE + 232)
|
|
#define SYS_sched_yield (SYS_BASE + 331)
|
|
#define SYS_sigprocmask (SYS_BASE + 340)
|
|
@@ -470,3 +471,10 @@ TEXT runtime·getCntxct(SB),NOSPLIT|NOFRAME,$0-8
|
|
|
|
MOVW R0, ret+4(FP)
|
|
RET
|
|
+
|
|
+// func issetugid() int32
|
|
+TEXT runtime·issetugid(SB),NOSPLIT,$0
|
|
+ MOVW $SYS_issetugid, R7
|
|
+ SWI $0
|
|
+ MOVW R0, ret+0(FP)
|
|
+ RET
|
|
diff --git a/src/runtime/sys_freebsd_arm64.s b/src/runtime/sys_freebsd_arm64.s
|
|
index 2330f2f..af87220 100644
|
|
--- a/src/runtime/sys_freebsd_arm64.s
|
|
+++ b/src/runtime/sys_freebsd_arm64.s
|
|
@@ -33,6 +33,7 @@
|
|
#define SYS_fcntl 92
|
|
#define SYS___sysctl 202
|
|
#define SYS_nanosleep 240
|
|
+#define SYS_issetugid 253
|
|
#define SYS_clock_gettime 232
|
|
#define SYS_sched_yield 331
|
|
#define SYS_sigprocmask 340
|
|
@@ -536,3 +537,10 @@ TEXT runtime·getpfr0(SB),NOSPLIT,$0
|
|
MRS ID_AA64PFR0_EL1, R0
|
|
MOVD R0, ret+0(FP)
|
|
RET
|
|
+
|
|
+// func issetugid() int32
|
|
+TEXT runtime·issetugid(SB),NOSPLIT|NOFRAME,$0
|
|
+ MOVD $SYS_issetugid, R8
|
|
+ SVC
|
|
+ MOVW R0, ret+0(FP)
|
|
+ RET
|
|
diff --git a/src/runtime/sys_netbsd_386.s b/src/runtime/sys_netbsd_386.s
|
|
index d0c470c..59f43cf 100644
|
|
--- a/src/runtime/sys_netbsd_386.s
|
|
+++ b/src/runtime/sys_netbsd_386.s
|
|
@@ -29,6 +29,7 @@
|
|
#define SYS___sysctl 202
|
|
#define SYS___sigaltstack14 281
|
|
#define SYS___sigprocmask14 293
|
|
+#define SYS_issetugid 305
|
|
#define SYS_getcontext 307
|
|
#define SYS_setcontext 308
|
|
#define SYS__lwp_create 309
|
|
@@ -497,3 +498,10 @@ TEXT runtime·setNonblock(SB),NOSPLIT,$16-4
|
|
MOVL $92, AX // fcntl
|
|
INT $0x80
|
|
RET
|
|
+
|
|
+// func issetugid() int32
|
|
+TEXT runtime·issetugid(SB),NOSPLIT,$0
|
|
+ MOVL $SYS_issetugid, AX
|
|
+ INT $0x80
|
|
+ MOVL AX, ret+0(FP)
|
|
+ RET
|
|
diff --git a/src/runtime/sys_netbsd_amd64.s b/src/runtime/sys_netbsd_amd64.s
|
|
index dc9bd12..9e78014 100644
|
|
--- a/src/runtime/sys_netbsd_amd64.s
|
|
+++ b/src/runtime/sys_netbsd_amd64.s
|
|
@@ -29,6 +29,7 @@
|
|
#define SYS___sysctl 202
|
|
#define SYS___sigaltstack14 281
|
|
#define SYS___sigprocmask14 293
|
|
+#define SYS_issetugid 305
|
|
#define SYS_getcontext 307
|
|
#define SYS_setcontext 308
|
|
#define SYS__lwp_create 309
|
|
@@ -466,3 +467,13 @@ TEXT runtime·setNonblock(SB),NOSPLIT,$0-4
|
|
MOVL $92, AX // fcntl
|
|
SYSCALL
|
|
RET
|
|
+
|
|
+// func issetugid() int32
|
|
+TEXT runtime·issetugid(SB),NOSPLIT,$0
|
|
+ MOVQ $0, DI
|
|
+ MOVQ $0, SI
|
|
+ MOVQ $0, DX
|
|
+ MOVL $SYS_issetugid, AX
|
|
+ SYSCALL
|
|
+ MOVL AX, ret+0(FP)
|
|
+ RET
|
|
diff --git a/src/runtime/sys_netbsd_arm.s b/src/runtime/sys_netbsd_arm.s
|
|
index 678dea5..91fb22f 100644
|
|
--- a/src/runtime/sys_netbsd_arm.s
|
|
+++ b/src/runtime/sys_netbsd_arm.s
|
|
@@ -30,6 +30,7 @@
|
|
#define SYS___sysctl SWI_OS_NETBSD | 202
|
|
#define SYS___sigaltstack14 SWI_OS_NETBSD | 281
|
|
#define SYS___sigprocmask14 SWI_OS_NETBSD | 293
|
|
+#define SYS_issetugid SWI_OS_NETBSD | 305
|
|
#define SYS_getcontext SWI_OS_NETBSD | 307
|
|
#define SYS_setcontext SWI_OS_NETBSD | 308
|
|
#define SYS__lwp_create SWI_OS_NETBSD | 309
|
|
@@ -439,3 +440,9 @@ TEXT runtime·read_tls_fallback(SB),NOSPLIT|NOFRAME,$0
|
|
SWI $SYS__lwp_getprivate
|
|
MOVM.IAW (R13), [R1, R2, R3, R12]
|
|
RET
|
|
+
|
|
+// func issetugid() int32
|
|
+TEXT runtime·issetugid(SB),NOSPLIT,$0
|
|
+ SWI $SYS_issetugid
|
|
+ MOVW R0, ret+0(FP)
|
|
+ RET
|
|
diff --git a/src/runtime/sys_netbsd_arm64.s b/src/runtime/sys_netbsd_arm64.s
|
|
index e70be0f..b263662 100644
|
|
--- a/src/runtime/sys_netbsd_arm64.s
|
|
+++ b/src/runtime/sys_netbsd_arm64.s
|
|
@@ -32,6 +32,7 @@
|
|
#define SYS___sysctl 202
|
|
#define SYS___sigaltstack14 281
|
|
#define SYS___sigprocmask14 293
|
|
+#define SYS_issetugid 305
|
|
#define SYS_getcontext 307
|
|
#define SYS_setcontext 308
|
|
#define SYS__lwp_create 309
|
|
@@ -474,3 +475,9 @@ TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4
|
|
MOVD $F_SETFL, R1 // arg 2 - cmd
|
|
SVC $SYS_fcntl
|
|
RET
|
|
+
|
|
+// func issetugid() int32
|
|
+TEXT runtime·issetugid(SB),NOSPLIT|NOFRAME,$0
|
|
+ SVC $SYS_issetugid
|
|
+ MOVW R0, ret+0(FP)
|
|
+ RET
|
|
diff --git a/src/runtime/sys_openbsd_386.s b/src/runtime/sys_openbsd_386.s
|
|
index 24fbfd6..72637d1 100644
|
|
--- a/src/runtime/sys_openbsd_386.s
|
|
+++ b/src/runtime/sys_openbsd_386.s
|
|
@@ -459,3 +459,12 @@ TEXT runtime·setNonblock(SB),NOSPLIT,$16-4
|
|
RET
|
|
|
|
GLOBL runtime·tlsoffset(SB),NOPTR,$4
|
|
+
|
|
+TEXT runtime·issetugid_trampoline(SB),NOSPLIT,$0
|
|
+ PUSHL BP
|
|
+ CALL libc_issetugid(SB)
|
|
+ NOP SP // tell vet SP changed - stop checking offsets
|
|
+ MOVL 8(SP), DX // pointer to return value
|
|
+ MOVL AX, 0(DX)
|
|
+ POPL BP
|
|
+ RET
|
|
diff --git a/src/runtime/sys_openbsd_amd64.s b/src/runtime/sys_openbsd_amd64.s
|
|
index 37d70ab..9cb49a6 100644
|
|
--- a/src/runtime/sys_openbsd_amd64.s
|
|
+++ b/src/runtime/sys_openbsd_amd64.s
|
|
@@ -414,3 +414,9 @@ TEXT runtime·setNonblock(SB),NOSPLIT,$0-4
|
|
MOVL $92, AX // fcntl
|
|
SYSCALL
|
|
RET
|
|
+
|
|
+TEXT runtime·issetugid_trampoline(SB),NOSPLIT,$0
|
|
+ MOVQ DI, BX // BX is caller-save
|
|
+ CALL libc_issetugid(SB)
|
|
+ MOVL AX, 0(BX) // return value
|
|
+ RET
|
|
diff --git a/src/runtime/sys_openbsd_arm.s b/src/runtime/sys_openbsd_arm.s
|
|
index 9e18ce0..5467cf8 100644
|
|
--- a/src/runtime/sys_openbsd_arm.s
|
|
+++ b/src/runtime/sys_openbsd_arm.s
|
|
@@ -433,3 +433,12 @@ TEXT runtime·read_tls_fallback(SB),NOSPLIT|NOFRAME,$0
|
|
INVOKE_SYSCALL
|
|
MOVM.IAW (R13), [R1, R2, R3, R12]
|
|
RET
|
|
+
|
|
+TEXT runtime·issetugid_trampoline(SB),NOSPLIT,$0
|
|
+ MOVW R13, R9
|
|
+ MOVW R0, R8
|
|
+ BIC $0x7, R13 // align for ELF ABI
|
|
+ BL libc_issetugid(SB)
|
|
+ MOVW R0, 0(R8)
|
|
+ MOVW R9, R13
|
|
+ RET
|
|
diff --git a/src/runtime/sys_openbsd_arm64.s b/src/runtime/sys_openbsd_arm64.s
|
|
index 621b1b1..00b7864 100644
|
|
--- a/src/runtime/sys_openbsd_arm64.s
|
|
+++ b/src/runtime/sys_openbsd_arm64.s
|
|
@@ -446,3 +446,9 @@ TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4
|
|
MOVD $92, R8 // sys_fcntl
|
|
INVOKE_SYSCALL
|
|
RET
|
|
+
|
|
+TEXT runtime·issetugid_trampoline(SB),NOSPLIT,$0
|
|
+ MOVD R0, R19 // pointer to args
|
|
+ CALL libc_issetugid(SB)
|
|
+ MOVW R0, 0(R19) // return value
|
|
+ RET
|
|
diff --git a/src/runtime/syscall2_solaris.go b/src/runtime/syscall2_solaris.go
|
|
index e098e80..426481b 100644
|
|
--- a/src/runtime/syscall2_solaris.go
|
|
+++ b/src/runtime/syscall2_solaris.go
|
|
@@ -23,6 +23,7 @@ import _ "unsafe" // for go:linkname
|
|
//go:cgo_import_dynamic libc_setpgid setpgid "libc.so"
|
|
//go:cgo_import_dynamic libc_syscall syscall "libc.so"
|
|
//go:cgo_import_dynamic libc_wait4 wait4 "libc.so"
|
|
+//go:cgo_import_dynamic libc_issetugid issetugid "libc.so"
|
|
|
|
//go:linkname libc_chdir libc_chdir
|
|
//go:linkname libc_chroot libc_chroot
|
|
@@ -41,3 +42,4 @@ import _ "unsafe" // for go:linkname
|
|
//go:linkname libc_setpgid libc_setpgid
|
|
//go:linkname libc_syscall libc_syscall
|
|
//go:linkname libc_wait4 libc_wait4
|
|
+//go:linkname libc_issetugid libc_issetugid
|
|
diff --git a/src/runtime/syscall_solaris.go b/src/runtime/syscall_solaris.go
|
|
index 0945169..aff1504 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 0000000..1949d2d
|
|
--- /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
|
|
|