mirror of
https://git.kernel.org/pub/scm/libs/libcap/libcap.git
synced 2026-01-26 15:39:08 +00:00
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>
This commit is contained in:
parent
e7e0e1b9e2
commit
90192cd364
@ -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,
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
15
go/Makefile
15
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
|
||||
|
||||
60
psx/doc.go
Normal file
60
psx/doc.go
Normal file
@ -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"
|
||||
114
psx/psx.go
114
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
|
||||
)
|
||||
|
||||
65
psx/psx_cgo.go
Normal file
65
psx/psx_cgo.go
Normal file
@ -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
|
||||
}
|
||||
40
psx/psx_cgo_test.go
Normal file
40
psx/psx_cgo_test.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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{}) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user