aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2020-12-11 23:27:50 -0800
committerAndrew G. Morgan <morgan@kernel.org>2020-12-11 23:48:29 -0800
commit90192cd36471c7dfb44b4ecd6a8ccf7595d26e9d (patch)
tree527da7c7d23a0ab06fd9cfeac874351ee18badff
parente7e0e1b9e2cf3378d329174ed5b0c716b0539c72 (diff)
downloadlibcap-90192cd36471c7dfb44b4ecd6a8ccf7595d26e9d.tar.gz
Refactor the "psx" vs "cap" package cgo-or-not complexity to "psx".
I've decided to put the decision to call syscall.AllThreadsSyscall*() into "psx" instead of the "cap" package. This should make client use of "psx" more straightforward and the code will 'just compile' if the user builds with CGO_ENABLED=1 or CGO_ENABLED=0. It also makes the "psx" package useful for other applications besides the "cap" package. Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-rw-r--r--cap/syscalls.go14
-rw-r--r--cap/syscalls_cgo.go29
-rw-r--r--go/Makefile15
-rw-r--r--psx/doc.go60
-rw-r--r--psx/psx.go114
-rw-r--r--psx/psx_cgo.go65
-rw-r--r--psx/psx_cgo_test.go40
-rw-r--r--psx/psx_test.go31
8 files changed, 190 insertions, 178 deletions
diff --git a/cap/syscalls.go b/cap/syscalls.go
index 9c5dd79..ab4bcef 100644
--- a/cap/syscalls.go
+++ b/cap/syscalls.go
@@ -1,14 +1,18 @@
-// +build linux,allthreadssyscall,!cgo
-
package cap
-import "syscall"
+import (
+ "syscall"
+
+ "kernel.org/pub/linux/libs/security/libcap/psx"
+)
// multisc provides syscalls overridable for testing purposes that
// support a single kernel security state for all OS threads.
+// We use this version when we are cgo compiling because
+// we need to manage the native C pthreads too.
var multisc = &syscaller{
- w3: syscall.AllThreadsSyscall,
- w6: syscall.AllThreadsSyscall6,
+ w3: psx.Syscall3,
+ w6: psx.Syscall6,
r3: syscall.RawSyscall,
r6: syscall.RawSyscall6,
}
diff --git a/cap/syscalls_cgo.go b/cap/syscalls_cgo.go
deleted file mode 100644
index 0dc6a0c..0000000
--- a/cap/syscalls_cgo.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// +build linux,cgo
-
-package cap
-
-import (
- "syscall"
-
- "kernel.org/pub/linux/libs/security/libcap/psx"
-)
-
-// multisc provides syscalls overridable for testing purposes that
-// support a single kernel security state for all OS threads.
-// We use this version when we are cgo compiling because
-// we need to manage the native C pthreads too.
-var multisc = &syscaller{
- w3: psx.Syscall3,
- w6: psx.Syscall6,
- r3: syscall.RawSyscall,
- r6: syscall.RawSyscall6,
-}
-
-// singlesc provides a single threaded implementation. Users should
-// take care to ensure the thread is locked and marked nogc.
-var singlesc = &syscaller{
- w3: syscall.RawSyscall,
- w6: syscall.RawSyscall6,
- r3: syscall.RawSyscall,
- r6: syscall.RawSyscall6,
-}
diff --git a/go/Makefile b/go/Makefile
index 3bd79c8..05ec7bf 100644
--- a/go/Makefile
+++ b/go/Makefile
@@ -61,8 +61,8 @@ ifeq ($(RAISE_GO_FILECAP),yes)
@echo "NOTE: RAISED cap_setpcap,cap_net_bind_service ON web binary"
endif
-setid: ../goapps/setid/setid.go $(CAPGOPACKAGE)
- GO111MODULE=off CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" GOPATH=$(GOPATH) $(GO) build -o $@ $(GOBUILDTAG) $<
+setid: ../goapps/setid/setid.go $(CAPGOPACKAGE) $(PSXGOPACKAGE)
+ GO111MODULE=off CGO_ENABLED="$(CGO_REQUIRED)" CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" GOPATH=$(GOPATH) $(GO) build -o $@ $(GOBUILDTAG) $<
gowns: ../goapps/gowns/gowns.go $(CAPGOPACKAGE)
GO111MODULE=off CGO_ENABLED="$(CGO_REQUIRED)" CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" GOPATH=$(GOPATH) $(GO) build -o $@ $(GOBUILDTAG) $<
@@ -77,15 +77,18 @@ ifeq ($(CGO_REQUIRED),0)
endif
# Bug reported issues:
+# https://bugzilla.kernel.org/show_bug.cgi?id=210533 (cgo - fixed)
+# https://github.com/golang/go/issues/43149 (nocgo - not fixed yet)
+# When the latter is fixed we can replace CGO_ENABLED=1 with ="$(CGO_REQUIRED)"
psx-signals: psx-signals.go $(PSXGOPACKAGE)
- GO111MODULE=off CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" GOPATH=$(GOPATH) $(GO) build $<
+ GO111MODULE=off CGO_ENABLED=1 CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" GOPATH=$(GOPATH) $(GO) build $<
b210613: b210613.go $(CAPGOPACKAGE)
- GO111MODULE=off CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" GOPATH=$(GOPATH) $(GO) build $<
+ GO111MODULE=off CGO_ENABLED="$(CGO_REQUIRED)" CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" GOPATH=$(GOPATH) $(GO) build $<
test: all
- GO111MODULE=off CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" GOPATH="$(GOPATH)" $(GO) test $(IMPORTDIR)/psx
- GO111MODULE=off CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" GOPATH="$(GOPATH)" $(GO) test $(IMPORTDIR)/cap
+ GO111MODULE=off CGO_ENABLED="$(CGO_REQUIRED)" CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" GOPATH="$(GOPATH)" $(GO) test $(IMPORTDIR)/psx
+ GO111MODULE=off CGO_ENABLED="$(CGO_REQUIRED)" CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" GOPATH="$(GOPATH)" $(GO) test $(IMPORTDIR)/cap
LD_LIBRARY_PATH=../libcap ./compare-cap
./psx-signals
./setid --caps=false
diff --git a/psx/doc.go b/psx/doc.go
new file mode 100644
index 0000000..e6f9013
--- /dev/null
+++ b/psx/doc.go
@@ -0,0 +1,60 @@
+// Package psx provides support for system calls that are run
+// simultanously on all threads under Linux.
+//
+// This property can be used to work around a historical lack of
+// native Go support for such a feature. Something that is the subject
+// of:
+//
+// https://github.com/golang/go/issues/1435
+//
+// The package works differently depending on whether or not
+// CGO_ENABLED is 0 or 1.
+//
+// In the former case, psx is a low overhead wrapper for the two
+// native go calls: syscall.AllThreadsSyscall() and
+// syscall.AllThreadsSyscall6() [expected to be] introduced in
+// go1.16. We provide this wrapping to minimize client source code
+// changes when compiling with or without CGo enabled.
+//
+// In the latter case, and toolchains prior to go1.16, it works via
+// CGo wrappers for system call functions that call the C [lib]psx
+// functions of these names. This ensures that the system calls
+// execute simultaneously on all the pthreads of the Go (and CGo)
+// combined runtime.
+//
+// With CGo, the psx support works in the following way: the pthread
+// that is first asked to execute the syscall does so, and determines
+// if it succeeds or fails. If it fails, it returns immediately
+// without attempting the syscall on other pthreads. If the initial
+// attempt succeeds, however, then the runtime is stopped in order for
+// the same system call to be performed on all the remaining pthreads
+// of the runtime. Once all pthreads have completed the syscall, the
+// return codes are those obtained by the first pthread's invocation
+// of the syscall.
+//
+// Note, there is no need to use this variant of syscall where the
+// syscalls only read state from the kernel. However, since Go's
+// runtime freely migrates code execution between pthreads, support of
+// this type is required for any successful attempt to fully drop or
+// modify the privilege of a running Go program under Linux.
+//
+// More info on how Linux privilege works and examples of using this
+// package can be found here:
+//
+// https://sites.google.com/site/fullycapable
+//
+// WARNING: For older go toolchains (prior to go1.15), correct
+// compilation of this package may require an extra workaround step:
+//
+// The workaround is to build with the following CGO_LDFLAGS_ALLOW in
+// effect (here the syntax is that of bash for defining an environment
+// variable):
+//
+// export CGO_LDFLAGS_ALLOW="-Wl,-?-wrap[=,][^-.@][^,]*"
+//
+//
+// Copyright (c) 2019,20 Andrew G. Morgan <morgan@kernel.org>
+//
+// The psx package is licensed with a (you choose) BSD 3-clause or
+// GPL2. See LICENSE file for details.
+package psx // import "kernel.org/pub/linux/libs/security/libcap/psx"
diff --git a/psx/psx.go b/psx/psx.go
index 3dfdcb0..77858ad 100644
--- a/psx/psx.go
+++ b/psx/psx.go
@@ -1,113 +1,13 @@
-// Package psx provides support for system calls that are run
-// simultanously on all pthreads.
-//
-// This property can be used to work around a lack of native Go
-// support for such a feature. Something that is the subject of:
-//
-// https://github.com/golang/go/issues/1435
-//
-// The package works via CGo wrappers for system call functions that
-// call the C [lib]psx functions of these names. This ensures that the
-// system calls execute simultaneously on all the pthreads of the Go
-// (and CGo) combined runtime.
-//
-// The psx support works in the following way: the pthread that is
-// first asked to execute the syscall does so, and determines if it
-// succeeds or fails. If it fails, it returns immediately without
-// attempting the syscall on other pthreads. If the initial attempt
-// succeeds, however, then the runtime is stopped in order for the
-// same system call to be performed on all the remaining pthreads of
-// the runtime. Once all pthreads have completed the syscall, the
-// return codes are those obtained by the first pthread's invocation
-// of the syscall.
-//
-// Note, there is no need to use this variant of syscall where the
-// syscalls only read state from the kernel. However, since Go's
-// runtime freely migrates code execution between pthreads, support of
-// this type is required for any successful attempt to fully drop or
-// modify the privilege of a running Go program under Linux.
-//
-// More info on how Linux privilege works can be found here:
-//
-// https://sites.google.com/site/fullycapable
-//
-// WARNING: Correct compilation of this package may require an extra
-// step:
-//
-// If your Go compiler is older than go1.15, a workaround may be
-// required to be able to link this package. In order to do what it
-// needs to this package employs some unusual linking flags.
-//
-// The workaround is to build with the following CGO_LDFLAGS_ALLOW
-// in effect:
-//
-// export CGO_LDFLAGS_ALLOW="-Wl,-?-wrap[=,][^-.@][^,]*"
-//
-//
-// Copyright (c) 2019,20 Andrew G. Morgan <morgan@kernel.org>
-//
-// The psx package is licensed with a (you choose) BSD 3-clause or
-// GPL2. See LICENSE file for details.
+// +build linux,!cgo
+// +build go1.16 allthreadssyscall
+
package psx // import "kernel.org/pub/linux/libs/security/libcap/psx"
import (
- "runtime"
"syscall"
)
-// #cgo LDFLAGS: -lpthread -Wl,-wrap,pthread_create
-//
-// #include <errno.h>
-// #include "psx_syscall.h"
-//
-// long __errno_too(long set_errno) {
-// long v = errno;
-// if (set_errno >= 0) {
-// errno = set_errno;
-// }
-// return v;
-// }
-import "C"
-
-// setErrno returns the current C.errno value and, if v >= 0, sets the
-// CGo errno for a random pthread to value v. If you want some
-// consistency, this needs to be called from runtime.LockOSThread()
-// code. This function is only defined for testing purposes. The psx.c
-// code should properly handle the case that a non-zero errno is saved
-// and restored independently of what these Syscall[36]() functions
-// observe.
-func setErrno(v int) int {
- return int(C.__errno_too(C.long(v)))
-}
-
-// Syscall3 performs a 3 argument syscall using the libpsx C function
-// psx_syscall3(). Syscall3 differs from syscall.[Raw]Syscall()
-// insofar as it is simultaneously executed on every pthread of the
-// combined Go and CGo runtimes.
-func Syscall3(syscallnr, arg1, arg2, arg3 uintptr) (uintptr, uintptr, syscall.Errno) {
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
-
- v := C.psx_syscall3(C.long(syscallnr), C.long(arg1), C.long(arg2), C.long(arg3))
- var errno syscall.Errno
- if v < 0 {
- errno = syscall.Errno(C.__errno_too(-1))
- }
- return uintptr(v), uintptr(v), errno
-}
-
-// Syscall6 performs a 6 argument syscall using the libpsx C function
-// psx_syscall6(). Syscall6 differs from syscall.[Raw]Syscall6() insofar as
-// it is simultaneously executed on every pthread of the combined Go
-// and CGo runtimes.
-func Syscall6(syscallnr, arg1, arg2, arg3, arg4, arg5, arg6 uintptr) (uintptr, uintptr, syscall.Errno) {
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
-
- v := C.psx_syscall6(C.long(syscallnr), C.long(arg1), C.long(arg2), C.long(arg3), C.long(arg4), C.long(arg5), C.long(arg6))
- var errno syscall.Errno
- if v < 0 {
- errno = syscall.Errno(C.__errno_too(-1))
- }
- return uintptr(v), uintptr(v), errno
-}
+var (
+ Syscall3 = syscall.AllThreadsSyscall
+ Syscall6 = syscall.AllThreadsSyscall6
+)
diff --git a/psx/psx_cgo.go b/psx/psx_cgo.go
new file mode 100644
index 0000000..c17b4f3
--- /dev/null
+++ b/psx/psx_cgo.go
@@ -0,0 +1,65 @@
+// +build linux,cgo
+
+package psx // import "kernel.org/pub/linux/libs/security/libcap/psx"
+
+import (
+ "runtime"
+ "syscall"
+)
+
+// #cgo LDFLAGS: -lpthread -Wl,-wrap,pthread_create
+//
+// #include <errno.h>
+// #include "psx_syscall.h"
+//
+// long __errno_too(long set_errno) {
+// long v = errno;
+// if (set_errno >= 0) {
+// errno = set_errno;
+// }
+// return v;
+// }
+import "C"
+
+// setErrno returns the current C.errno value and, if v >= 0, sets the
+// CGo errno for a random pthread to value v. If you want some
+// consistency, this needs to be called from runtime.LockOSThread()
+// code. This function is only defined for testing purposes. The psx.c
+// code should properly handle the case that a non-zero errno is saved
+// and restored independently of what these Syscall[36]() functions
+// observe.
+func setErrno(v int) int {
+ return int(C.__errno_too(C.long(v)))
+}
+
+// Syscall3 performs a 3 argument syscall using the libpsx C function
+// psx_syscall3(). Syscall3 differs from syscall.[Raw]Syscall()
+// insofar as it is simultaneously executed on every pthread of the
+// combined Go and CGo runtimes.
+func Syscall3(syscallnr, arg1, arg2, arg3 uintptr) (uintptr, uintptr, syscall.Errno) {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ v := C.psx_syscall3(C.long(syscallnr), C.long(arg1), C.long(arg2), C.long(arg3))
+ var errno syscall.Errno
+ if v < 0 {
+ errno = syscall.Errno(C.__errno_too(-1))
+ }
+ return uintptr(v), uintptr(v), errno
+}
+
+// Syscall6 performs a 6 argument syscall using the libpsx C function
+// psx_syscall6(). Syscall6 differs from syscall.[Raw]Syscall6() insofar as
+// it is simultaneously executed on every pthread of the combined Go
+// and CGo runtimes.
+func Syscall6(syscallnr, arg1, arg2, arg3, arg4, arg5, arg6 uintptr) (uintptr, uintptr, syscall.Errno) {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ v := C.psx_syscall6(C.long(syscallnr), C.long(arg1), C.long(arg2), C.long(arg3), C.long(arg4), C.long(arg5), C.long(arg6))
+ var errno syscall.Errno
+ if v < 0 {
+ errno = syscall.Errno(C.__errno_too(-1))
+ }
+ return uintptr(v), uintptr(v), errno
+}
diff --git a/psx/psx_cgo_test.go b/psx/psx_cgo_test.go
new file mode 100644
index 0000000..090a96a
--- /dev/null
+++ b/psx/psx_cgo_test.go
@@ -0,0 +1,40 @@
+// +build cgo
+
+package psx
+
+import (
+ "runtime"
+ "syscall"
+ "testing"
+)
+
+// The man page for errno indicates that it is never set to zero, so
+// validate that it retains its value over a successful Syscall[36]()
+// and is overwritten on a failing syscall.
+func TestErrno(t *testing.T) {
+ // This testing is much easier if we don't have to guess which
+ // thread is running this Go code.
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ // Start from a known bad state and clean up afterwards.
+ setErrno(int(syscall.EPERM))
+ defer setErrno(0)
+
+ v3, _, errno := Syscall3(syscall.SYS_GETUID, 0, 0, 0)
+ if errno != 0 {
+ t.Fatalf("psx getuid failed: %v", errno)
+ }
+ v6, _, errno := Syscall6(syscall.SYS_GETUID, 0, 0, 0, 0, 0, 0)
+ if errno != 0 {
+ t.Fatalf("psx getuid failed: %v", errno)
+ }
+
+ if v3 != v6 {
+ t.Errorf("psx getuid failed to match v3=%d, v6=%d", v3, v6)
+ }
+
+ if v := setErrno(-1); v != int(syscall.EPERM) {
+ t.Errorf("psx changes prevailing errno got=%v(%d) want=%v", syscall.Errno(v), v, syscall.EPERM)
+ }
+}
diff --git a/psx/psx_test.go b/psx/psx_test.go
index 3f0445c..4b90f63 100644
--- a/psx/psx_test.go
+++ b/psx/psx_test.go
@@ -34,37 +34,6 @@ func TestSyscall6(t *testing.T) {
}
}
-// The man page for errno indicates that it is never set to zero, so
-// validate that it retains its value over a successful Syscall[36]()
-// and is overwritten on a failing syscall.
-func TestErrno(t *testing.T) {
- // This testing is much easier if we don't have to guess which
- // thread is running this Go code.
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
-
- // Start from a known bad state and clean up afterwards.
- setErrno(int(syscall.EPERM))
- defer setErrno(0)
-
- v3, _, errno := Syscall3(syscall.SYS_GETUID, 0, 0, 0)
- if errno != 0 {
- t.Fatalf("psx getuid failed: %v", errno)
- }
- v6, _, errno := Syscall6(syscall.SYS_GETUID, 0, 0, 0, 0, 0, 0)
- if errno != 0 {
- t.Fatalf("psx getuid failed: %v", errno)
- }
-
- if v3 != v6 {
- t.Errorf("psx getuid failed to match v3=%d, v6=%d", v3, v6)
- }
-
- if v := setErrno(-1); v != int(syscall.EPERM) {
- t.Errorf("psx changes prevailing errno got=%v(%d) want=%v", syscall.Errno(v), v, syscall.EPERM)
- }
-}
-
// killAThread locks the goroutine to a thread and exits. This has the
// effect of making the go runtime terminate the thread.
func killAThread(c <-chan struct{}) {