Home | History | Annotate | Download | only in tests
      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  */
     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  */
     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  */
     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