1*2810ac1bSKiyoung Kim /*
2*2810ac1bSKiyoung Kim * Copyright (c) 2019-21 Andrew G Morgan <[email protected]>
3*2810ac1bSKiyoung Kim *
4*2810ac1bSKiyoung Kim * This file contains a collection of routines that perform thread
5*2810ac1bSKiyoung Kim * synchronization to ensure that a whole process is running as a
6*2810ac1bSKiyoung Kim * single privilege entity - independent of the number of pthreads.
7*2810ac1bSKiyoung Kim *
8*2810ac1bSKiyoung Kim * The whole file would be unnecessary if glibc exported an explicit
9*2810ac1bSKiyoung Kim * psx_syscall()-like function that leveraged the nptl:setxid
10*2810ac1bSKiyoung Kim * mechanism to synchronize thread state over the whole process.
11*2810ac1bSKiyoung Kim */
12*2810ac1bSKiyoung Kim #undef _POSIX_C_SOURCE
13*2810ac1bSKiyoung Kim #define _POSIX_C_SOURCE 199309L
14*2810ac1bSKiyoung Kim
15*2810ac1bSKiyoung Kim #ifndef _GNU_SOURCE
16*2810ac1bSKiyoung Kim #define _GNU_SOURCE
17*2810ac1bSKiyoung Kim #endif
18*2810ac1bSKiyoung Kim
19*2810ac1bSKiyoung Kim #include <errno.h>
20*2810ac1bSKiyoung Kim #include <pthread.h>
21*2810ac1bSKiyoung Kim #include <sched.h>
22*2810ac1bSKiyoung Kim #include <signal.h>
23*2810ac1bSKiyoung Kim #include <stdarg.h>
24*2810ac1bSKiyoung Kim #include <stdio.h>
25*2810ac1bSKiyoung Kim #include <stdlib.h>
26*2810ac1bSKiyoung Kim #include <string.h>
27*2810ac1bSKiyoung Kim #include <unistd.h>
28*2810ac1bSKiyoung Kim #include <sys/syscall.h>
29*2810ac1bSKiyoung Kim
30*2810ac1bSKiyoung Kim #include "psx_syscall.h"
31*2810ac1bSKiyoung Kim
32*2810ac1bSKiyoung Kim #ifdef _PSX_DEBUG_MEMORY
33*2810ac1bSKiyoung Kim
_psx_calloc(const char * file,const int line,size_t nmemb,size_t size)34*2810ac1bSKiyoung Kim static void *_psx_calloc(const char *file, const int line,
35*2810ac1bSKiyoung Kim size_t nmemb, size_t size) {
36*2810ac1bSKiyoung Kim void *ptr = calloc(nmemb, size);
37*2810ac1bSKiyoung Kim fprintf(stderr, "psx:%d:%s:%d: calloc(%ld, %ld) -> %p\n", gettid(),
38*2810ac1bSKiyoung Kim file, line, (long int)nmemb, (long int)size, ptr);
39*2810ac1bSKiyoung Kim return ptr;
40*2810ac1bSKiyoung Kim }
41*2810ac1bSKiyoung Kim
_psx_free(const char * file,const int line,void * ptr)42*2810ac1bSKiyoung Kim static void _psx_free(const char *file, const int line, void *ptr) {
43*2810ac1bSKiyoung Kim fprintf(stderr, "psx:%d:%s:%d: free(%p)\n", gettid(), file, line, ptr);
44*2810ac1bSKiyoung Kim return free(ptr);
45*2810ac1bSKiyoung Kim }
46*2810ac1bSKiyoung Kim
47*2810ac1bSKiyoung Kim #define calloc(a, b) _psx_calloc(__FILE__, __LINE__, a, b)
48*2810ac1bSKiyoung Kim #define free(a) _psx_free(__FILE__, __LINE__, a)
49*2810ac1bSKiyoung Kim
50*2810ac1bSKiyoung Kim #endif /* def _PSX_DEBUG_MEMORY */
51*2810ac1bSKiyoung Kim
52*2810ac1bSKiyoung Kim /*
53*2810ac1bSKiyoung Kim * psx_load_syscalls() can be weakly defined in dependent libraries to
54*2810ac1bSKiyoung Kim * provide a mechanism for a library to optionally leverage this psx
55*2810ac1bSKiyoung Kim * mechanism. Specifically, when libcap calls psx_load_sycalls() it
56*2810ac1bSKiyoung Kim * provides a weakly declared default that maps its system calls to
57*2810ac1bSKiyoung Kim * the regular system call functions. However, when linked with psx,
58*2810ac1bSKiyoung Kim * this function here overrides the syscalls to be the psx ones.
59*2810ac1bSKiyoung Kim */
psx_load_syscalls(long int (** syscall_fn)(long int,long int,long int,long int),long int (** syscall6_fn)(long int,long int,long int,long int,long int,long int,long int))60*2810ac1bSKiyoung Kim void psx_load_syscalls(long int (**syscall_fn)(long int,
61*2810ac1bSKiyoung Kim long int, long int, long int),
62*2810ac1bSKiyoung Kim long int (**syscall6_fn)(long int,
63*2810ac1bSKiyoung Kim long int, long int, long int,
64*2810ac1bSKiyoung Kim long int, long int, long int))
65*2810ac1bSKiyoung Kim {
66*2810ac1bSKiyoung Kim *syscall_fn = psx_syscall3;
67*2810ac1bSKiyoung Kim *syscall6_fn = psx_syscall6;
68*2810ac1bSKiyoung Kim }
69*2810ac1bSKiyoung Kim
70*2810ac1bSKiyoung Kim /*
71*2810ac1bSKiyoung Kim * type to keep track of registered threads.
72*2810ac1bSKiyoung Kim */
73*2810ac1bSKiyoung Kim typedef struct registered_thread_s {
74*2810ac1bSKiyoung Kim struct registered_thread_s *next, *prev;
75*2810ac1bSKiyoung Kim pthread_t thread;
76*2810ac1bSKiyoung Kim pthread_mutex_t mu;
77*2810ac1bSKiyoung Kim int pending;
78*2810ac1bSKiyoung Kim int gone;
79*2810ac1bSKiyoung Kim long int retval;
80*2810ac1bSKiyoung Kim pid_t tid;
81*2810ac1bSKiyoung Kim } registered_thread_t;
82*2810ac1bSKiyoung Kim
83*2810ac1bSKiyoung Kim static pthread_once_t psx_tracker_initialized = PTHREAD_ONCE_INIT;
84*2810ac1bSKiyoung Kim
85*2810ac1bSKiyoung Kim typedef enum {
86*2810ac1bSKiyoung Kim _PSX_IDLE = 0,
87*2810ac1bSKiyoung Kim _PSX_SETUP = 1,
88*2810ac1bSKiyoung Kim _PSX_SYSCALL = 2,
89*2810ac1bSKiyoung Kim _PSX_CREATE = 3,
90*2810ac1bSKiyoung Kim _PSX_INFORK = 4,
91*2810ac1bSKiyoung Kim _PSX_EXITING = 5,
92*2810ac1bSKiyoung Kim } psx_tracker_state_t;
93*2810ac1bSKiyoung Kim
94*2810ac1bSKiyoung Kim /*
95*2810ac1bSKiyoung Kim * This global structure holds the global coordination state for
96*2810ac1bSKiyoung Kim * libcap's psx_posix_syscall() support.
97*2810ac1bSKiyoung Kim */
98*2810ac1bSKiyoung Kim static struct psx_tracker_s {
99*2810ac1bSKiyoung Kim int has_forked;
100*2810ac1bSKiyoung Kim
101*2810ac1bSKiyoung Kim pthread_mutex_t state_mu;
102*2810ac1bSKiyoung Kim pthread_cond_t cond; /* this is only used to wait on 'state' changes */
103*2810ac1bSKiyoung Kim psx_tracker_state_t state;
104*2810ac1bSKiyoung Kim int initialized;
105*2810ac1bSKiyoung Kim int psx_sig;
106*2810ac1bSKiyoung Kim psx_sensitivity_t sensitivity;
107*2810ac1bSKiyoung Kim
108*2810ac1bSKiyoung Kim struct {
109*2810ac1bSKiyoung Kim long syscall_nr;
110*2810ac1bSKiyoung Kim long arg1, arg2, arg3, arg4, arg5, arg6;
111*2810ac1bSKiyoung Kim int six;
112*2810ac1bSKiyoung Kim int active;
113*2810ac1bSKiyoung Kim } cmd;
114*2810ac1bSKiyoung Kim
115*2810ac1bSKiyoung Kim struct sigaction sig_action;
116*2810ac1bSKiyoung Kim struct sigaction chained_action;
117*2810ac1bSKiyoung Kim registered_thread_t *root;
118*2810ac1bSKiyoung Kim } psx_tracker;
119*2810ac1bSKiyoung Kim
120*2810ac1bSKiyoung Kim /*
121*2810ac1bSKiyoung Kim * psx_action_key is used for thread local storage of the thread's
122*2810ac1bSKiyoung Kim * registration.
123*2810ac1bSKiyoung Kim */
124*2810ac1bSKiyoung Kim pthread_key_t psx_action_key;
125*2810ac1bSKiyoung Kim
126*2810ac1bSKiyoung Kim /*
127*2810ac1bSKiyoung Kim * psx_do_registration called locked and creates a tracker entry for
128*2810ac1bSKiyoung Kim * the current thread with a TLS specific key pointing at the threads
129*2810ac1bSKiyoung Kim * specific tracker.
130*2810ac1bSKiyoung Kim */
psx_do_registration(void)131*2810ac1bSKiyoung Kim static void *psx_do_registration(void) {
132*2810ac1bSKiyoung Kim registered_thread_t *node = calloc(1, sizeof(registered_thread_t));
133*2810ac1bSKiyoung Kim if (node == NULL) {
134*2810ac1bSKiyoung Kim perror("unable to register psx handler");
135*2810ac1bSKiyoung Kim _exit(1);
136*2810ac1bSKiyoung Kim }
137*2810ac1bSKiyoung Kim pthread_mutex_init(&node->mu, NULL);
138*2810ac1bSKiyoung Kim node->thread = pthread_self();
139*2810ac1bSKiyoung Kim pthread_setspecific(psx_action_key, node);
140*2810ac1bSKiyoung Kim node->next = psx_tracker.root;
141*2810ac1bSKiyoung Kim if (node->next) {
142*2810ac1bSKiyoung Kim node->next->prev = node;
143*2810ac1bSKiyoung Kim }
144*2810ac1bSKiyoung Kim psx_tracker.root = node;
145*2810ac1bSKiyoung Kim return node;
146*2810ac1bSKiyoung Kim }
147*2810ac1bSKiyoung Kim
148*2810ac1bSKiyoung Kim /*
149*2810ac1bSKiyoung Kim * psx_posix_syscall_actor performs the system call on the targeted
150*2810ac1bSKiyoung Kim * thread and signals it is no longer pending.
151*2810ac1bSKiyoung Kim */
psx_posix_syscall_actor(int signum,siginfo_t * info,void * ignore)152*2810ac1bSKiyoung Kim static void psx_posix_syscall_actor(int signum, siginfo_t *info, void *ignore) {
153*2810ac1bSKiyoung Kim /* bail early if this isn't something we recognize */
154*2810ac1bSKiyoung Kim if (signum != psx_tracker.psx_sig || !psx_tracker.cmd.active ||
155*2810ac1bSKiyoung Kim info == NULL || info->si_code != SI_TKILL || info->si_pid != getpid()) {
156*2810ac1bSKiyoung Kim if (psx_tracker.chained_action.sa_sigaction != 0) {
157*2810ac1bSKiyoung Kim psx_tracker.chained_action.sa_sigaction(signum, info, ignore);
158*2810ac1bSKiyoung Kim }
159*2810ac1bSKiyoung Kim return;
160*2810ac1bSKiyoung Kim }
161*2810ac1bSKiyoung Kim
162*2810ac1bSKiyoung Kim long int retval;
163*2810ac1bSKiyoung Kim if (!psx_tracker.cmd.six) {
164*2810ac1bSKiyoung Kim retval = syscall(psx_tracker.cmd.syscall_nr,
165*2810ac1bSKiyoung Kim psx_tracker.cmd.arg1,
166*2810ac1bSKiyoung Kim psx_tracker.cmd.arg2,
167*2810ac1bSKiyoung Kim psx_tracker.cmd.arg3);
168*2810ac1bSKiyoung Kim } else {
169*2810ac1bSKiyoung Kim retval = syscall(psx_tracker.cmd.syscall_nr,
170*2810ac1bSKiyoung Kim psx_tracker.cmd.arg1,
171*2810ac1bSKiyoung Kim psx_tracker.cmd.arg2,
172*2810ac1bSKiyoung Kim psx_tracker.cmd.arg3,
173*2810ac1bSKiyoung Kim psx_tracker.cmd.arg4,
174*2810ac1bSKiyoung Kim psx_tracker.cmd.arg5,
175*2810ac1bSKiyoung Kim psx_tracker.cmd.arg6);
176*2810ac1bSKiyoung Kim }
177*2810ac1bSKiyoung Kim
178*2810ac1bSKiyoung Kim /*
179*2810ac1bSKiyoung Kim * This handler can only be called on registered threads which
180*2810ac1bSKiyoung Kim * have had this specific defined at start-up. (But see the
181*2810ac1bSKiyoung Kim * subsequent test.)
182*2810ac1bSKiyoung Kim */
183*2810ac1bSKiyoung Kim registered_thread_t *ref = pthread_getspecific(psx_action_key);
184*2810ac1bSKiyoung Kim if (ref) {
185*2810ac1bSKiyoung Kim pthread_mutex_lock(&ref->mu);
186*2810ac1bSKiyoung Kim ref->pending = 0;
187*2810ac1bSKiyoung Kim ref->retval = retval;
188*2810ac1bSKiyoung Kim ref->tid = syscall(SYS_gettid);
189*2810ac1bSKiyoung Kim pthread_mutex_unlock(&ref->mu);
190*2810ac1bSKiyoung Kim } /*
191*2810ac1bSKiyoung Kim * else thread must be dying and its psx_action_key has already
192*2810ac1bSKiyoung Kim * been cleaned up.
193*2810ac1bSKiyoung Kim */
194*2810ac1bSKiyoung Kim }
195*2810ac1bSKiyoung Kim
196*2810ac1bSKiyoung Kim /*
197*2810ac1bSKiyoung Kim * Some forward declarations for the initialization
198*2810ac1bSKiyoung Kim * psx_syscall_start() routine.
199*2810ac1bSKiyoung Kim */
200*2810ac1bSKiyoung Kim static void _psx_cleanup(void);
201*2810ac1bSKiyoung Kim static void _psx_prepare_fork(void);
202*2810ac1bSKiyoung Kim static void _psx_fork_completed(void);
203*2810ac1bSKiyoung Kim static void _psx_forked_child(void);
204*2810ac1bSKiyoung Kim int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
205*2810ac1bSKiyoung Kim void *(*start_routine) (void *), void *arg);
206*2810ac1bSKiyoung Kim
207*2810ac1bSKiyoung Kim /*
208*2810ac1bSKiyoung Kim * psx requires this function to be provided by the linkage wrapping.
209*2810ac1bSKiyoung Kim */
210*2810ac1bSKiyoung Kim extern int __real_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
211*2810ac1bSKiyoung Kim void *(*start_routine) (void *), void *arg);
212*2810ac1bSKiyoung Kim
213*2810ac1bSKiyoung Kim /*
214*2810ac1bSKiyoung Kim * psx_confirm_sigaction reconfirms that the psx handler is the first
215*2810ac1bSKiyoung Kim * handler to respond to the psx signal. It assumes that
216*2810ac1bSKiyoung Kim * psx_tracker.psx_sig has been set.
217*2810ac1bSKiyoung Kim */
psx_confirm_sigaction(void)218*2810ac1bSKiyoung Kim static void psx_confirm_sigaction(void) {
219*2810ac1bSKiyoung Kim sigset_t mask, orig;
220*2810ac1bSKiyoung Kim struct sigaction existing_sa;
221*2810ac1bSKiyoung Kim
222*2810ac1bSKiyoung Kim /*
223*2810ac1bSKiyoung Kim * Block interrupts while potentially rewriting the handler.
224*2810ac1bSKiyoung Kim */
225*2810ac1bSKiyoung Kim sigemptyset(&mask);
226*2810ac1bSKiyoung Kim sigaddset(&mask, psx_tracker.psx_sig);
227*2810ac1bSKiyoung Kim sigprocmask(SIG_BLOCK, &mask, &orig);
228*2810ac1bSKiyoung Kim
229*2810ac1bSKiyoung Kim sigaction(psx_tracker.psx_sig, NULL, &existing_sa);
230*2810ac1bSKiyoung Kim if (existing_sa.sa_sigaction != psx_posix_syscall_actor) {
231*2810ac1bSKiyoung Kim memcpy(&psx_tracker.chained_action, &existing_sa, sizeof(struct sigaction));
232*2810ac1bSKiyoung Kim psx_tracker.sig_action.sa_sigaction = psx_posix_syscall_actor;
233*2810ac1bSKiyoung Kim sigemptyset(&psx_tracker.sig_action.sa_mask);
234*2810ac1bSKiyoung Kim psx_tracker.sig_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
235*2810ac1bSKiyoung Kim sigaction(psx_tracker.psx_sig, &psx_tracker.sig_action, NULL);
236*2810ac1bSKiyoung Kim }
237*2810ac1bSKiyoung Kim
238*2810ac1bSKiyoung Kim sigprocmask(SIG_SETMASK, &orig, NULL);
239*2810ac1bSKiyoung Kim }
240*2810ac1bSKiyoung Kim
241*2810ac1bSKiyoung Kim /*
242*2810ac1bSKiyoung Kim * psx_syscall_start initializes the subsystem including initializing
243*2810ac1bSKiyoung Kim * the mutex.
244*2810ac1bSKiyoung Kim */
psx_syscall_start(void)245*2810ac1bSKiyoung Kim static void psx_syscall_start(void) {
246*2810ac1bSKiyoung Kim pthread_mutex_init(&psx_tracker.state_mu, NULL);
247*2810ac1bSKiyoung Kim pthread_cond_init(&psx_tracker.cond, NULL);
248*2810ac1bSKiyoung Kim pthread_key_create(&psx_action_key, NULL);
249*2810ac1bSKiyoung Kim pthread_atfork(_psx_prepare_fork, _psx_fork_completed, _psx_forked_child);
250*2810ac1bSKiyoung Kim
251*2810ac1bSKiyoung Kim /*
252*2810ac1bSKiyoung Kim * All sorts of things are assumed by Linux and glibc and/or musl
253*2810ac1bSKiyoung Kim * about signal handlers and which can be blocked. Go has its own
254*2810ac1bSKiyoung Kim * idiosyncrasies too. We tried SIGRTMAX until
255*2810ac1bSKiyoung Kim *
256*2810ac1bSKiyoung Kim * https://bugzilla.kernel.org/show_bug.cgi?id=210533
257*2810ac1bSKiyoung Kim *
258*2810ac1bSKiyoung Kim * Our current strategy is to aggressively intercept SIGSYS.
259*2810ac1bSKiyoung Kim */
260*2810ac1bSKiyoung Kim psx_tracker.psx_sig = SIGSYS;
261*2810ac1bSKiyoung Kim
262*2810ac1bSKiyoung Kim psx_confirm_sigaction();
263*2810ac1bSKiyoung Kim psx_do_registration(); /* register the main thread. */
264*2810ac1bSKiyoung Kim atexit(_psx_cleanup);
265*2810ac1bSKiyoung Kim
266*2810ac1bSKiyoung Kim psx_tracker.initialized = 1;
267*2810ac1bSKiyoung Kim }
268*2810ac1bSKiyoung Kim
269*2810ac1bSKiyoung Kim /*
270*2810ac1bSKiyoung Kim * This is the only way this library globally locks. Note, this is not
271*2810ac1bSKiyoung Kim * to be confused with psx_sig (interrupt) blocking - which is
272*2810ac1bSKiyoung Kim * performed around thread creation and when the signal handler is
273*2810ac1bSKiyoung Kim * being confirmed.
274*2810ac1bSKiyoung Kim */
psx_lock(void)275*2810ac1bSKiyoung Kim static void psx_lock(void)
276*2810ac1bSKiyoung Kim {
277*2810ac1bSKiyoung Kim pthread_once(&psx_tracker_initialized, psx_syscall_start);
278*2810ac1bSKiyoung Kim pthread_mutex_lock(&psx_tracker.state_mu);
279*2810ac1bSKiyoung Kim }
280*2810ac1bSKiyoung Kim
281*2810ac1bSKiyoung Kim /*
282*2810ac1bSKiyoung Kim * This is the only way this library unlocks.
283*2810ac1bSKiyoung Kim */
psx_unlock(void)284*2810ac1bSKiyoung Kim static void psx_unlock(void)
285*2810ac1bSKiyoung Kim {
286*2810ac1bSKiyoung Kim pthread_mutex_unlock(&psx_tracker.state_mu);
287*2810ac1bSKiyoung Kim }
288*2810ac1bSKiyoung Kim
289*2810ac1bSKiyoung Kim /*
290*2810ac1bSKiyoung Kim * under lock perform a state transition. Changing state is generally
291*2810ac1bSKiyoung Kim * done via this function. However, there is a single exception in
292*2810ac1bSKiyoung Kim * _psx_cleanup().
293*2810ac1bSKiyoung Kim */
psx_new_state(psx_tracker_state_t was,psx_tracker_state_t is)294*2810ac1bSKiyoung Kim static void psx_new_state(psx_tracker_state_t was, psx_tracker_state_t is)
295*2810ac1bSKiyoung Kim {
296*2810ac1bSKiyoung Kim psx_lock();
297*2810ac1bSKiyoung Kim while (psx_tracker.state != was) {
298*2810ac1bSKiyoung Kim pthread_cond_wait(&psx_tracker.cond, &psx_tracker.state_mu);
299*2810ac1bSKiyoung Kim }
300*2810ac1bSKiyoung Kim psx_tracker.state = is;
301*2810ac1bSKiyoung Kim if (is == _PSX_IDLE) {
302*2810ac1bSKiyoung Kim /* only announce newly idle states since that is all we wait for */
303*2810ac1bSKiyoung Kim pthread_cond_signal(&psx_tracker.cond);
304*2810ac1bSKiyoung Kim }
305*2810ac1bSKiyoung Kim psx_unlock();
306*2810ac1bSKiyoung Kim }
307*2810ac1bSKiyoung Kim
psx_syscall3(long int syscall_nr,long int arg1,long int arg2,long int arg3)308*2810ac1bSKiyoung Kim long int psx_syscall3(long int syscall_nr,
309*2810ac1bSKiyoung Kim long int arg1, long int arg2, long int arg3) {
310*2810ac1bSKiyoung Kim return psx_syscall(syscall_nr, arg1, arg2, arg3);
311*2810ac1bSKiyoung Kim }
312*2810ac1bSKiyoung Kim
psx_syscall6(long int syscall_nr,long int arg1,long int arg2,long int arg3,long int arg4,long int arg5,long int arg6)313*2810ac1bSKiyoung Kim long int psx_syscall6(long int syscall_nr,
314*2810ac1bSKiyoung Kim long int arg1, long int arg2, long int arg3,
315*2810ac1bSKiyoung Kim long int arg4, long int arg5, long int arg6) {
316*2810ac1bSKiyoung Kim return psx_syscall(syscall_nr, arg1, arg2, arg3, arg4, arg5, arg6);
317*2810ac1bSKiyoung Kim }
318*2810ac1bSKiyoung Kim
_psx_prepare_fork(void)319*2810ac1bSKiyoung Kim static void _psx_prepare_fork(void) {
320*2810ac1bSKiyoung Kim /*
321*2810ac1bSKiyoung Kim * obtain global lock - we don't want any syscalls while the fork
322*2810ac1bSKiyoung Kim * is occurring since it may interfere with the preparation for
323*2810ac1bSKiyoung Kim * the fork.
324*2810ac1bSKiyoung Kim */
325*2810ac1bSKiyoung Kim psx_new_state(_PSX_IDLE, _PSX_INFORK);
326*2810ac1bSKiyoung Kim }
327*2810ac1bSKiyoung Kim
_psx_fork_completed(void)328*2810ac1bSKiyoung Kim static void _psx_fork_completed(void) {
329*2810ac1bSKiyoung Kim /*
330*2810ac1bSKiyoung Kim * The only way we can get here is if state is _PSX_INFORK and was
331*2810ac1bSKiyoung Kim * previously _PSX_IDLE. Now that the fork has completed, the
332*2810ac1bSKiyoung Kim * parent can continue as if it hadn't happened - the forked child
333*2810ac1bSKiyoung Kim * does not tie its security state to that of the parent process
334*2810ac1bSKiyoung Kim * and threads.
335*2810ac1bSKiyoung Kim *
336*2810ac1bSKiyoung Kim * We don't strictly need to change the psx_tracker.state since we
337*2810ac1bSKiyoung Kim * hold the mutex over the fork, but we do to make deadlock
338*2810ac1bSKiyoung Kim * debugging easier.
339*2810ac1bSKiyoung Kim */
340*2810ac1bSKiyoung Kim psx_new_state(_PSX_INFORK, _PSX_IDLE);
341*2810ac1bSKiyoung Kim }
342*2810ac1bSKiyoung Kim
_psx_forked_child(void)343*2810ac1bSKiyoung Kim static void _psx_forked_child(void) {
344*2810ac1bSKiyoung Kim /*
345*2810ac1bSKiyoung Kim * The only way we can get here is if state is _PSX_INFORK and was
346*2810ac1bSKiyoung Kim * previously _PSX_IDLE. However, none of the registered threads
347*2810ac1bSKiyoung Kim * exist in this newly minted child process, so we have to reset
348*2810ac1bSKiyoung Kim * the tracking structure to avoid any confusion. We also scuttle
349*2810ac1bSKiyoung Kim * any chance of the PSX API working on more than one thread in
350*2810ac1bSKiyoung Kim * the child by leaving the state as _PSX_INFORK. We do support
351*2810ac1bSKiyoung Kim * all psx_syscall()s by reverting to them being direct in the
352*2810ac1bSKiyoung Kim * fork()ed child.
353*2810ac1bSKiyoung Kim *
354*2810ac1bSKiyoung Kim * We do this because the glibc man page for fork() suggests that
355*2810ac1bSKiyoung Kim * only a subset of things will work post fork(). Specifically,
356*2810ac1bSKiyoung Kim * only a "async-signal-safe functions (see signal-safety(7))
357*2810ac1bSKiyoung Kim * until such time as it calls execve(2)" can be relied upon. That
358*2810ac1bSKiyoung Kim * man page suggests that you can't expect mutexes to work: "not
359*2810ac1bSKiyoung Kim * async-signal-safe because it uses pthread_mutex_lock(3)
360*2810ac1bSKiyoung Kim * internally.".
361*2810ac1bSKiyoung Kim */
362*2810ac1bSKiyoung Kim registered_thread_t *next, *old_root;
363*2810ac1bSKiyoung Kim old_root = psx_tracker.root;
364*2810ac1bSKiyoung Kim psx_tracker.root = NULL;
365*2810ac1bSKiyoung Kim
366*2810ac1bSKiyoung Kim psx_tracker.has_forked = 1;
367*2810ac1bSKiyoung Kim
368*2810ac1bSKiyoung Kim for (; old_root; old_root = next) {
369*2810ac1bSKiyoung Kim next = old_root->next;
370*2810ac1bSKiyoung Kim memset(old_root, 0, sizeof(*old_root));
371*2810ac1bSKiyoung Kim free(old_root);
372*2810ac1bSKiyoung Kim }
373*2810ac1bSKiyoung Kim }
374*2810ac1bSKiyoung Kim
375*2810ac1bSKiyoung Kim /*
376*2810ac1bSKiyoung Kim * called locked to unregister a node from the tracker.
377*2810ac1bSKiyoung Kim */
psx_do_unregister(registered_thread_t * node)378*2810ac1bSKiyoung Kim static void psx_do_unregister(registered_thread_t *node) {
379*2810ac1bSKiyoung Kim if (psx_tracker.root == node) {
380*2810ac1bSKiyoung Kim psx_tracker.root = node->next;
381*2810ac1bSKiyoung Kim }
382*2810ac1bSKiyoung Kim if (node->next) {
383*2810ac1bSKiyoung Kim node->next->prev = node->prev;
384*2810ac1bSKiyoung Kim }
385*2810ac1bSKiyoung Kim if (node->prev) {
386*2810ac1bSKiyoung Kim node->prev->next = node->next;
387*2810ac1bSKiyoung Kim }
388*2810ac1bSKiyoung Kim pthread_mutex_destroy(&node->mu);
389*2810ac1bSKiyoung Kim memset(node, 0, sizeof(*node));
390*2810ac1bSKiyoung Kim free(node);
391*2810ac1bSKiyoung Kim }
392*2810ac1bSKiyoung Kim
393*2810ac1bSKiyoung Kim typedef struct {
394*2810ac1bSKiyoung Kim void *(*fn)(void *);
395*2810ac1bSKiyoung Kim void *arg;
396*2810ac1bSKiyoung Kim sigset_t sigbits;
397*2810ac1bSKiyoung Kim } psx_starter_t;
398*2810ac1bSKiyoung Kim
399*2810ac1bSKiyoung Kim /*
400*2810ac1bSKiyoung Kim * _psx_exiting is used to cleanup the node for the thread on its exit
401*2810ac1bSKiyoung Kim * path. This is needed for musl libc:
402*2810ac1bSKiyoung Kim *
403*2810ac1bSKiyoung Kim * https://bugzilla.kernel.org/show_bug.cgi?id=208477
404*2810ac1bSKiyoung Kim *
405*2810ac1bSKiyoung Kim * and likely wise for glibc too:
406*2810ac1bSKiyoung Kim *
407*2810ac1bSKiyoung Kim * https://sourceware.org/bugzilla/show_bug.cgi?id=12889
408*2810ac1bSKiyoung Kim */
_psx_exiting(void * node)409*2810ac1bSKiyoung Kim static void _psx_exiting(void *node) {
410*2810ac1bSKiyoung Kim /*
411*2810ac1bSKiyoung Kim * Until we are in the _PSX_EXITING state, we must not block the
412*2810ac1bSKiyoung Kim * psx_sig interrupt for this dying thread. That is, until this
413*2810ac1bSKiyoung Kim * exiting thread can set ref->gone to 1, this dying thread is
414*2810ac1bSKiyoung Kim * still participating in the psx syscall distribution.
415*2810ac1bSKiyoung Kim *
416*2810ac1bSKiyoung Kim * See https://github.com/golang/go/issues/42494 for a situation
417*2810ac1bSKiyoung Kim * where this code is called with psx_tracker.psx_sig blocked.
418*2810ac1bSKiyoung Kim */
419*2810ac1bSKiyoung Kim sigset_t sigbit, orig_sigbits;
420*2810ac1bSKiyoung Kim sigemptyset(&sigbit);
421*2810ac1bSKiyoung Kim pthread_sigmask(SIG_UNBLOCK, &sigbit, &orig_sigbits);
422*2810ac1bSKiyoung Kim sigaddset(&sigbit, psx_tracker.psx_sig);
423*2810ac1bSKiyoung Kim pthread_sigmask(SIG_UNBLOCK, &sigbit, NULL);
424*2810ac1bSKiyoung Kim
425*2810ac1bSKiyoung Kim /*
426*2810ac1bSKiyoung Kim * With psx_tracker.psx_sig unblocked we can wait until this
427*2810ac1bSKiyoung Kim * thread can enter the _PSX_EXITING state.
428*2810ac1bSKiyoung Kim */
429*2810ac1bSKiyoung Kim psx_new_state(_PSX_IDLE, _PSX_EXITING);
430*2810ac1bSKiyoung Kim
431*2810ac1bSKiyoung Kim /*
432*2810ac1bSKiyoung Kim * We now indicate that this thread is no longer participating in
433*2810ac1bSKiyoung Kim * the psx mechanism.
434*2810ac1bSKiyoung Kim */
435*2810ac1bSKiyoung Kim registered_thread_t *ref = node;
436*2810ac1bSKiyoung Kim pthread_mutex_lock(&ref->mu);
437*2810ac1bSKiyoung Kim ref->gone = 1;
438*2810ac1bSKiyoung Kim pthread_mutex_unlock(&ref->mu);
439*2810ac1bSKiyoung Kim
440*2810ac1bSKiyoung Kim /*
441*2810ac1bSKiyoung Kim * At this point, we can restore the calling sigmask to whatever
442*2810ac1bSKiyoung Kim * the caller thought was appropriate for a dying thread to have.
443*2810ac1bSKiyoung Kim */
444*2810ac1bSKiyoung Kim pthread_sigmask(SIG_SETMASK, &orig_sigbits, NULL);
445*2810ac1bSKiyoung Kim
446*2810ac1bSKiyoung Kim /*
447*2810ac1bSKiyoung Kim * Allow the rest of the psx system to carry on as per normal.
448*2810ac1bSKiyoung Kim */
449*2810ac1bSKiyoung Kim psx_new_state(_PSX_EXITING, _PSX_IDLE);
450*2810ac1bSKiyoung Kim }
451*2810ac1bSKiyoung Kim
452*2810ac1bSKiyoung Kim /*
453*2810ac1bSKiyoung Kim * _psx_start_fn is a trampoline for the intended start function, it
454*2810ac1bSKiyoung Kim * is called blocked (_PSX_CREATE), but releases the block before
455*2810ac1bSKiyoung Kim * calling starter->fn. Before releasing the block, the TLS specific
456*2810ac1bSKiyoung Kim * attributes are initialized for use by the interrupt handler under
457*2810ac1bSKiyoung Kim * the psx mutex, so it doesn't race with an interrupt received by
458*2810ac1bSKiyoung Kim * this thread and the interrupt handler does not need to poll for
459*2810ac1bSKiyoung Kim * that specific attribute to be present (which is problematic during
460*2810ac1bSKiyoung Kim * thread shutdown).
461*2810ac1bSKiyoung Kim */
_psx_start_fn(void * data)462*2810ac1bSKiyoung Kim static void *_psx_start_fn(void *data) {
463*2810ac1bSKiyoung Kim void *node = psx_do_registration();
464*2810ac1bSKiyoung Kim
465*2810ac1bSKiyoung Kim psx_new_state(_PSX_CREATE, _PSX_IDLE);
466*2810ac1bSKiyoung Kim
467*2810ac1bSKiyoung Kim psx_starter_t *starter = data;
468*2810ac1bSKiyoung Kim pthread_sigmask(SIG_SETMASK, &starter->sigbits, NULL);
469*2810ac1bSKiyoung Kim void *(*fn)(void *) = starter->fn;
470*2810ac1bSKiyoung Kim void *arg = starter->arg;
471*2810ac1bSKiyoung Kim
472*2810ac1bSKiyoung Kim memset(data, 0, sizeof(*starter));
473*2810ac1bSKiyoung Kim free(data);
474*2810ac1bSKiyoung Kim
475*2810ac1bSKiyoung Kim void *ret;
476*2810ac1bSKiyoung Kim
477*2810ac1bSKiyoung Kim pthread_cleanup_push(_psx_exiting, node);
478*2810ac1bSKiyoung Kim ret = fn(arg);
479*2810ac1bSKiyoung Kim pthread_cleanup_pop(1);
480*2810ac1bSKiyoung Kim
481*2810ac1bSKiyoung Kim return ret;
482*2810ac1bSKiyoung Kim }
483*2810ac1bSKiyoung Kim
484*2810ac1bSKiyoung Kim /*
485*2810ac1bSKiyoung Kim * __wrap_pthread_create is the wrapped destination of all regular
486*2810ac1bSKiyoung Kim * pthread_create calls.
487*2810ac1bSKiyoung Kim */
__wrap_pthread_create(pthread_t * thread,const pthread_attr_t * attr,void * (* start_routine)(void *),void * arg)488*2810ac1bSKiyoung Kim int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
489*2810ac1bSKiyoung Kim void *(*start_routine) (void *), void *arg) {
490*2810ac1bSKiyoung Kim psx_starter_t *starter = calloc(1, sizeof(psx_starter_t));
491*2810ac1bSKiyoung Kim if (starter == NULL) {
492*2810ac1bSKiyoung Kim perror("failed at thread creation");
493*2810ac1bSKiyoung Kim exit(1);
494*2810ac1bSKiyoung Kim }
495*2810ac1bSKiyoung Kim starter->fn = start_routine;
496*2810ac1bSKiyoung Kim starter->arg = arg;
497*2810ac1bSKiyoung Kim /*
498*2810ac1bSKiyoung Kim * Until we are in the _PSX_IDLE state and locked, we must not
499*2810ac1bSKiyoung Kim * block the psx_sig interrupt for this parent thread. Arrange
500*2810ac1bSKiyoung Kim * that parent thread and newly created one can restore signal
501*2810ac1bSKiyoung Kim * mask.
502*2810ac1bSKiyoung Kim */
503*2810ac1bSKiyoung Kim sigset_t sigbit, orig_sigbits;
504*2810ac1bSKiyoung Kim sigemptyset(&sigbit);
505*2810ac1bSKiyoung Kim pthread_sigmask(SIG_UNBLOCK, &sigbit, &starter->sigbits);
506*2810ac1bSKiyoung Kim sigaddset(&sigbit, psx_tracker.psx_sig);
507*2810ac1bSKiyoung Kim pthread_sigmask(SIG_UNBLOCK, &sigbit, &orig_sigbits);
508*2810ac1bSKiyoung Kim
509*2810ac1bSKiyoung Kim psx_new_state(_PSX_IDLE, _PSX_CREATE);
510*2810ac1bSKiyoung Kim
511*2810ac1bSKiyoung Kim /*
512*2810ac1bSKiyoung Kim * until the child thread has been blessed with its own TLS
513*2810ac1bSKiyoung Kim * specific attribute(s) we prevent either the parent thread or
514*2810ac1bSKiyoung Kim * the new one from experiencing a PSX interrupt.
515*2810ac1bSKiyoung Kim */
516*2810ac1bSKiyoung Kim pthread_sigmask(SIG_BLOCK, &sigbit, NULL);
517*2810ac1bSKiyoung Kim
518*2810ac1bSKiyoung Kim int ret = __real_pthread_create(thread, attr, _psx_start_fn, starter);
519*2810ac1bSKiyoung Kim if (ret > 0) {
520*2810ac1bSKiyoung Kim psx_new_state(_PSX_CREATE, _PSX_IDLE);
521*2810ac1bSKiyoung Kim memset(starter, 0, sizeof(*starter));
522*2810ac1bSKiyoung Kim free(starter);
523*2810ac1bSKiyoung Kim } /* else unlock happens in _psx_start_fn */
524*2810ac1bSKiyoung Kim
525*2810ac1bSKiyoung Kim /* the parent can once again receive psx interrupt signals */
526*2810ac1bSKiyoung Kim pthread_sigmask(SIG_SETMASK, &orig_sigbits, NULL);
527*2810ac1bSKiyoung Kim
528*2810ac1bSKiyoung Kim return ret;
529*2810ac1bSKiyoung Kim }
530*2810ac1bSKiyoung Kim
531*2810ac1bSKiyoung Kim /*
532*2810ac1bSKiyoung Kim * __psx_immediate_syscall does one syscall using the current
533*2810ac1bSKiyoung Kim * process.
534*2810ac1bSKiyoung Kim */
__psx_immediate_syscall(long int syscall_nr,int count,long int * arg)535*2810ac1bSKiyoung Kim static long int __psx_immediate_syscall(long int syscall_nr,
536*2810ac1bSKiyoung Kim int count, long int *arg) {
537*2810ac1bSKiyoung Kim psx_tracker.cmd.syscall_nr = syscall_nr;
538*2810ac1bSKiyoung Kim psx_tracker.cmd.arg1 = count > 0 ? arg[0] : 0;
539*2810ac1bSKiyoung Kim psx_tracker.cmd.arg2 = count > 1 ? arg[1] : 0;
540*2810ac1bSKiyoung Kim psx_tracker.cmd.arg3 = count > 2 ? arg[2] : 0;
541*2810ac1bSKiyoung Kim
542*2810ac1bSKiyoung Kim if (count > 3) {
543*2810ac1bSKiyoung Kim psx_tracker.cmd.six = 1;
544*2810ac1bSKiyoung Kim psx_tracker.cmd.arg4 = arg[3];
545*2810ac1bSKiyoung Kim psx_tracker.cmd.arg5 = count > 4 ? arg[4] : 0;
546*2810ac1bSKiyoung Kim psx_tracker.cmd.arg6 = count > 5 ? arg[5] : 0;
547*2810ac1bSKiyoung Kim return syscall(syscall_nr,
548*2810ac1bSKiyoung Kim psx_tracker.cmd.arg1,
549*2810ac1bSKiyoung Kim psx_tracker.cmd.arg2,
550*2810ac1bSKiyoung Kim psx_tracker.cmd.arg3,
551*2810ac1bSKiyoung Kim psx_tracker.cmd.arg4,
552*2810ac1bSKiyoung Kim psx_tracker.cmd.arg5,
553*2810ac1bSKiyoung Kim psx_tracker.cmd.arg6);
554*2810ac1bSKiyoung Kim }
555*2810ac1bSKiyoung Kim
556*2810ac1bSKiyoung Kim psx_tracker.cmd.six = 0;
557*2810ac1bSKiyoung Kim return syscall(syscall_nr, psx_tracker.cmd.arg1,
558*2810ac1bSKiyoung Kim psx_tracker.cmd.arg2, psx_tracker.cmd.arg3);
559*2810ac1bSKiyoung Kim }
560*2810ac1bSKiyoung Kim
561*2810ac1bSKiyoung Kim /*
562*2810ac1bSKiyoung Kim * __psx_syscall performs the syscall on the current thread and if no
563*2810ac1bSKiyoung Kim * error is detected it ensures that the syscall is also performed on
564*2810ac1bSKiyoung Kim * all (other) registered threads. The return code is the value for
565*2810ac1bSKiyoung Kim * the first invocation. It uses a trick to figure out how many
566*2810ac1bSKiyoung Kim * arguments the user has supplied. The other half of the trick is
567*2810ac1bSKiyoung Kim * provided by the macro psx_syscall() in the <sys/psx_syscall.h>
568*2810ac1bSKiyoung Kim * file. The trick is the 7th optional argument (8th over all) to
569*2810ac1bSKiyoung Kim * __psx_syscall is the count of arguments supplied to psx_syscall.
570*2810ac1bSKiyoung Kim *
571*2810ac1bSKiyoung Kim * User:
572*2810ac1bSKiyoung Kim * psx_syscall(nr, a, b);
573*2810ac1bSKiyoung Kim * Expanded by macro to:
574*2810ac1bSKiyoung Kim * __psx_syscall(nr, a, b, 6, 5, 4, 3, 2, 1, 0);
575*2810ac1bSKiyoung Kim * The eighth arg is now ------------------------------------^
576*2810ac1bSKiyoung Kim */
__psx_syscall(long int syscall_nr,...)577*2810ac1bSKiyoung Kim long int __psx_syscall(long int syscall_nr, ...) {
578*2810ac1bSKiyoung Kim long int arg[7];
579*2810ac1bSKiyoung Kim int i;
580*2810ac1bSKiyoung Kim
581*2810ac1bSKiyoung Kim va_list aptr;
582*2810ac1bSKiyoung Kim va_start(aptr, syscall_nr);
583*2810ac1bSKiyoung Kim for (i = 0; i < 7; i++) {
584*2810ac1bSKiyoung Kim arg[i] = va_arg(aptr, long int);
585*2810ac1bSKiyoung Kim }
586*2810ac1bSKiyoung Kim va_end(aptr);
587*2810ac1bSKiyoung Kim
588*2810ac1bSKiyoung Kim int count = arg[6];
589*2810ac1bSKiyoung Kim if (count < 0 || count > 6) {
590*2810ac1bSKiyoung Kim errno = EINVAL;
591*2810ac1bSKiyoung Kim return -1;
592*2810ac1bSKiyoung Kim }
593*2810ac1bSKiyoung Kim
594*2810ac1bSKiyoung Kim if (psx_tracker.has_forked) {
595*2810ac1bSKiyoung Kim return __psx_immediate_syscall(syscall_nr, count, arg);
596*2810ac1bSKiyoung Kim }
597*2810ac1bSKiyoung Kim
598*2810ac1bSKiyoung Kim psx_new_state(_PSX_IDLE, _PSX_SETUP);
599*2810ac1bSKiyoung Kim psx_confirm_sigaction();
600*2810ac1bSKiyoung Kim
601*2810ac1bSKiyoung Kim long int ret;
602*2810ac1bSKiyoung Kim
603*2810ac1bSKiyoung Kim ret = __psx_immediate_syscall(syscall_nr, count, arg);
604*2810ac1bSKiyoung Kim if (ret == -1 || !psx_tracker.initialized) {
605*2810ac1bSKiyoung Kim psx_new_state(_PSX_SETUP, _PSX_IDLE);
606*2810ac1bSKiyoung Kim goto defer;
607*2810ac1bSKiyoung Kim }
608*2810ac1bSKiyoung Kim
609*2810ac1bSKiyoung Kim int restore_errno = errno;
610*2810ac1bSKiyoung Kim
611*2810ac1bSKiyoung Kim psx_new_state(_PSX_SETUP, _PSX_SYSCALL);
612*2810ac1bSKiyoung Kim psx_tracker.cmd.active = 1;
613*2810ac1bSKiyoung Kim
614*2810ac1bSKiyoung Kim pthread_t self = pthread_self();
615*2810ac1bSKiyoung Kim registered_thread_t *next = NULL, *ref;
616*2810ac1bSKiyoung Kim
617*2810ac1bSKiyoung Kim psx_lock();
618*2810ac1bSKiyoung Kim for (ref = psx_tracker.root; ref; ref = next) {
619*2810ac1bSKiyoung Kim next = ref->next;
620*2810ac1bSKiyoung Kim if (ref->thread == self) {
621*2810ac1bSKiyoung Kim continue;
622*2810ac1bSKiyoung Kim }
623*2810ac1bSKiyoung Kim pthread_mutex_lock(&ref->mu);
624*2810ac1bSKiyoung Kim ref->pending = 1;
625*2810ac1bSKiyoung Kim int gone = ref->gone;
626*2810ac1bSKiyoung Kim if (!gone) {
627*2810ac1bSKiyoung Kim gone = pthread_kill(ref->thread, psx_tracker.psx_sig) != 0;
628*2810ac1bSKiyoung Kim }
629*2810ac1bSKiyoung Kim pthread_mutex_unlock(&ref->mu);
630*2810ac1bSKiyoung Kim if (!gone) {
631*2810ac1bSKiyoung Kim continue;
632*2810ac1bSKiyoung Kim }
633*2810ac1bSKiyoung Kim /*
634*2810ac1bSKiyoung Kim * need to remove invalid thread id from linked list
635*2810ac1bSKiyoung Kim */
636*2810ac1bSKiyoung Kim psx_do_unregister(ref);
637*2810ac1bSKiyoung Kim }
638*2810ac1bSKiyoung Kim psx_unlock();
639*2810ac1bSKiyoung Kim
640*2810ac1bSKiyoung Kim int mismatch = 0;
641*2810ac1bSKiyoung Kim for (;;) {
642*2810ac1bSKiyoung Kim int waiting = 0;
643*2810ac1bSKiyoung Kim psx_lock();
644*2810ac1bSKiyoung Kim for (ref = psx_tracker.root; ref; ref = next) {
645*2810ac1bSKiyoung Kim next = ref->next;
646*2810ac1bSKiyoung Kim if (ref->thread == self) {
647*2810ac1bSKiyoung Kim continue;
648*2810ac1bSKiyoung Kim }
649*2810ac1bSKiyoung Kim
650*2810ac1bSKiyoung Kim pthread_mutex_lock(&ref->mu);
651*2810ac1bSKiyoung Kim int pending = ref->pending;
652*2810ac1bSKiyoung Kim int gone = ref->gone;
653*2810ac1bSKiyoung Kim if (!gone) {
654*2810ac1bSKiyoung Kim if (pending) {
655*2810ac1bSKiyoung Kim gone = (pthread_kill(ref->thread, 0) != 0);
656*2810ac1bSKiyoung Kim } else {
657*2810ac1bSKiyoung Kim mismatch |= (ref->retval != ret);
658*2810ac1bSKiyoung Kim }
659*2810ac1bSKiyoung Kim }
660*2810ac1bSKiyoung Kim pthread_mutex_unlock(&ref->mu);
661*2810ac1bSKiyoung Kim if (!gone) {
662*2810ac1bSKiyoung Kim waiting += pending;
663*2810ac1bSKiyoung Kim continue;
664*2810ac1bSKiyoung Kim }
665*2810ac1bSKiyoung Kim /*
666*2810ac1bSKiyoung Kim * need to remove invalid thread id from linked list
667*2810ac1bSKiyoung Kim */
668*2810ac1bSKiyoung Kim psx_do_unregister(ref);
669*2810ac1bSKiyoung Kim }
670*2810ac1bSKiyoung Kim psx_unlock();
671*2810ac1bSKiyoung Kim if (!waiting) {
672*2810ac1bSKiyoung Kim break;
673*2810ac1bSKiyoung Kim }
674*2810ac1bSKiyoung Kim sched_yield();
675*2810ac1bSKiyoung Kim }
676*2810ac1bSKiyoung Kim
677*2810ac1bSKiyoung Kim psx_tracker.cmd.active = 0;
678*2810ac1bSKiyoung Kim if (mismatch) {
679*2810ac1bSKiyoung Kim psx_lock();
680*2810ac1bSKiyoung Kim switch (psx_tracker.sensitivity) {
681*2810ac1bSKiyoung Kim case PSX_IGNORE:
682*2810ac1bSKiyoung Kim break;
683*2810ac1bSKiyoung Kim default:
684*2810ac1bSKiyoung Kim fprintf(stderr, "psx_syscall result differs.\n");
685*2810ac1bSKiyoung Kim if (psx_tracker.cmd.six) {
686*2810ac1bSKiyoung Kim fprintf(stderr, "trap:%ld a123456=[%ld,%ld,%ld,%ld,%ld,%ld]\n",
687*2810ac1bSKiyoung Kim psx_tracker.cmd.syscall_nr,
688*2810ac1bSKiyoung Kim psx_tracker.cmd.arg1,
689*2810ac1bSKiyoung Kim psx_tracker.cmd.arg2,
690*2810ac1bSKiyoung Kim psx_tracker.cmd.arg3,
691*2810ac1bSKiyoung Kim psx_tracker.cmd.arg4,
692*2810ac1bSKiyoung Kim psx_tracker.cmd.arg5,
693*2810ac1bSKiyoung Kim psx_tracker.cmd.arg6);
694*2810ac1bSKiyoung Kim } else {
695*2810ac1bSKiyoung Kim fprintf(stderr, "trap:%ld a123=[%ld,%ld,%ld]\n",
696*2810ac1bSKiyoung Kim psx_tracker.cmd.syscall_nr,
697*2810ac1bSKiyoung Kim psx_tracker.cmd.arg1,
698*2810ac1bSKiyoung Kim psx_tracker.cmd.arg2,
699*2810ac1bSKiyoung Kim psx_tracker.cmd.arg3);
700*2810ac1bSKiyoung Kim }
701*2810ac1bSKiyoung Kim fprintf(stderr, "results:");
702*2810ac1bSKiyoung Kim for (ref = psx_tracker.root; ref; ref = next) {
703*2810ac1bSKiyoung Kim next = ref->next;
704*2810ac1bSKiyoung Kim if (ref->thread == self) {
705*2810ac1bSKiyoung Kim continue;
706*2810ac1bSKiyoung Kim }
707*2810ac1bSKiyoung Kim if (ret != ref->retval) {
708*2810ac1bSKiyoung Kim fprintf(stderr, " %d={%ld}", ref->tid, ref->retval);
709*2810ac1bSKiyoung Kim }
710*2810ac1bSKiyoung Kim }
711*2810ac1bSKiyoung Kim fprintf(stderr, " wanted={%ld}\n", ret);
712*2810ac1bSKiyoung Kim if (psx_tracker.sensitivity == PSX_WARNING) {
713*2810ac1bSKiyoung Kim break;
714*2810ac1bSKiyoung Kim }
715*2810ac1bSKiyoung Kim pthread_kill(self, SIGSYS);
716*2810ac1bSKiyoung Kim }
717*2810ac1bSKiyoung Kim psx_unlock();
718*2810ac1bSKiyoung Kim }
719*2810ac1bSKiyoung Kim errno = restore_errno;
720*2810ac1bSKiyoung Kim psx_new_state(_PSX_SYSCALL, _PSX_IDLE);
721*2810ac1bSKiyoung Kim
722*2810ac1bSKiyoung Kim defer:
723*2810ac1bSKiyoung Kim return ret;
724*2810ac1bSKiyoung Kim }
725*2810ac1bSKiyoung Kim
726*2810ac1bSKiyoung Kim /*
727*2810ac1bSKiyoung Kim * _psx_cleanup its called when the program exits. It is used to free
728*2810ac1bSKiyoung Kim * any memory used by the thread tracker.
729*2810ac1bSKiyoung Kim */
_psx_cleanup(void)730*2810ac1bSKiyoung Kim static void _psx_cleanup(void) {
731*2810ac1bSKiyoung Kim registered_thread_t *ref, *next;
732*2810ac1bSKiyoung Kim
733*2810ac1bSKiyoung Kim /*
734*2810ac1bSKiyoung Kim * We enter the exiting state. Unlike exiting a single thread we
735*2810ac1bSKiyoung Kim * never leave this state since this cleanup is only done at
736*2810ac1bSKiyoung Kim * program exit.
737*2810ac1bSKiyoung Kim */
738*2810ac1bSKiyoung Kim psx_lock();
739*2810ac1bSKiyoung Kim while (psx_tracker.state != _PSX_IDLE && psx_tracker.state != _PSX_INFORK) {
740*2810ac1bSKiyoung Kim pthread_cond_wait(&psx_tracker.cond, &psx_tracker.state_mu);
741*2810ac1bSKiyoung Kim }
742*2810ac1bSKiyoung Kim psx_tracker.state = _PSX_EXITING;
743*2810ac1bSKiyoung Kim psx_unlock();
744*2810ac1bSKiyoung Kim
745*2810ac1bSKiyoung Kim for (ref = psx_tracker.root; ref; ref = next) {
746*2810ac1bSKiyoung Kim next = ref->next;
747*2810ac1bSKiyoung Kim psx_do_unregister(ref);
748*2810ac1bSKiyoung Kim }
749*2810ac1bSKiyoung Kim }
750*2810ac1bSKiyoung Kim
751*2810ac1bSKiyoung Kim /*
752*2810ac1bSKiyoung Kim * Change the PSX sensitivity level. If the threads appear to have
753*2810ac1bSKiyoung Kim * diverged in behavior, this can cause the library to notify the
754*2810ac1bSKiyoung Kim * user.
755*2810ac1bSKiyoung Kim */
psx_set_sensitivity(psx_sensitivity_t level)756*2810ac1bSKiyoung Kim int psx_set_sensitivity(psx_sensitivity_t level) {
757*2810ac1bSKiyoung Kim if (level < PSX_IGNORE || level > PSX_ERROR) {
758*2810ac1bSKiyoung Kim errno = EINVAL;
759*2810ac1bSKiyoung Kim return -1;
760*2810ac1bSKiyoung Kim }
761*2810ac1bSKiyoung Kim psx_lock();
762*2810ac1bSKiyoung Kim psx_tracker.sensitivity = level;
763*2810ac1bSKiyoung Kim psx_unlock();
764*2810ac1bSKiyoung Kim return 0;
765*2810ac1bSKiyoung Kim }
766