• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <pthread.h>
2 #include <signal.h>
3 #include <setjmp.h>
4 #include <errno.h>
5 #include <assert.h>
6 
7 static 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  */
sigill_handler(int signum,siginfo_t * siginfo,void * sigcontext)15 static 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  */
segv_handler(int signum,siginfo_t * siginfo,void * sigcontext)27 static 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  */
safe_pthread_rwlock_unlock(pthread_rwlock_t * rwlock)38 static 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