busfault.c revision 35c4bbdf
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
38struct 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
50static Bool             busfaulted;
51static struct xorg_list busfaults;
52
53struct busfault *
54busfault_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
72void
73busfault_unregister(struct busfault *busfault)
74{
75    xorg_list_del(&busfault->list);
76    free(busfault);
77}
78
79void
80busfault_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
95static void (*previous_busfault_sigaction)(int sig, siginfo_t *info, void *param);
96
97static void
98busfault_sigaction(int sig, siginfo_t *info, void *param)
99{
100    void                *fault = info->si_addr;
101    struct busfault     *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(busfault, &busfaults, list) {
107        if ((char *) busfault->addr <= (char *) fault && (char *) fault < (char *) busfault->addr + busfault->size) {
108            break;
109        }
110    }
111    if (!busfault)
112        goto panic;
113
114    if (!busfault->valid)
115        goto panic;
116
117    busfault->valid = FALSE;
118    busfaulted = TRUE;
119
120    /* The client truncated the file; unmap the shared file, map
121     * /dev/zero over that area and keep going
122     */
123
124    new_addr = mmap(busfault->addr, busfault->size, PROT_READ|PROT_WRITE,
125                    MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, 0);
126
127    if (new_addr == MAP_FAILED)
128        goto panic;
129
130    return;
131panic:
132    if (previous_busfault_sigaction)
133        (*previous_busfault_sigaction)(sig, info, param);
134    else
135        FatalError("bus error");
136}
137
138Bool
139busfault_init(void)
140{
141    struct sigaction    act, old_act;
142
143    act.sa_sigaction = busfault_sigaction;
144    act.sa_flags = SA_SIGINFO;
145    sigemptyset(&act.sa_mask);
146    if (sigaction(SIGBUS, &act, &old_act) < 0)
147        return FALSE;
148    previous_busfault_sigaction = old_act.sa_sigaction;
149    xorg_list_init(&busfaults);
150    return TRUE;
151}
152