aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2021-12-11 14:56:07 -0800
committerAndrew G. Morgan <morgan@kernel.org>2021-12-11 14:56:07 -0800
commite458889fbda4052919b61fd9f727bb1ac906d436 (patch)
tree40a2c13b15ad211a2108622aa75f85750e7e7e86
parent806b53d13a792d834622b2e546cfdceecc5af699 (diff)
downloadlibcap-e458889fbda4052919b61fd9f727bb1ac906d436.tar.gz
Complete launch thread exit sequence.
This should complete the fix for: https://bugzilla.kernel.org/show_bug.cgi?id=215283 Simplify the code, and add a test that the kernel has confirmed that the thread is no longer running. Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-rw-r--r--cap/launch.go49
1 files changed, 29 insertions, 20 deletions
diff --git a/cap/launch.go b/cap/launch.go
index b67a856..cc3be50 100644
--- a/cap/launch.go
+++ b/cap/launch.go
@@ -181,6 +181,10 @@ func (attr *Launcher) SetChroot(root string) {
// lResult is used to get the result from the doomed launcher thread.
type lResult struct {
+ // tgid holds the thread group id, which is an alias for the
+ // shared process id of the parent program.
+ tgid int
+
// tid holds the tid of the locked launching thread which dies
// as the launch completes.
tid int
@@ -232,13 +236,14 @@ func launch(result chan<- lResult, attr *Launcher, data interface{}, quit chan<-
defer close(quit)
}
- pid := syscall.Getpid()
+ tgid := syscall.Getpid()
+
// This code waits until we are not scheduled on the parent
// thread. We will exit this thread once the child has
// launched.
runtime.LockOSThread()
tid := syscall.Gettid()
- if tid == pid {
+ if tid == tgid {
// Force the go runtime to find a new thread to run
// on. (It is really awkward to have a process'
// PID=TID thread in effectively a zombie state. The
@@ -291,12 +296,12 @@ func launch(result chan<- lResult, attr *Launcher, data interface{}, quit chan<-
}
}
+ var pid int
if attr.callbackFn != nil {
if err = attr.callbackFn(pa, data); err != nil {
goto abort
}
if attr.path == "" {
- pid = 0
goto abort
}
}
@@ -343,12 +348,24 @@ abort:
pid = -1
}
result <- lResult{
- tid: tid,
- pid: pid,
- err: err,
+ tgid: tgid,
+ tid: tid,
+ pid: pid,
+ err: err,
}
}
+// pollForThreadExit waits for a thread to terminate.
+func (v lResult) pollForThreadExit() {
+ if v.tid == -1 {
+ return
+ }
+ for syscall.Tgkill(v.tgid, v.tid, 0) == nil {
+ runtime.Gosched()
+ }
+ scwSetState(launchActive, launchIdle, v.tid)
+}
+
// Launch performs a callback function and/or new program launch with
// a disposable security state. The data object, when not nil, can be
// used to communicate with the callback. It can also be used to
@@ -393,19 +410,11 @@ func (attr *Launcher) Launch(data interface{}) (int, error) {
result := make(chan lResult)
go launch(result, attr, data, nil)
- for {
- select {
- case v, ok := <-result:
- <-result // blocks until the launch() goroutine exits
- if !ok {
- return -1, ErrLaunchFailed
- }
- if v.tid != -1 {
- defer scwSetState(launchActive, launchIdle, v.tid)
- }
- return v.pid, v.err
- default:
- runtime.Gosched()
- }
+ v, ok := <-result
+ if !ok {
+ return -1, ErrLaunchFailed
}
+ <-result // blocks until the launch() goroutine exits
+ v.pollForThreadExit()
+ return v.pid, v.err
}