mirror of
https://git.kernel.org/pub/scm/libs/libcap/libcap.git
synced 2026-01-26 15:39:08 +00:00
Avoid thread churn deadlock with PSX rewrite.
This bug, reported by David Runge, identifies another regression vs. libpsx-2.71: https://bugzilla.kernel.org/show_bug.cgi?id=219687 A regression of the LWP threading support reintroduced an old bug in the libpsx-2.72 rewrite, specifically this bug: https://github.com/golang/go/issues/42494 This present change includes x86_64, i386 and arm* (32-bit and 64-bit) support. I've tested each of these with debian OSes, some running via QEMU. Before releasing 2.74, I plan to extend the testing to all of the targets supported by Debian (see https://wiki.debian.org/DebianInstaller/Qemu). Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
This commit is contained in:
parent
2dda39e0e3
commit
025f28ca4f
@ -139,6 +139,10 @@ endif
|
||||
|
||||
test: vet setid gowns captree psx-fd $(TESTS)
|
||||
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(GO) test -v -mod=vendor $(IMPORTDIR)/psx
|
||||
ifeq ($(CGO_REQUIRED),0)
|
||||
CC="$(CC)" CGO_ENABLED="1" $(GO) test -v -mod=vendor $(IMPORTDIR)/psx
|
||||
CC="$(CC)" CGO_ENABLED="1" $(GO) test -v -mod=vendor $(IMPORTDIR)/cap
|
||||
endif
|
||||
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(GO) test -v -mod=vendor $(IMPORTDIR)/cap
|
||||
LD_LIBRARY_PATH=../libcap ./compare-cap
|
||||
./psx-signals
|
||||
|
||||
@ -48,7 +48,7 @@ func tryFileCaps() {
|
||||
// Failing attempt to remove capabilities.
|
||||
var empty *cap.Set
|
||||
if err := empty.SetFile(os.Args[0]); err != syscall.EPERM {
|
||||
log.Fatalf("failed to be blocked from removing filecaps: %v", err)
|
||||
log.Fatalf("failed to be blocked from removing filecaps (with=%q): %v", cap.GetProc(), err)
|
||||
}
|
||||
|
||||
// The privilege we want (in the case we are root, we need the
|
||||
@ -96,7 +96,7 @@ func tryFileCaps() {
|
||||
|
||||
// Failing attempt to remove capabilities.
|
||||
if err := empty.SetFd(f); err != syscall.EPERM {
|
||||
log.Fatalf("failed to be blocked from fremoving filecaps: %v", err)
|
||||
log.Fatalf("failed to be blocked from removing filecaps (w/ %q): %v", cap.GetProc(), err)
|
||||
}
|
||||
|
||||
// For the next section, we won't set the effective bit on the file.
|
||||
|
||||
@ -15,7 +15,7 @@ STAPSXLIBNAME=$(PSXTITLE).a
|
||||
|
||||
CAPFILES=cap_alloc cap_proc cap_extint cap_flag cap_text cap_file cap_syscalls
|
||||
CAPMAGICOBJ=cap_magic.o
|
||||
PSXFILES=../psx/psx ../psx/wrap/psx_wrap
|
||||
PSXFILES=../psx/psx ../psx/psx_calls ../psx/wrap/psx_wrap
|
||||
PSXMAGICOBJ=psx_magic.o
|
||||
|
||||
# Always build libcap sources this way:
|
||||
|
||||
91
psx/libpsx.h
Normal file
91
psx/libpsx.h
Normal file
@ -0,0 +1,91 @@
|
||||
#ifndef LIBPSX_H
|
||||
#define LIBPSX_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025 Andrew G. Morgan <morgan@kernel.org>
|
||||
*
|
||||
* These header definitions are private to the PSX mechanism. External
|
||||
* API references are to be found in <sys/psx_syscall.h>
|
||||
*
|
||||
* Since we no longer (libcap-2.72) operate at the pthreads
|
||||
* abstraction, we need our own mutex etc implementation.
|
||||
*/
|
||||
|
||||
typedef unsigned char psx_mutex_t;
|
||||
#define _psx_mu_blocked(x) \
|
||||
__atomic_test_and_set((void *)(x), __ATOMIC_SEQ_CST)
|
||||
#define _psx_mu_lock(x) \
|
||||
while (_psx_mu_blocked(x)) sched_yield()
|
||||
#define _psx_mu_unlock(x) \
|
||||
__atomic_clear((void *)(x), __ATOMIC_SEQ_CST)
|
||||
#define _psx_mu_unlock_return(x, y) \
|
||||
do { _psx_mu_unlock(x); return (y); } while (0)
|
||||
#define _psx_mu_cond_wait(x) \
|
||||
do { \
|
||||
_psx_mu_unlock(x); \
|
||||
sched_yield(); \
|
||||
_psx_mu_lock(x); \
|
||||
} while (0)
|
||||
|
||||
/* Not reliably defined by *libc so, alias the direct syscall. */
|
||||
#define _psx_gettid() syscall(SYS_gettid)
|
||||
|
||||
extern void psx_lock(void);
|
||||
extern void psx_unlock(void);
|
||||
extern void psx_cond_wait(void);
|
||||
extern long psx_mix(long value);
|
||||
|
||||
typedef enum {
|
||||
_PSX_IDLE = 0,
|
||||
_PSX_SETUP = 1,
|
||||
_PSX_SYSCALL = 2,
|
||||
_PSX_EXITING = 3,
|
||||
} psx_tracker_state_t;
|
||||
|
||||
/*
|
||||
* Tracking threads is done via a hash map of these objects.
|
||||
*/
|
||||
typedef struct psx_thread_ref_s {
|
||||
long sweep;
|
||||
long pending;
|
||||
long tid;
|
||||
long retval;
|
||||
} psx_thread_ref_t;
|
||||
|
||||
/*
|
||||
* This global structure holds the global coordination state for
|
||||
* libcap's psx_syscall() support.
|
||||
*/
|
||||
typedef struct {
|
||||
long pid;
|
||||
char *pid_path;
|
||||
|
||||
psx_mutex_t state_mu;
|
||||
psx_tracker_state_t state;
|
||||
int initialized;
|
||||
int incomplete;
|
||||
int psx_sig;
|
||||
int force_failure; /* leave this as zero to avoid forcing a crash */
|
||||
psx_sensitivity_t sensitivity;
|
||||
|
||||
struct {
|
||||
long syscall_nr;
|
||||
long arg1, arg2, arg3, arg4, arg5, arg6;
|
||||
int six;
|
||||
int active;
|
||||
} cmd;
|
||||
|
||||
/* This is kept opaque here, but its details are known to psx_calls.c */
|
||||
void *actions;
|
||||
|
||||
int map_entries;
|
||||
long map_mask;
|
||||
psx_thread_ref_t *map;
|
||||
} psx_tracker_t;
|
||||
|
||||
/* defined in psx_calls.c */
|
||||
extern psx_tracker_t psx_tracker;
|
||||
extern int psx_actions_size(void);
|
||||
extern void psx_confirm_sigaction(void);
|
||||
|
||||
#endif /* def LIBPSX_H */
|
||||
213
psx/psx.c
213
psx/psx.c
@ -17,7 +17,6 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h> /* pthread_atfork() */
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
@ -29,10 +28,8 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Not reliably defined by *libc so, alias the direct syscall. */
|
||||
#define _psx_gettid() syscall(SYS_gettid)
|
||||
|
||||
#include "psx_syscall.h"
|
||||
#include "../psx/libpsx.h"
|
||||
|
||||
#ifdef _PSX_DEBUG_MEMORY
|
||||
|
||||
@ -45,7 +42,8 @@ static void *_psx_calloc(const char *file, const int line,
|
||||
}
|
||||
|
||||
static void _psx_free(const char *file, const int line, void *ptr) {
|
||||
fprintf(stderr, "psx:%d:%s:%d: free(%p)\n", _psx_gettid(), file, line, ptr);
|
||||
fprintf(stderr, "psx:%d:%s:%d: free(%p)\n",
|
||||
_psx_gettid(), file, line, ptr);
|
||||
return free(ptr);
|
||||
}
|
||||
|
||||
@ -73,75 +71,12 @@ void psx_load_syscalls(long int (**syscall_fn)(long int,
|
||||
}
|
||||
|
||||
/*
|
||||
* Since we no longer (libcap-2.72) operate at the pthreads
|
||||
* abstraction, we need our own mutex etc implementation.
|
||||
* This global coordinates the PSX mechanism.
|
||||
*/
|
||||
|
||||
typedef uint8_t psx_mutex_t;
|
||||
#define _psx_mu_blocked(x) \
|
||||
__atomic_test_and_set((void *)(x), __ATOMIC_SEQ_CST)
|
||||
#define _psx_mu_lock(x) \
|
||||
while (_psx_mu_blocked(x)) sched_yield()
|
||||
#define _psx_mu_unlock(x) \
|
||||
__atomic_clear((void *)(x), __ATOMIC_SEQ_CST)
|
||||
#define _psx_mu_unlock_return(x, y) \
|
||||
do { _psx_mu_unlock(x); return (y); } while (0)
|
||||
#define _psx_mu_cond_wait(x) \
|
||||
do { \
|
||||
_psx_mu_unlock(x); \
|
||||
sched_yield(); \
|
||||
_psx_mu_lock(x); \
|
||||
} while (0)
|
||||
|
||||
typedef enum {
|
||||
_PSX_IDLE = 0,
|
||||
_PSX_SETUP = 1,
|
||||
_PSX_SYSCALL = 2,
|
||||
_PSX_EXITING = 3,
|
||||
} psx_tracker_state_t;
|
||||
|
||||
/*
|
||||
* Tracking threads is done via a hash map of these objects.
|
||||
*/
|
||||
typedef struct psx_thread_ref_s {
|
||||
long sweep;
|
||||
long pending;
|
||||
long tid;
|
||||
long retval;
|
||||
} psx_thread_ref_t;
|
||||
|
||||
/*
|
||||
* This global structure holds the global coordination state for
|
||||
* libcap's psx_syscall() support.
|
||||
*/
|
||||
static struct psx_tracker_s {
|
||||
long pid;
|
||||
char *pid_path;
|
||||
|
||||
psx_mutex_t state_mu;
|
||||
psx_tracker_state_t state;
|
||||
int initialized;
|
||||
int incomplete;
|
||||
int psx_sig;
|
||||
psx_sensitivity_t sensitivity;
|
||||
|
||||
struct {
|
||||
long syscall_nr;
|
||||
long arg1, arg2, arg3, arg4, arg5, arg6;
|
||||
int six;
|
||||
int active;
|
||||
} cmd;
|
||||
|
||||
struct sigaction sig_action;
|
||||
struct sigaction chained_action;
|
||||
|
||||
int map_entries;
|
||||
long map_mask;
|
||||
psx_thread_ref_t *map;
|
||||
} psx_tracker;
|
||||
__attribute__((visibility ("hidden"))) psx_tracker_t psx_tracker;
|
||||
|
||||
/* psx_mix is our trivial hash mixing for the thread reference map */
|
||||
static long psx_mix(long value) {
|
||||
__attribute__((visibility ("hidden"))) long psx_mix(long value) {
|
||||
return value ^ (value >> 7) ^ (value >> 13) ^ (value >> 23);
|
||||
}
|
||||
|
||||
@ -168,7 +103,7 @@ static void _psx_proc_start(void)
|
||||
long pid = getpid();
|
||||
psx_tracker.pid = pid;
|
||||
if (psx_tracker.pid_path == NULL) {
|
||||
psx_tracker.pid_path = malloc(3*sizeof(pid) + 13 /* strlen(taskdir_fmt) */);
|
||||
psx_tracker.pid_path = calloc(1, 3*sizeof(pid) + sizeof(taskdir_fmt));
|
||||
}
|
||||
sprintf(psx_tracker.pid_path, taskdir_fmt, pid);
|
||||
psx_tracker.state = _PSX_IDLE;
|
||||
@ -194,11 +129,22 @@ static void psx_syscall_start(void)
|
||||
*
|
||||
* https://bugzilla.kernel.org/show_bug.cgi?id=210533
|
||||
*
|
||||
* Our current strategy is to aggressively intercept SIGSYS,
|
||||
* We tried SYGSYS until
|
||||
*
|
||||
* https://bugzilla.kernel.org/show_bug.cgi?id=219687
|
||||
*
|
||||
* Our current strategy is to aggressively intercept SIGNO=33,
|
||||
* something that is confirmed to be the case each time _PSX_SETUP
|
||||
* state is entered.
|
||||
* state is entered. Note, this signal is special and hidden by
|
||||
* glibc and musl, but we inject our use of it via raw system
|
||||
* calls and quietly cooperate with those library usages. Go
|
||||
* treats this signal specially (avoiding blocking it for extended
|
||||
* periods) because of its hidden glibc usage as discussed here:
|
||||
*
|
||||
* https://github.com/golang/go/issues/42494
|
||||
*/
|
||||
psx_tracker.psx_sig = SIGSYS;
|
||||
psx_tracker.psx_sig = 33;
|
||||
psx_tracker.actions = calloc(2, psx_actions_size());
|
||||
psx_set_map(256);
|
||||
atexit(_psx_cleanup);
|
||||
pthread_atfork(NULL, NULL, _psx_new_proc);
|
||||
@ -210,7 +156,7 @@ static void psx_syscall_start(void)
|
||||
* to be confused with psx_sig (interrupt) blocking - which is
|
||||
* performed when the signal handler is being confirmed.
|
||||
*/
|
||||
static void psx_lock(void)
|
||||
__attribute__((visibility ("hidden"))) void psx_lock(void)
|
||||
{
|
||||
_psx_mu_lock(&psx_tracker.state_mu);
|
||||
if (!psx_tracker.initialized) {
|
||||
@ -222,7 +168,7 @@ static void psx_lock(void)
|
||||
/*
|
||||
* This is the only way this library unlocks.
|
||||
*/
|
||||
static void psx_unlock(void)
|
||||
__attribute__((visibility ("hidden"))) void psx_unlock(void)
|
||||
{
|
||||
_psx_mu_unlock(&psx_tracker.state_mu);
|
||||
}
|
||||
@ -232,7 +178,7 @@ static void psx_unlock(void)
|
||||
* other code to run that may require the lock. This is the only way
|
||||
* the psx code waits like this.
|
||||
*/
|
||||
static void psx_cond_wait(void)
|
||||
__attribute__((visibility ("hidden"))) void psx_cond_wait(void)
|
||||
{
|
||||
_psx_mu_cond_wait(&psx_tracker.state_mu);
|
||||
}
|
||||
@ -251,96 +197,12 @@ static void _psx_cleanup(void) {
|
||||
psx_cond_wait();
|
||||
}
|
||||
psx_tracker.state = _PSX_EXITING;
|
||||
free(psx_tracker.actions);
|
||||
free(psx_tracker.map);
|
||||
free(psx_tracker.pid_path);
|
||||
psx_unlock();
|
||||
}
|
||||
|
||||
/*
|
||||
* psx_posix_syscall_actor performs the system call on the targeted
|
||||
* thread and signals it is no longer pending.
|
||||
*/
|
||||
static void psx_posix_syscall_actor(int signum, siginfo_t *info, void *ignore) {
|
||||
/* bail early to the next in the chain if not something we recognize */
|
||||
if (signum != psx_tracker.psx_sig || !psx_tracker.cmd.active ||
|
||||
info == NULL || info->si_code != SI_TKILL ||
|
||||
info->si_pid != psx_tracker.pid) {
|
||||
if (psx_tracker.chained_action.sa_sigaction != 0) {
|
||||
psx_tracker.chained_action.sa_sigaction(signum, info, ignore);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
long int retval;
|
||||
if (!psx_tracker.cmd.six) {
|
||||
retval = syscall(psx_tracker.cmd.syscall_nr,
|
||||
psx_tracker.cmd.arg1,
|
||||
psx_tracker.cmd.arg2,
|
||||
psx_tracker.cmd.arg3);
|
||||
} else {
|
||||
retval = syscall(psx_tracker.cmd.syscall_nr,
|
||||
psx_tracker.cmd.arg1,
|
||||
psx_tracker.cmd.arg2,
|
||||
psx_tracker.cmd.arg3,
|
||||
psx_tracker.cmd.arg4,
|
||||
psx_tracker.cmd.arg5,
|
||||
psx_tracker.cmd.arg6);
|
||||
}
|
||||
|
||||
/*
|
||||
* communicate the result of the thread's attempt to perform the
|
||||
* syscall.
|
||||
*/
|
||||
long tid = _psx_gettid();
|
||||
|
||||
psx_lock();
|
||||
psx_thread_ref_t *ref = &psx_tracker.map[psx_mix(tid) & psx_tracker.map_mask];
|
||||
ref->retval = retval;
|
||||
ref->pending = 0;
|
||||
/*
|
||||
* Block this thread until all threads have been interrupted.
|
||||
* This prevents threads clone()ing after running the syscall and
|
||||
* confusing the psx mechanism into thinking they need to also run
|
||||
* the syscall. They wouldn't need to run it, because they would
|
||||
* inherit the thread state of a syscall that has already
|
||||
* happened. However, figuring that out for an unblocked thread is
|
||||
* hard, so we prevent it from happening.
|
||||
*/
|
||||
while (psx_tracker.cmd.active) {
|
||||
psx_cond_wait();
|
||||
}
|
||||
psx_tracker.incomplete--;
|
||||
psx_unlock();
|
||||
}
|
||||
|
||||
/*
|
||||
* psx_confirm_sigaction (re)confirms 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* under lock perform a state transition. Changing state is generally
|
||||
* done via this function. However, there is a single exception in
|
||||
@ -466,7 +328,6 @@ long int __psx_syscall(long int syscall_nr, ...) {
|
||||
|
||||
int restore_errno = errno;
|
||||
psx_new_state(_PSX_SETUP, _PSX_SYSCALL);
|
||||
psx_tracker.cmd.active = 1;
|
||||
|
||||
/*
|
||||
* cleaning up before we start helps a fork()ed child not inherit
|
||||
@ -548,9 +409,25 @@ long int __psx_syscall(long int syscall_nr, ...) {
|
||||
free(old);
|
||||
x = &psx_tracker.map[mix & psx_tracker.map_mask];
|
||||
}
|
||||
/* a new entry */
|
||||
/*
|
||||
* A new entry - this is where we will also
|
||||
* (first) enable the PSX parts of our installed
|
||||
* handler. This is, potentially racing with other
|
||||
* users of the same signal, so we do this under
|
||||
* lock.
|
||||
*/
|
||||
psx_lock();
|
||||
x->pending = 1;
|
||||
x->tid = tid;
|
||||
psx_tracker.cmd.active = 1;
|
||||
psx_unlock();
|
||||
/*
|
||||
* There is a small chance that this signal may be
|
||||
* racing with another user of this signal.
|
||||
* Locking above should ensure both forks of the
|
||||
* handler get invoked - perhaps out of order
|
||||
* though...
|
||||
*/
|
||||
syscall(SYS_tkill, tid, psx_tracker.psx_sig);
|
||||
}
|
||||
psx_lock();
|
||||
@ -645,6 +522,12 @@ int psx_set_sensitivity(psx_sensitivity_t level) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following is required for legacy linkage libcap-2.71 and
|
||||
* earlier backward compatibility. The Go use of psx no longer has any
|
||||
* need for this, and does not provide the define when building this
|
||||
* code.
|
||||
*/
|
||||
#ifdef _LIBPSX_PTHREAD_LINKAGE
|
||||
|
||||
/*
|
||||
|
||||
258
psx/psx_calls.c
Normal file
258
psx/psx_calls.c
Normal file
@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Andrew G. Morgan <morgan@kernel.org>
|
||||
*
|
||||
* This file contains the low level magic to ensure that the PSX
|
||||
* mechanism is the first to intercept the specified psx_sig signal.
|
||||
* This is a hidden function of the psx library in its own file
|
||||
* because its use of kernel headers conflicts badly with the more
|
||||
* traditional *libc provided headers.
|
||||
*/
|
||||
#include <linux/signal.h>
|
||||
#include <string.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "psx_syscall.h"
|
||||
#include "../psx/libpsx.h"
|
||||
|
||||
/*
|
||||
* This is terrible, but some of the OS installed <asm/signal.h>
|
||||
* headers only seem to know about the legacy 32-bit signal masks. So,
|
||||
* we need to #define our way around that...
|
||||
*
|
||||
* Also, the rt_sig*() system calls use a different sigaction
|
||||
* definition in these cases, so #define around that too.
|
||||
*/
|
||||
#if defined(__arm__) || defined(__i386__) || defined(__aarch64__)
|
||||
|
||||
#undef _NSIG
|
||||
#undef _NSIG_BPW
|
||||
#undef _NSIG_WORDS
|
||||
#undef SA_RESTORER
|
||||
#undef sa_handler
|
||||
#undef sa_sigaction
|
||||
|
||||
#define _NSIG 64
|
||||
#define _NSIG_BPW (8*sizeof(unsigned long))
|
||||
#define _NSIG_WORDS (_NSIG / _NSIG_BPW)
|
||||
|
||||
#if defined(__arm__) || defined(__i386__)
|
||||
#define SA_RESTORER 0x04000000
|
||||
#endif /* architectures that use SA_RESTORER */
|
||||
|
||||
typedef struct {
|
||||
unsigned long sig[_NSIG_WORDS];
|
||||
} psx_sigset_t;
|
||||
|
||||
#define sigset_t psx_sigset_t
|
||||
|
||||
struct psx_sigaction {
|
||||
void *sa_handler;
|
||||
unsigned long sa_flags;
|
||||
#ifdef SA_RESTORER
|
||||
void *sa_restorer;
|
||||
#endif
|
||||
sigset_t sa_mask;
|
||||
};
|
||||
|
||||
#define sigaction psx_sigaction
|
||||
|
||||
#endif /* various architecture defines */
|
||||
|
||||
/*
|
||||
* Local definition of the "sigaction"s for signal handling including
|
||||
* chaining of signal handlers. The content of this type is not known
|
||||
* outside of this present source file. The reason for this
|
||||
* obfuscation is the fact that this present file uses kernel headers
|
||||
* which may differ in structure definitions from the regular *libc
|
||||
* implementations with the same name.
|
||||
*/
|
||||
typedef struct {
|
||||
struct sigaction sig_action;
|
||||
struct sigaction chained_action;
|
||||
} psx_actions_t;
|
||||
|
||||
/*
|
||||
* Used to allocate space for the actions in the tracker, without
|
||||
* revealing the content outside this current file.
|
||||
*/
|
||||
__attribute__((visibility ("hidden"))) int psx_actions_size(void) {
|
||||
return sizeof(psx_actions_t);
|
||||
}
|
||||
|
||||
/* int how, sigset_t *nset, sigset_t *oset */
|
||||
#define _psx_rt_sigprocmask(how, nset, oset) \
|
||||
syscall(SYS_rt_sigprocmask, how, nset, oset, sizeof(sigset_t))
|
||||
|
||||
/* int sig, struct sigaction *act, struct sigaction *oact */
|
||||
#define _psx_rt_sigaction(sig, act, oact) \
|
||||
syscall(SYS_rt_sigaction, sig, act, oact, sizeof(sigset_t))
|
||||
|
||||
#define _psx_sigemptyset(pmask) \
|
||||
memset(pmask, 0, sizeof(*(pmask)))
|
||||
|
||||
#ifdef _NSIG_WORDS
|
||||
|
||||
#define _psx_sigaddset(pmask, signo) \
|
||||
(pmask)->sig[(signo-1)/_NSIG_BPW] |= 1UL << (((signo-1)%_NSIG_BPW))
|
||||
|
||||
#else /* ndef _NSIG_WORDS */
|
||||
|
||||
#define _psx_sigaddset(pmask, signo) \
|
||||
*(pmask) |= 1UL << (signo-1)
|
||||
|
||||
#endif /* def _NSIG_WORDS */
|
||||
|
||||
#ifdef SA_RESTORER
|
||||
/*
|
||||
* Actual assembly code for this "function" is embedded in
|
||||
* hidden form in psx_posix_syscall_actor() below.
|
||||
*/
|
||||
__attribute__((visibility ("hidden"))) void psx_restorer(void);
|
||||
#endif /* def SA_RESTORER */
|
||||
|
||||
/*
|
||||
* psx_posix_syscall_actor performs the system call on the targeted
|
||||
* thread and signals it is no longer pending.
|
||||
*/
|
||||
static void psx_posix_syscall_actor(int signum, siginfo_t *info, void *ignore) {
|
||||
/* bail early to the next in the chain if not something we recognize */
|
||||
psx_lock();
|
||||
if (signum != psx_tracker.psx_sig || !psx_tracker.cmd.active ||
|
||||
info == NULL || info->si_code != SI_TKILL ||
|
||||
info->si_pid != psx_tracker.pid) {
|
||||
psx_actions_t *actions;
|
||||
void (*chained_actor)(int, siginfo_t *,void *);
|
||||
psx_unlock();
|
||||
actions = psx_tracker.actions;
|
||||
chained_actor = (void *) actions->chained_action.sa_handler;
|
||||
if (chained_actor != 0) {
|
||||
chained_actor(signum, info, ignore);
|
||||
}
|
||||
#ifdef SA_RESTORER
|
||||
/*
|
||||
* This architecture requires we provide a signal exit fixup
|
||||
* trampoline. We use some assembly to provide this, aliased
|
||||
* to a function (prototype above). This assembly won't be
|
||||
* executed as part of the preceding code, but has to live
|
||||
* somewhere so we hide it here where it will get compiled,
|
||||
* but not slow down the PSX functionality.
|
||||
*/
|
||||
if (psx_tracker.force_failure) {
|
||||
#if defined(__x86_64__)
|
||||
asm("\npsx_restorer:\n\tmov $15,%rax\n\tsyscall\n");
|
||||
#elif defined(__i386__)
|
||||
asm("\npsx_restorer:\n\tmov $173, %eax\n\tint $0x80\n");
|
||||
#elif defined(__arm__)
|
||||
asm("\npsx_restorer:\n\tmov r7,#173\n\tswi 0\n");
|
||||
#else
|
||||
#error "unsupported architecture - https://bugzilla.kernel.org/show_bug.cgi?id=219687"
|
||||
/*
|
||||
* These are supported by go (go tool dist list | grep linux),
|
||||
* so we plan to also support them:
|
||||
* linux/loong64
|
||||
* linux/mips
|
||||
* linux/mips64
|
||||
* linux/mips64le
|
||||
* linux/mipsle
|
||||
* linux/ppc64
|
||||
* linux/ppc64le
|
||||
* linux/riscv64
|
||||
* linux/s390x
|
||||
*/
|
||||
#endif /* supported architectures */
|
||||
}
|
||||
#endif /* def SA_RESTORER */
|
||||
return;
|
||||
}
|
||||
psx_unlock();
|
||||
|
||||
long int retval;
|
||||
if (!psx_tracker.cmd.six) {
|
||||
retval = syscall(psx_tracker.cmd.syscall_nr,
|
||||
psx_tracker.cmd.arg1,
|
||||
psx_tracker.cmd.arg2,
|
||||
psx_tracker.cmd.arg3);
|
||||
} else {
|
||||
retval = syscall(psx_tracker.cmd.syscall_nr,
|
||||
psx_tracker.cmd.arg1,
|
||||
psx_tracker.cmd.arg2,
|
||||
psx_tracker.cmd.arg3,
|
||||
psx_tracker.cmd.arg4,
|
||||
psx_tracker.cmd.arg5,
|
||||
psx_tracker.cmd.arg6);
|
||||
}
|
||||
|
||||
/*
|
||||
* communicate the result of the thread's attempt to perform the
|
||||
* syscall.
|
||||
*/
|
||||
long tid = _psx_gettid();
|
||||
|
||||
psx_lock();
|
||||
psx_thread_ref_t *ref =
|
||||
&psx_tracker.map[psx_mix(tid) & psx_tracker.map_mask];
|
||||
ref->retval = retval;
|
||||
ref->pending = 0;
|
||||
/*
|
||||
* Block this thread until all threads have been interrupted.
|
||||
* This prevents threads clone()ing after running the syscall and
|
||||
* confusing the psx mechanism into thinking they need to also run
|
||||
* the syscall. They wouldn't need to run it, because they would
|
||||
* inherit the thread state of a syscall that has already
|
||||
* happened. However, figuring that out for an unblocked thread is
|
||||
* hard, so we prevent it from happening.
|
||||
*/
|
||||
while (psx_tracker.cmd.active) {
|
||||
psx_cond_wait();
|
||||
}
|
||||
psx_tracker.incomplete--;
|
||||
psx_unlock();
|
||||
}
|
||||
|
||||
/*
|
||||
* only used by psx_confirm_sigaction() which is called under lock. We
|
||||
* don't have to rely on the stack for this structure because we're
|
||||
* reserving static space for it.
|
||||
*/
|
||||
static struct sigaction existing_sa;
|
||||
|
||||
/*
|
||||
* psx_confirm_sigaction (re)confirms that the psx handler is the
|
||||
* first handler to respond to the psx signal. It assumes that
|
||||
* psx_tracker.psx_sig has been set.
|
||||
*/
|
||||
__attribute__((visibility ("hidden"))) void psx_confirm_sigaction(void) {
|
||||
sigset_t mask, orig;
|
||||
psx_actions_t *actions = psx_tracker.actions;
|
||||
|
||||
/*
|
||||
* Block interrupts while potentially rewriting the handler.
|
||||
*/
|
||||
_psx_sigemptyset(&mask);
|
||||
_psx_sigaddset(&mask, psx_tracker.psx_sig);
|
||||
_psx_rt_sigprocmask(SIG_BLOCK, &mask, &orig);
|
||||
_psx_rt_sigaction(psx_tracker.psx_sig, NULL, &existing_sa);
|
||||
if (existing_sa.sa_handler != (void *) &psx_posix_syscall_actor) {
|
||||
memcpy(&actions->chained_action, &existing_sa,
|
||||
sizeof(struct sigaction));
|
||||
/*
|
||||
* no less masking etc than original handler (some
|
||||
* architectures hook things on the restorer etc)
|
||||
*/
|
||||
memcpy(&actions->sig_action, &existing_sa, sizeof(existing_sa));
|
||||
actions->sig_action.sa_handler = (void *) &psx_posix_syscall_actor;
|
||||
actions->sig_action.sa_flags |= SA_SIGINFO | SA_ONSTACK | SA_RESTART
|
||||
#ifdef SA_RESTORER
|
||||
| SA_RESTORER;
|
||||
/* if we need a restorer, and nothing is set, use our own */
|
||||
if (actions->sig_action.sa_restorer == 0) {
|
||||
actions->sig_action.sa_restorer = (void *) &psx_restorer;
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif /* def SA_RESTORER */
|
||||
_psx_rt_sigaction(psx_tracker.psx_sig, &actions->sig_action, NULL);
|
||||
}
|
||||
_psx_rt_sigprocmask(SIG_SETMASK, &orig, NULL);
|
||||
}
|
||||
@ -43,8 +43,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/*
|
||||
* Programmatic way to recognize feature set.
|
||||
*/
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
@ -119,7 +119,7 @@ int main(int argc, char **argv) {
|
||||
cap_free(caps);
|
||||
|
||||
printf("no privilege in main process thread: len:%ld, caps:\"%s\"\n",
|
||||
greatest_len, text);
|
||||
(long int) greatest_len, text);
|
||||
if (greatest_len != 1) {
|
||||
printf("failed to lower privilege as expected\n");
|
||||
exit(1);
|
||||
@ -148,7 +148,7 @@ int main(int argc, char **argv) {
|
||||
*/
|
||||
|
||||
printf("greatest privilege in main process thread: len:%ld, caps:\"%s\"\n",
|
||||
greatest_len, text);
|
||||
(long int) greatest_len, text);
|
||||
|
||||
cap_free(text);
|
||||
if (greatest_len != 1) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user