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 *
busfault_register_mmap(void * addr,size_t size,busfault_notify_ptr notify,void * context)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
busfault_unregister(struct busfault * busfault)73 busfault_unregister(struct busfault *busfault)
74 {
75 xorg_list_del(&busfault->list);
76 free(busfault);
77 }
78
79 void
busfault_check(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
busfault_sigaction(int sig,siginfo_t * info,void * param)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
busfault_init(void)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 }
153