busfault.c (4120B)
1 /* 2 * Copyright © 2013 Keith Packard 3 * 4 * Permission to use, copy, modify, distribute, and sell this software and its 5 * documentation for any purpose is hereby granted without fee, provided that 6 * the above copyright notice appear in all copies and that both that copyright 7 * notice and this permission notice appear in supporting documentation, and 8 * that the name of the copyright holders not be used in advertising or 9 * publicity pertaining to distribution of the software without specific, 10 * written prior permission. The copyright holders make no representations 11 * about the suitability of this software for any purpose. It is provided "as 12 * is" without express or implied warranty. 13 * 14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 20 * OF THIS SOFTWARE. 21 */ 22 23 #ifdef HAVE_DIX_CONFIG_H 24 #include <dix-config.h> 25 #endif 26 27 #include <X11/Xos.h> 28 #include <X11/Xdefs.h> 29 #include "misc.h" 30 #include <busfault.h> 31 #include <list.h> 32 #include <stddef.h> 33 #include <stdlib.h> 34 #include <stdint.h> 35 #include <sys/mman.h> 36 #include <signal.h> 37 38 struct busfault { 39 struct xorg_list list; 40 41 void *addr; 42 size_t size; 43 44 Bool valid; 45 46 busfault_notify_ptr notify; 47 void *context; 48 }; 49 50 static Bool busfaulted; 51 static struct xorg_list busfaults; 52 53 struct busfault * 54 busfault_register_mmap(void *addr, size_t size, busfault_notify_ptr notify, void *context) 55 { 56 struct busfault *busfault; 57 58 busfault = calloc(1, sizeof (struct busfault)); 59 if (!busfault) 60 return NULL; 61 62 busfault->addr = addr; 63 busfault->size = size; 64 busfault->notify = notify; 65 busfault->context = context; 66 busfault->valid = TRUE; 67 68 xorg_list_add(&busfault->list, &busfaults); 69 return busfault; 70 } 71 72 void 73 busfault_unregister(struct busfault *busfault) 74 { 75 xorg_list_del(&busfault->list); 76 free(busfault); 77 } 78 79 void 80 busfault_check(void) 81 { 82 struct busfault *busfault, *tmp; 83 84 if (!busfaulted) 85 return; 86 87 busfaulted = FALSE; 88 89 xorg_list_for_each_entry_safe(busfault, tmp, &busfaults, list) { 90 if (!busfault->valid) 91 (*busfault->notify)(busfault->context); 92 } 93 } 94 95 static void (*previous_busfault_sigaction)(int sig, siginfo_t *info, void *param); 96 97 static void 98 busfault_sigaction(int sig, siginfo_t *info, void *param) 99 { 100 void *fault = info->si_addr; 101 struct busfault *iter, *busfault = NULL; 102 void *new_addr; 103 104 /* Locate the faulting address in our list of shared segments 105 */ 106 xorg_list_for_each_entry(iter, &busfaults, list) { 107 if ((char *) iter->addr <= (char *) fault && (char *) fault < (char *) iter->addr + iter->size) { 108 busfault = iter; 109 break; 110 } 111 } 112 if (!busfault) 113 goto panic; 114 115 if (!busfault->valid) 116 goto panic; 117 118 busfault->valid = FALSE; 119 busfaulted = TRUE; 120 121 /* The client truncated the file; unmap the shared file, map 122 * /dev/zero over that area and keep going 123 */ 124 125 new_addr = mmap(busfault->addr, busfault->size, PROT_READ|PROT_WRITE, 126 MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, 0); 127 128 if (new_addr == MAP_FAILED) 129 goto panic; 130 131 return; 132 panic: 133 if (previous_busfault_sigaction) 134 (*previous_busfault_sigaction)(sig, info, param); 135 else 136 FatalError("bus error\n"); 137 } 138 139 Bool 140 busfault_init(void) 141 { 142 struct sigaction act, old_act; 143 144 act.sa_sigaction = busfault_sigaction; 145 act.sa_flags = SA_SIGINFO; 146 sigemptyset(&act.sa_mask); 147 if (sigaction(SIGBUS, &act, &old_act) < 0) 148 return FALSE; 149 previous_busfault_sigaction = old_act.sa_sigaction; 150 xorg_list_init(&busfaults); 151 return TRUE; 152 }