#include <pthread.h> #include <signal.h> #include <setjmp.h> #include <errno.h> #include <assert.h> static sigjmp_buf env; /* * Starting with glibc 2.20 some pthread calls may execute * an xend instruction unconditionally when a lock is used in * a way that is invalid so defined a sigill handler that can * convert these invalid instructions to a normal error. */ static void sigill_handler( int signum, siginfo_t *siginfo, void *sigcontext ) { unsigned char *pc = siginfo->si_addr; assert( pc[0] == 0x0f && pc[1] == 0x01 && pc[2] == 0xd5 ); siglongjmp( env, EPERM ); } /* * Same as above, but in case we do recognize the xend, * but detect it is invalid (used outside a transaction) * and generate a segv. Unfortunately then si_addr is, * just zero, so we cannot add an assert/sanity check. */ static void segv_handler( int signum, siginfo_t *siginfo, void *sigcontext ) { siglongjmp( env, EPERM ); } /* * Wrapper for pthread_rwlock_unlock which may execute xend * unconditionally when used on a lock that is not locked. * * Note that we return 0 instead of EPERM because that is what * glibc normally does - error reporting is optional. */ static int safe_pthread_rwlock_unlock( pthread_rwlock_t *rwlock ) { struct sigaction sa_ill, sa_segv; struct sigaction oldsa_ill, oldsa_segv; int r; sa_ill.sa_handler = NULL; sa_ill.sa_sigaction = sigill_handler; sigemptyset( &sa_ill.sa_mask ); sa_ill.sa_flags = SA_SIGINFO; sigaction( SIGILL, &sa_ill, &oldsa_ill ); sa_segv.sa_handler = NULL; sa_segv.sa_sigaction = segv_handler; sigemptyset( &sa_segv.sa_mask ); sa_segv.sa_flags = SA_SIGINFO; sigaction( SIGSEGV, &sa_segv, &oldsa_segv ); if ( ( r = sigsetjmp( env, 1 ) ) == 0 ) { r = pthread_rwlock_unlock( rwlock ); } else { r = 0; } sigaction( SIGILL, &oldsa_ill, NULL ); sigaction( SIGSEGV, &oldsa_segv, NULL ); return r; } #define pthread_rwlock_unlock safe_pthread_rwlock_unlock