diff --git a/os/busfault.c b/os/busfault.c index e18e2bc48..d4f2e98a4 100644 --- a/os/busfault.c +++ b/os/busfault.c @@ -35,6 +35,7 @@ #include "os/busfault.h" #include +#include struct busfault { struct xorg_list list; @@ -46,6 +47,8 @@ struct busfault { busfault_notify_ptr notify; void *context; + + sigjmp_buf *jmp; }; static Bool busfaulted; @@ -65,6 +68,7 @@ busfault_register_mmap(void *addr, size_t size, busfault_notify_ptr notify, void busfault->notify = notify; busfault->context = context; busfault->valid = TRUE; + busfault->jmp = NULL; xorg_list_add(&busfault->list, &busfaults); return busfault; @@ -77,6 +81,13 @@ busfault_unregister(struct busfault *busfault) free(busfault); } +void +busfault_set_jmp(struct busfault *busfault, sigjmp_buf *jmp) +{ + if (busfault) + busfault->jmp = jmp; +} + void busfault_check(void) { @@ -101,6 +112,7 @@ busfault_sigaction(int sig, siginfo_t *info, void *param) void *fault = info->si_addr; struct busfault *iter, *busfault = NULL; void *new_addr; + sigjmp_buf *jmp; /* Locate the faulting address in our list of shared segments */ @@ -119,6 +131,12 @@ busfault_sigaction(int sig, siginfo_t *info, void *param) busfault->valid = FALSE; busfaulted = TRUE; + jmp = busfault->jmp; + if (jmp) { + busfault->jmp = NULL; + siglongjmp(*jmp, 1); + } + /* The client truncated the file; unmap the shared file, map * /dev/zero over that area and keep going */ diff --git a/os/busfault.h b/os/busfault.h index f5a0d35f4..2dfc7552b 100644 --- a/os/busfault.h +++ b/os/busfault.h @@ -30,6 +30,7 @@ typedef void (*busfault_notify_ptr) (void *context); #ifdef HAVE_SIGACTION #include +#include struct busfault * busfault_register_mmap(void *addr, size_t size, busfault_notify_ptr notify, void *context); @@ -37,6 +38,15 @@ busfault_register_mmap(void *addr, size_t size, busfault_notify_ptr notify, void void busfault_unregister(struct busfault *busfault); +/* + * If jmp is non-NULL when a SIGBUS hits this mapping, the handler + * siglongjmps there instead of remapping and retrying the faulting + * instruction. Callers must use sigsetjmp(..., 1). + * Pass NULL to clear. + */ +void +busfault_set_jmp(struct busfault *busfault, sigjmp_buf *jmp); + void busfault_check(void);