1#include <pthread.h>
2#include <signal.h>
3#include <setjmp.h>
4#include <errno.h>
5#include <assert.h>
6
7static sigjmp_buf env;
8
9/*
10 * Starting with glibc 2.20 some pthread calls may execute
11 * an xend instruction unconditionally when a lock is used in
12 * a way that is invalid so defined a sigill handler that can
13 * convert these invalid instructions to a normal error.
14 */
15static void sigill_handler( int signum, siginfo_t *siginfo, void *sigcontext ) {
16   unsigned char *pc = siginfo->si_addr;
17   assert( pc[0] == 0x0f && pc[1] == 0x01 && pc[2] == 0xd5 );
18   siglongjmp( env, EPERM );
19}
20
21/*
22 * Same as above, but in case we do recognize the xend,
23 * but detect it is invalid (used outside a transaction)
24 * and generate a segv.  Unfortunately then si_addr is,
25 * just zero, so we cannot add an assert/sanity check.
26 */
27static void segv_handler( int signum, siginfo_t *siginfo, void *sigcontext ) {
28   siglongjmp( env, EPERM );
29}
30
31/*
32 * Wrapper for pthread_rwlock_unlock which may execute xend
33 * unconditionally when used on a lock that is not locked.
34 *
35 * Note that we return 0 instead of EPERM because that is what
36 * glibc normally does - error reporting is optional.
37 */
38static int safe_pthread_rwlock_unlock( pthread_rwlock_t *rwlock ) {
39   struct sigaction sa_ill, sa_segv;
40   struct sigaction oldsa_ill, oldsa_segv;
41   int r;
42
43   sa_ill.sa_handler = NULL;
44   sa_ill.sa_sigaction = sigill_handler;
45   sigemptyset( &sa_ill.sa_mask );
46   sa_ill.sa_flags = SA_SIGINFO;
47
48   sigaction( SIGILL, &sa_ill, &oldsa_ill );
49
50   sa_segv.sa_handler = NULL;
51   sa_segv.sa_sigaction = segv_handler;
52   sigemptyset( &sa_segv.sa_mask );
53   sa_segv.sa_flags = SA_SIGINFO;
54
55   sigaction( SIGSEGV, &sa_segv, &oldsa_segv );
56
57   if ( ( r = sigsetjmp( env, 1 ) ) == 0 ) {
58     r = pthread_rwlock_unlock( rwlock );
59   } else {
60     r = 0;
61   }
62
63   sigaction( SIGILL, &oldsa_ill, NULL );
64   sigaction( SIGSEGV, &oldsa_segv, NULL );
65
66   return r;
67}
68
69#define pthread_rwlock_unlock safe_pthread_rwlock_unlock
70