diff --git a/0001-Allow-a-dying-thread-to-participate-in-the-psx-mecha.patch b/0001-Allow-a-dying-thread-to-participate-in-the-psx-mecha.patch new file mode 100644 index 0000000..c5d4875 --- /dev/null +++ b/0001-Allow-a-dying-thread-to-participate-in-the-psx-mecha.patch @@ -0,0 +1,114 @@ +From 7cfe15ee579ea83a7780c6190576fdcab3e2faac Mon Sep 17 00:00:00 2001 +From: "Andrew G. Morgan" +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 +--- + 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 + diff --git a/libcap-2.44.tar.xz b/libcap-2.44.tar.xz new file mode 100644 index 0000000..cbd6eda --- /dev/null +++ b/libcap-2.44.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92188359cd5be86e8e5bd3f6483ac6ce582264f912398937ef763def2205c8e1 +size 125568 diff --git a/libcap-2.45.tar.xz b/libcap-2.45.tar.xz deleted file mode 100644 index 5c02c48..0000000 --- a/libcap-2.45.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d66639f765c0e10557666b00f519caf0bd07a95f867dddaee131cd284fac3286 -size 127608 diff --git a/libcap.spec b/libcap.spec index 680c874..d6d5862 100644 --- a/libcap.spec +++ b/libcap.spec @@ -17,7 +17,7 @@ Name: libcap -Version: 2.45 +Version: 2.44 Release: 0 Summary: Library for Capabilities (linux-privs) Support License: BSD-3-Clause AND GPL-2.0-only @@ -25,6 +25,7 @@ Group: Development/Libraries/C and C++ URL: https://sites.google.com/site/fullycapable/ Source: https://www.kernel.org/pub/linux/libs/security/linux-privs/libcap2/libcap-%{version}.tar.xz Source2: baselibs.conf +Patch: 0001-Allow-a-dying-thread-to-participate-in-the-psx-mecha.patch BuildRequires: fdupes BuildRequires: glibc-devel-static BuildRequires: pkgconfig @@ -89,11 +90,12 @@ libcap. %prep %setup -q +%patch -p1 %build %global _lto_cflags %{_lto_cflags} -ffat-lto-objects make prefix=%{_prefix} lib=%{_lib} LIBDIR=%{_libdir} SBINDIR=%{_sbindir} \ - INCDIR=%{_includedir} MANDIR=%{_mandir} DYNAMIC=yes DEBUG="-g %{optflags}" + INCDIR=%{_includedir} MANDIR=%{_mandir} SHARED=yes DEBUG="-g %{optflags}" %install make install RAISE_SETFCAP=no \ @@ -115,12 +117,14 @@ make test %post -n libcap2 -p /sbin/ldconfig %postun -n libcap2 -p /sbin/ldconfig +%ifarch aarch64 %post -n libpsx2 -p /sbin/ldconfig %postun -n libpsx2 -p /sbin/ldconfig %files -n libpsx2 %license License %{_libdir}/libpsx.so.2* +%endif %files -n libcap2 %license License