[PATCH] Trap SIGBUS to handle truncated shared memory segments
Mark Kettenis
mark.kettenis at xs4all.nl
Sun Nov 3 22:45:14 CET 2013
> From: Keith Packard <keithp at keithp.com>
> Date: Sun, 3 Nov 2013 13:17:00 -0800
>
> If a client passes a section of memory via file descriptor and then
> subsequently truncates that file, the underlying pages will be freed
> and the addresses invalidated. Subsequent accesses to the page will
> fail with a SIGBUS error.
>
> Trap that SIGBUS, figure out which segment was causing the error and
> then allocate new pages to fill in for that region. Mark the offending
> shared segment as invalid and free the resource ID so that the client
> will be able to tell when subsequently attempting to use the segment.
>
> diff --git a/os/busfault.c b/os/busfault.c
> new file mode 100644
> index 0000000..05f610e
> --- /dev/null
> +++ b/os/busfault.c
> @@ -0,0 +1,154 @@
> +/*
> + * 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
> + */
> +
> + if (munmap(busfault->addr, busfault->size) < 0)
> + goto panic;
> +
> + new_addr = mmap(busfault->addr, busfault->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
> +
> + if (new_addr != busfault->addr) {
> + munmap(new_addr, busfault->size);
> + goto panic;
> + }
Any reason why you're not using MAP_FIXED instead of doing this
munmap/mmap dance?
More information about the xorg-devel
mailing list