diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2020-07-05 12:50:08 -0700 |
---|---|---|
committer | Andrew G. Morgan <morgan@kernel.org> | 2020-07-05 12:50:08 -0700 |
commit | 42afb6ac6b92e4694b97fef5ab1d1fc87d4be0e9 (patch) | |
tree | ffe4b8b8ada64856c85738357db869dc6d235f68 | |
parent | 5457efd92c533259eecde426d9e6bb6abb270d89 (diff) | |
download | platform_external_libcap-42afb6ac6b92e4694b97fef5ab1d1fc87d4be0e9.tar.gz platform_external_libcap-42afb6ac6b92e4694b97fef5ab1d1fc87d4be0e9.tar.bz2 platform_external_libcap-42afb6ac6b92e4694b97fef5ab1d1fc87d4be0e9.zip |
Fix a rare deadlock in cap.Launch().
The main functional change with this commit is to fix this bug:
https://bugzilla.kernel.org/show_bug.cgi?id=208445
Also, include better documentation for the "cap" module. Now that it
is a proper Go module, it is starting to show up on the automated
golang module sites (such as pkg.go.dev) and I thought it deserved
more of an intro comment.
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-rw-r--r-- | cap/cap.go | 41 | ||||
-rw-r--r-- | cap/launch.go | 35 |
2 files changed, 49 insertions, 27 deletions
@@ -1,22 +1,35 @@ -// Package cap is the Linux capabilities user space API (libcap) +// Package cap provides the Linux Capabilities userspace library API // bindings in native Go. // -// For cgo linked binaries, behind the scenes, the package -// "kernel.org/pub/linux/libs/security/libcap/psx" is used to broker -// the POSIX semantics system calls that manipulate thread state -// uniformly over the whole process runtime. +// Capabilities are a feature of the Linux kernel that allow fine +// grain permissions to perform privileged operations. Privileged +// operations are required to do irregular system level operations +// from code. You can read more about how Capabilities are intended to +// work here: // -// If the Go runtime syscall interface contains the linux variant -// syscall.AllThreadsSyscall() API (it is not in go1.15beta1 for -// example) then this package can use that to invoke capability -// setting system calls for pure Go binaries. To force this behavior -// use the CGO_ENABLED=0 environment variable and, for now, a build -// tag: +// https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/33528.pdf // -// CGO_ENABLED=0 go build -tags allthreadssyscall ... +// This package supports native Go bindings for all the features +// described in that paper as well as supporting subsequent changes to +// the kernel for other styles of inheritable Capability. // -// If syscall.AllThreadsSyscall() is not present, the ".../libcap/cap" -// package will failover to using ".../libcap/psx". +// See https://sites.google.com/site/fullycapable/ for recent updates +// and information on how to file bugs. +// +// For CGo linked binaries, behind the scenes, the package +// "kernel.org/pub/linux/libs/security/libcap/psx" is used to perform +// POSIX semantics system calls that manipulate thread state +// uniformly over the whole Go (and CGo linked) process runtime. +// +// Note, if the Go runtime syscall interface contains the linux +// variant syscall.AllThreadsSyscall() API (it is not in go1.15beta1 +// for example, but see https://github.com/golang/go/issues/1435 for +// current status) then this present package can use that to invoke +// Capability setting system calls for pure Go binaries. In such an +// enhanced Go runtime, to force this behavior, use the CGO_ENABLED=0 +// environment variable and, for now, a build tag: +// +// CGO_ENABLED=0 go build -tags allthreadssyscall ... // // Copyright (c) 2019,20 Andrew G. Morgan <morgan@kernel.org> package cap // import "kernel.org/pub/linux/libs/security/libcap/cap" diff --git a/cap/launch.go b/cap/launch.go index f4327c2..3d9c81b 100644 --- a/cap/launch.go +++ b/cap/launch.go @@ -126,31 +126,40 @@ var lName = []byte("cap-launcher\000") const prSetName = 15 //go:uintptrescapes -func launch(result chan<- lResult, attr *Launcher, data interface{}) { - defer close(result) +func launch(result chan<- lResult, attr *Launcher, data interface{}, quit chan<- struct{}) { + if quit != nil { + defer close(quit) + } pid := syscall.Getpid() // Wait until we are not scheduled on the parent thread. We // will exit this thread once the child has launched, and // don't want other goroutines to use this thread afterwards. - for { - runtime.LockOSThread() - tid := syscall.Gettid() - if tid != pid { - break - } + runtime.LockOSThread() + tid := syscall.Gettid() + if tid == pid { + // Force the go runtime to find a new thread to run on. + quit := make(chan struct{}) + go launch(result, attr, data, quit) + + // Wait for that go routine to complete. + <-quit runtime.UnlockOSThread() - runtime.Gosched() + return } // By never releasing the LockOSThread here, we guarantee that - // the runtime will terminate this OS thread once this + // the runtime will terminate the current OS thread once this // function returns. - // Name the launcher thread - transient, but helps if the - // callbackFn or something else hangs up. + // Name the launcher thread - transient, but helps to debug if + // the callbackFn or something else hangs up. singlesc.prctlrcall(prSetName, uintptr(unsafe.Pointer(&lName[0])), 0) + // Provide a way to serialize the caller on the thread + // completing. + defer close(result) + pa := &syscall.ProcAttr{ Files: []uintptr{0, 1, 2}, } @@ -225,7 +234,7 @@ func (attr *Launcher) Launch(data interface{}) (int, error) { defer scwMu.Unlock() result := make(chan lResult) - go launch(result, attr, data) + go launch(result, attr, data, nil) for { select { case v, ok := <-result: |