summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go/.gitignore1
-rw-r--r--go/Makefile8
-rw-r--r--go/psx-signals.go46
-rw-r--r--psx/psx.c50
4 files changed, 96 insertions, 9 deletions
diff --git a/go/.gitignore b/go/.gitignore
index 322297c..3a3b13f 100644
--- a/go/.gitignore
+++ b/go/.gitignore
@@ -2,6 +2,7 @@ good-names.go
compare-cap
try-launching
try-launching-cgo
+psx-signals
mknames
web
ok
diff --git a/go/Makefile b/go/Makefile
index eee379e..70a8abf 100644
--- a/go/Makefile
+++ b/go/Makefile
@@ -18,7 +18,7 @@ CAPGOPACKAGE=$(PKGDIR)/cap.a
DEPS=../libcap/libcap.a ../libcap/libpsx.a
-all: $(PSXGOPACKAGE) $(CAPGOPACKAGE) web compare-cap try-launching
+all: $(PSXGOPACKAGE) $(CAPGOPACKAGE) web compare-cap try-launching psx-signals
$(DEPS):
make -C ../libcap all
@@ -70,10 +70,15 @@ ifeq ($(CGO_REQUIRED),0)
GO111MODULE=off CGO_ENABLED="1" CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" GOPATH=$(GOPATH) $(GO) build -o $@-cgo $<
endif
+# Bug reported issues:
+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 $<
+
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
LD_LIBRARY_PATH=../libcap ./compare-cap
+ ./psx-signals
sudotest: test ../progs/tcapsh-static
./try-launching
@@ -98,4 +103,5 @@ clean:
rm -f *.o *.so *~ mknames web ok good-names.go
rm -f compare-cap try-launching try-launching-cgo
rm -f $(topdir)/cap/*~ $(topdir)/psx/*~
+ rm -f psx-signals
rm -fr pkg src
diff --git a/go/psx-signals.go b/go/psx-signals.go
new file mode 100644
index 0000000..486f284
--- /dev/null
+++ b/go/psx-signals.go
@@ -0,0 +1,46 @@
+// Program psx-signals validates that the psx mechanism can coexist
+// with Go use of signals. This is an unprivilaged program derived
+// from the sample code provided in this bug report:
+//
+// https://bugzilla.kernel.org/show_bug.cgi?id=210533
+package main
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+
+ "kernel.org/pub/linux/libs/security/libcap/psx"
+)
+
+const maxSig = 10
+const prSetKeepCaps = 8
+
+func main() {
+ sig := make(chan os.Signal, maxSig)
+ signal.Notify(sig, os.Interrupt)
+
+ fmt.Print("Toggling KEEP_CAPS ")
+ for i := 0; i < maxSig; i++ {
+ fmt.Print(".")
+ _, _, err := psx.Syscall3(syscall.SYS_PRCTL, prSetKeepCaps, uintptr(i&1), 0)
+ if err != 0 {
+ log.Fatalf("[%d] attempt to set KEEPCAPS (to %d) failed: %v", i, i%2, err)
+ }
+ }
+
+ fmt.Println(" done")
+ fmt.Print("Wait 1 second to see if unwanted signals arrive...")
+ // Confirm no signals are delivered.
+ select {
+ case <-time.After(1 * time.Second):
+ break
+ case info := <-sig:
+ log.Fatalf("signal received: %v", info)
+ }
+ fmt.Println(" none arrived")
+ fmt.Println("PASSED")
+}
diff --git a/psx/psx.c b/psx/psx.c
index 233267d..046620f 100644
--- a/psx/psx.c
+++ b/psx/psx.c
@@ -89,6 +89,7 @@ static struct psx_tracker_s {
} cmd;
struct sigaction sig_action;
+ struct sigaction chained_action;
registered_thread_t *root;
} psx_tracker;
@@ -123,6 +124,9 @@ static void psx_posix_syscall_actor(int signum, siginfo_t *info, void *ignore) {
/* bail early if this isn't something we recognize */
if (signum != psx_tracker.psx_sig || !psx_tracker.cmd.active ||
info == NULL || info->si_code != SI_TKILL || info->si_pid != getpid()) {
+ if (psx_tracker.chained_action.sa_sigaction != 0) {
+ psx_tracker.chained_action.sa_sigaction(signum, info, ignore);
+ }
return;
}
@@ -174,6 +178,34 @@ extern int __real_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
/*
+ * psx_confirm_sigaction reconfirms that the psx handler is the first
+ * handler to respond to the psx signal. It assumes that
+ * psx_tracker.psx_sig has been set.
+ */
+static void psx_confirm_sigaction(void) {
+ sigset_t mask, orig;
+ struct sigaction existing_sa;
+
+ /*
+ * Block interrupts while potentially rewriting the handler.
+ */
+ sigemptyset(&mask);
+ sigaddset(&mask, psx_tracker.psx_sig);
+ sigprocmask(SIG_BLOCK, &mask, &orig);
+
+ sigaction(psx_tracker.psx_sig, NULL, &existing_sa);
+ if (existing_sa.sa_sigaction != psx_posix_syscall_actor) {
+ memcpy(&psx_tracker.chained_action, &existing_sa, sizeof(struct sigaction));
+ psx_tracker.sig_action.sa_sigaction = psx_posix_syscall_actor;
+ sigemptyset(&psx_tracker.sig_action.sa_mask);
+ psx_tracker.sig_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
+ sigaction(psx_tracker.psx_sig, &psx_tracker.sig_action, NULL);
+ }
+
+ sigprocmask(SIG_SETMASK, &orig, NULL);
+}
+
+/*
* psx_syscall_start initializes the subsystem including initializing
* the mutex.
*/
@@ -184,15 +216,15 @@ static void psx_syscall_start(void) {
pthread_atfork(_psx_prepare_fork, _psx_fork_completed, _psx_forked_child);
/*
- * glibc nptl picks from the SIGRTMIN end, so we pick from the
- * SIGRTMAX end
+ * All sorts of things are assumed by Linux and glibc and/or musl
+ * about signal handlers and which can be blocked. Go has its own
+ * idiosyncrasies too. We tried SIGRTMAX until
+ * https://bugzilla.kernel.org/show_bug.cgi?id=210533, so this is
+ * our current strategy: to intercept SIGSYS.
*/
- psx_tracker.psx_sig = SIGRTMAX;
- psx_tracker.sig_action.sa_sigaction = psx_posix_syscall_actor;
- sigemptyset(&psx_tracker.sig_action.sa_mask);
- psx_tracker.sig_action.sa_flags = SA_SIGINFO | SA_RESTART;;
- sigaction(psx_tracker.psx_sig, &psx_tracker.sig_action, NULL);
+ psx_tracker.psx_sig = SIGSYS;
+ psx_confirm_sigaction();
psx_do_registration(); // register the main thread.
psx_tracker.initialized = 1;
@@ -201,7 +233,8 @@ static void psx_syscall_start(void) {
/*
* This is the only way this library globally locks. Note, this is not
* to be confused with psx_sig (interrupt) blocking - which is
- * performed around thread creation.
+ * performed around thread creation and when the signal handler is
+ * being confirmed.
*/
static void psx_lock(void)
{
@@ -531,6 +564,7 @@ long int __psx_syscall(long int syscall_nr, ...) {
}
psx_new_state(_PSX_IDLE, _PSX_SETUP);
+ psx_confirm_sigaction();
long int ret;