#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