diff options
Diffstat (limited to 'os')
-rw-r--r-- | os/Makefile.am | 5 | ||||
-rw-r--r-- | os/WaitFor.c | 5 | ||||
-rw-r--r-- | os/busfault.c | 149 | ||||
-rw-r--r-- | os/osinit.c | 5 |
4 files changed, 164 insertions, 0 deletions
diff --git a/os/Makefile.am b/os/Makefile.am index 364b6da2d..a1bbb4d1e 100644 --- a/os/Makefile.am +++ b/os/Makefile.am @@ -5,6 +5,7 @@ AM_CFLAGS = $(DIX_CFLAGS) $(SHA1_CFLAGS) SECURERPC_SRCS = rpcauth.c XDMCP_SRCS = xdmcp.c XORG_SRCS = log.c +BUSFAULT_SRCS = busfault.c libos_la_SOURCES = \ WaitFor.c \ @@ -39,6 +40,10 @@ AM_CFLAGS += $(LIBUNWIND_CFLAGS) libos_la_LIBADD += $(LIBUNWIND_LIBS) endif +if BUSFAULT +libos_la_SOURCES += $(BUSFAULT_SRCS) +endif + EXTRA_DIST = $(SECURERPC_SRCS) $(XDMCP_SRCS) if SPECIAL_DTRACE_OBJECTS diff --git a/os/WaitFor.c b/os/WaitFor.c index c5f4cd78b..39fedd9a2 100644 --- a/os/WaitFor.c +++ b/os/WaitFor.c @@ -72,6 +72,7 @@ SOFTWARE. #ifdef DPMSExtension #include "dpmsproc.h" #endif +#include "busfault.h" #ifdef WIN32 /* Error codes from windows sockets differ from fileio error codes */ @@ -162,6 +163,10 @@ WaitForSomething(int *pClientsReady) SmartScheduleStopTimer(); nready = 0; +#ifdef BUSFAULT + busfault_check(); +#endif + /* We need a while loop here to handle crashed connections and the screen saver timeout */ while (1) { diff --git a/os/busfault.c b/os/busfault.c new file mode 100644 index 000000000..78e7693e3 --- /dev/null +++ b/os/busfault.c @@ -0,0 +1,149 @@ +/* + * Copyright © 2013 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifdef HAVE_DIX_CONFIG_H +#include <dix-config.h> +#endif + +#include <X11/Xos.h> +#include <X11/Xdefs.h> +#include "misc.h" +#include <busfault.h> +#include <list.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdint.h> +#include <sys/mman.h> +#include <signal.h> + +struct busfault { + struct xorg_list list; + + void *addr; + size_t size; + + Bool valid; + + busfault_notify_ptr notify; + void *context; +}; + +static Bool busfaulted; +static struct xorg_list busfaults; + +struct busfault * +busfault_register_mmap(void *addr, size_t size, busfault_notify_ptr notify, void *context) +{ + struct busfault *busfault; + + busfault = calloc(1, sizeof (struct busfault)); + if (!busfault) + return NULL; + + busfault->addr = addr; + busfault->size = size; + busfault->notify = notify; + busfault->context = context; + busfault->valid = TRUE; + + xorg_list_add(&busfault->list, &busfaults); + return busfault; +} + +void +busfault_unregister(struct busfault *busfault) +{ + xorg_list_del(&busfault->list); + free(busfault); +} + +void +busfault_check(void) +{ + struct busfault *busfault, *tmp; + + if (!busfaulted) + return; + + busfaulted = FALSE; + + xorg_list_for_each_entry_safe(busfault, tmp, &busfaults, list) { + if (!busfault->valid) + (*busfault->notify)(busfault->context); + } +} + +static void (*previous_busfault_sigaction)(int sig, siginfo_t *info, void *param); + +static void +busfault_sigaction(int sig, siginfo_t *info, void *param) +{ + void *fault = info->si_addr; + struct busfault *busfault = NULL; + void *new_addr; + + /* Locate the faulting address in our list of shared segments + */ + xorg_list_for_each_entry(busfault, &busfaults, list) { + if ((char *) busfault->addr <= (char *) fault && (char *) fault < (char *) busfault->addr + busfault->size) { + break; + } + } + if (!busfault) + goto panic; + + if (!busfault->valid) + goto panic; + + busfault->valid = FALSE; + busfaulted = TRUE; + + /* The client truncated the file; unmap the shared file, map + * /dev/zero over that area and keep going + */ + + new_addr = mmap(busfault->addr, busfault->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0); + + if (new_addr == MAP_FAILED) + goto panic; + + return; +panic: + if (previous_busfault_sigaction) + (*previous_busfault_sigaction)(sig, info, param); + else + FatalError("bus error"); +} + +Bool +busfault_init(void) +{ + struct sigaction act, old_act; + + act.sa_sigaction = busfault_sigaction; + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGBUS, &act, &old_act) < 0) + return FALSE; + previous_busfault_sigaction = old_act.sa_sigaction; + xorg_list_init(&busfaults); + return TRUE; +} diff --git a/os/osinit.c b/os/osinit.c index 6c66f9c12..60d10694b 100644 --- a/os/osinit.c +++ b/os/osinit.c @@ -149,6 +149,8 @@ OsSigHandler(int signo) } #endif /* !WIN32 || __CYGWIN__ */ +#include "busfault.h" + void OsInit(void) { @@ -185,6 +187,9 @@ OsInit(void) } } #endif /* !WIN32 || __CYGWIN__ */ +#ifdef BUSFAULT + busfault_init(); +#endif #ifdef HAVE_BACKTRACE /* |