diff --git a/Xext/shm.c b/Xext/shm.c index 109a381a0..46ce521af 100644 --- a/Xext/shm.c +++ b/Xext/shm.c @@ -438,9 +438,11 @@ ShmDetachSegment(pointer value, /* must conform to DeleteType */ if (--shmdesc->refcnt) return TRUE; #if SHM_FD_PASSING - if (shmdesc->is_fd) + if (shmdesc->is_fd) { + if (shmdesc->busfault) + busfault_unregister(shmdesc->busfault); munmap(shmdesc->addr, shmdesc->size); - else + } else #endif shmdt(shmdesc->addr); for (prev = &Shmsegs; *prev != shmdesc; prev = &(*prev)->next); @@ -1099,6 +1101,19 @@ ProcShmCreatePixmap(ClientPtr client) } #ifdef SHM_FD_PASSING + +static void +ShmBusfaultNotify(void *context) +{ + ShmDescPtr shmdesc = context; + + ErrorF("shared memory 0x%x truncated by client\n", + (unsigned int) shmdesc->resource); + busfault_unregister(shmdesc->busfault); + shmdesc->busfault = NULL; + FreeResource (shmdesc->resource, RT_NONE); +} + static int ProcShmAttachFd(ClientPtr client) { @@ -1143,6 +1158,15 @@ ProcShmAttachFd(ClientPtr client) shmdesc->refcnt = 1; shmdesc->writable = !stuff->readOnly; shmdesc->size = statb.st_size; + shmdesc->resource = stuff->shmseg; + + shmdesc->busfault = busfault_register_mmap(shmdesc->addr, shmdesc->size, ShmBusfaultNotify, shmdesc); + if (!shmdesc->busfault) { + munmap(shmdesc->addr, shmdesc->size); + free(shmdesc); + return BadAlloc; + } + shmdesc->next = Shmsegs; Shmsegs = shmdesc; @@ -1198,6 +1222,15 @@ ProcShmCreateSegment(ClientPtr client) shmdesc->refcnt = 1; shmdesc->writable = !stuff->readOnly; shmdesc->size = stuff->size; + + shmdesc->busfault = busfault_register_mmap(shmdesc->addr, shmdesc->size, ShmBusfaultNotify, shmdesc); + if (!shmdesc->busfault) { + close(fd); + munmap(shmdesc->addr, shmdesc->size); + free(shmdesc); + return BadAlloc; + } + shmdesc->next = Shmsegs; Shmsegs = shmdesc; diff --git a/Xext/shmint.h b/Xext/shmint.h index 7b3ea9bab..21d6cc4ee 100644 --- a/Xext/shmint.h +++ b/Xext/shmint.h @@ -62,6 +62,10 @@ typedef struct _ShmFuncs { #define SHM_FD_PASSING 1 #endif +#ifdef SHM_FD_PASSING +#include "busfault.h" +#endif + typedef struct _ShmDesc { struct _ShmDesc *next; int shmid; @@ -71,6 +75,7 @@ typedef struct _ShmDesc { unsigned long size; #ifdef SHM_FD_PASSING Bool is_fd; + struct busfault *busfault; XID resource; #endif } ShmDescRec, *ShmDescPtr; diff --git a/configure.ac b/configure.ac index 04de2cd63..01f2a2efe 100644 --- a/configure.ac +++ b/configure.ac @@ -1171,6 +1171,25 @@ case "$DRI3,$HAVE_DRI3PROTO" in ;; esac +AC_CHECK_FUNCS([sigaction]) + +BUSFAULT=no + +case x"$ac_cv_func_sigaction" in + xyes) + AC_DEFINE(HAVE_SIGACTION, 1, [Have sigaction function]) + BUSFAULT=yes + ;; +esac + +case x"$BUSFAULT" in + xyes) + AC_DEFINE(BUSFAULT, 1, [Include busfault OS API]) + ;; +esac + +AM_CONDITIONAL(BUSFAULT, test x"$BUSFAULT" = xyes) + PKG_CHECK_MODULES([XSHMFENCE], $XSHMFENCE, [HAVE_XSHMFENCE=yes], [HAVE_XSHMFENCE=no]) diff --git a/include/Makefile.am b/include/Makefile.am index 5cdea1d15..13d91e22c 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -2,6 +2,7 @@ if XORG sdk_HEADERS = \ XIstubs.h \ Xprintf.h \ + busfault.h \ callback.h \ client.h \ closestr.h \ diff --git a/include/busfault.h b/include/busfault.h new file mode 100644 index 000000000..3b668818d --- /dev/null +++ b/include/busfault.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef _BUSFAULT_H_ +#define _BUSFAULT_H_ + +#include + +#ifdef BUSFAULT + +#include + +typedef void (*busfault_notify_ptr) (void *context); + +struct busfault * +busfault_register_mmap(void *addr, size_t size, busfault_notify_ptr notify, void *context); + +void +busfault_unregister(struct busfault *busfault); + +void +busfault_check(void); + +Bool +busfault_init(void); + +#endif + +#endif /* _BUSFAULT_H_ */ diff --git a/include/dix-config.h.in b/include/dix-config.h.in index e4eea8d01..9bdbe53c1 100644 --- a/include/dix-config.h.in +++ b/include/dix-config.h.in @@ -455,4 +455,7 @@ /* Use XTrans FD passing support */ #undef XTRANS_SEND_FDS +/* Wrap SIGBUS to catch MIT-SHM faults */ +#undef BUSFAULT + #endif /* _DIX_CONFIG_H_ */ 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 +#endif + +#include +#include +#include "misc.h" +#include +#include +#include +#include +#include +#include +#include + +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 /*