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