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