xref: /aosp_15_r20/external/libcap/psx/psx.c (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
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