forked from pool/libcap
115 lines
3.6 KiB
Diff
115 lines
3.6 KiB
Diff
From 7cfe15ee579ea83a7780c6190576fdcab3e2faac Mon Sep 17 00:00:00 2001
|
|
From: "Andrew G. Morgan" <morgan@kernel.org>
|
|
Date: Wed, 11 Nov 2020 19:53:06 -0800
|
|
Subject: [PATCH] Allow a dying thread to participate in the psx mechanism.
|
|
|
|
If a dying thread blocks all interrupts in order to die in peace,
|
|
it can deadlock the psx mechanism for all the surviving threads.
|
|
To account for this corner case, while waiting to enter the
|
|
_PSX_EXITING state, temporarily unblock the psx_tracker.psx_sig.
|
|
|
|
https://github.com/golang/go/issues/42494
|
|
|
|
Add a psx_test.go:TestThreadChurn() test case for this issue.
|
|
|
|
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
|
|
---
|
|
psx/psx.c | 34 ++++++++++++++++++++++++++++++++++
|
|
psx/psx_test.go | 33 +++++++++++++++++++++++++++++++++
|
|
2 files changed, 67 insertions(+)
|
|
|
|
diff --git a/psx/psx.c b/psx/psx.c
|
|
index 72c2098..233267d 100644
|
|
--- a/psx/psx.c
|
|
+++ b/psx/psx.c
|
|
@@ -336,11 +336,45 @@ typedef struct {
|
|
* https://sourceware.org/bugzilla/show_bug.cgi?id=12889
|
|
*/
|
|
static void _psx_exiting(void *node) {
|
|
+ /*
|
|
+ * Until we are in the _PSX_EXITING state, we must not block the
|
|
+ * psx_sig interrupt for this dying thread. That is, until this
|
|
+ * exiting thread can set ref->gone to 1, this dying thread is
|
|
+ * still participating in the psx syscall distribution.
|
|
+ *
|
|
+ * See https://github.com/golang/go/issues/42494 for a situation
|
|
+ * where this code is called with psx_tracker.psx_sig blocked.
|
|
+ */
|
|
+ sigset_t sigbit, orig_sigbits;
|
|
+ sigemptyset(&sigbit);
|
|
+ pthread_sigmask(SIG_UNBLOCK, &sigbit, &orig_sigbits);
|
|
+ sigaddset(&sigbit, psx_tracker.psx_sig);
|
|
+ pthread_sigmask(SIG_UNBLOCK, &sigbit, NULL);
|
|
+
|
|
+ /*
|
|
+ * With psx_tracker.psx_sig unblocked we can wait until this
|
|
+ * thread can enter the _PSX_EXITING state.
|
|
+ */
|
|
psx_new_state(_PSX_IDLE, _PSX_EXITING);
|
|
+
|
|
+ /*
|
|
+ * We now indicate that this thread is no longer participating in
|
|
+ * the psx mechanism.
|
|
+ */
|
|
registered_thread_t *ref = node;
|
|
pthread_mutex_lock(&ref->mu);
|
|
ref->gone = 1;
|
|
pthread_mutex_unlock(&ref->mu);
|
|
+
|
|
+ /*
|
|
+ * At this point, we can restore the calling sigmask to whatever
|
|
+ * the caller thought was appropriate for a dying thread to have.
|
|
+ */
|
|
+ pthread_sigmask(SIG_SETMASK, &orig_sigbits, NULL);
|
|
+
|
|
+ /*
|
|
+ * Allow the rest of the psx system carry on as per normal.
|
|
+ */
|
|
psx_new_state(_PSX_EXITING, _PSX_IDLE);
|
|
}
|
|
|
|
diff --git a/psx/psx_test.go b/psx/psx_test.go
|
|
index ae6ccd2..3f0445c 100644
|
|
--- a/psx/psx_test.go
|
|
+++ b/psx/psx_test.go
|
|
@@ -64,3 +64,36 @@ func TestErrno(t *testing.T) {
|
|
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{}) {
|
|
+ runtime.LockOSThread()
|
|
+ <-c
|
|
+}
|
|
+
|
|
+// Test to confirm no regression against:
|
|
+//
|
|
+// https://github.com/golang/go/issues/42494
|
|
+func TestThreadChurn(t *testing.T) {
|
|
+ const prSetKeepCaps = 8
|
|
+
|
|
+ for j := 0; j < 4; j++ {
|
|
+ kill := (j & 1) != 0
|
|
+ sysc := (j & 2) != 0
|
|
+ t.Logf("[%d] testing kill=%v, sysc=%v", j, kill, sysc)
|
|
+ for i := 50; i > 0; i-- {
|
|
+ if kill {
|
|
+ c := make(chan struct{})
|
|
+ go killAThread(c)
|
|
+ close(c)
|
|
+ }
|
|
+ if sysc {
|
|
+ if _, _, e := Syscall3(syscall.SYS_PRCTL, prSetKeepCaps, uintptr(i&1), 0); e != 0 {
|
|
+ t.Fatalf("[%d] psx:prctl(SET_KEEPCAPS, %d) failed: %v", i, i&1, syscall.Errno(e))
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ t.Logf("[%d] PASSED kill=%v, sysc=%v", j, kill, sysc)
|
|
+ }
|
|
+}
|
|
--
|
|
2.29.2
|
|
|