<p dir="ltr">Neil,<br>
While I think this patch will work in the single-threaded case, I'm not a big fan of the api for two reasons: 1) without doing thread-local things it is inherently single-threaded. (This is obviously not an issue for Weston, but requiring all shm buffer reads to be on the Wayland fd thread could be a big issue for others.)  2) it doesn't allow for integration with another SIGBUS/SEGEV handler.</p>

<p dir="ltr">My knowledge of thread-local stuff isn't great, so (1) may be a non-issue in the case where we're not fighting with another signal handler.  However, I do think it would be good to provide better integration with other handlers.  Perhaps a function that takes a "bad" pointer and checks to see if it is in an active pool?<br>

Thanks,<br>
--Jason Ekstrand</p>
<div class="gmail_quote">On Sep 30, 2013 6:52 PM, "Neil Roberts" <<a href="mailto:neil@linux.intel.com">neil@linux.intel.com</a>> wrote:<br type="attribution"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Linux will let you mmap a region of a file that is larger than the<br>
size of the file. If you then try to read from that region the process<br>
will get a SIGBUS signal. Currently the clients can use this to crash<br>
a compositor because it can create a pool and lie about the size of<br>
the file which will cause the compositor to try and read past the end<br>
of it. The compositor can't simply check the size of the file to<br>
verify that it is big enough because then there is a race condition<br>
where the client may truncate the file after the check is performed.<br>
<br>
This patch adds the following two public functions in the server API<br>
which can be used wrap access to an SHM buffer:<br>
<br>
void wl_shm_buffer_begin_access(struct wl_shm_buffer *buffer);<br>
void wl_shm_buffer_end_access(struct wl_shm_buffer *buffer);<br>
<br>
In-between calls to these functions there will be a signal handler for<br>
SIGBUS. If the signal is caught then the buffer is remapped to an<br>
anonymous private buffer at the same address which allows the<br>
compositor to continue without crashing. The end_access function will<br>
then post an error to the buffer resource.<br>
---<br>
<br>
This problem was pointed out earlier by wm4 on #wayland and it seems<br>
like quite a thorny issue. Here is a first attempt at a patch which<br>
does seem to work for Weston but I think there are plenty of issues to<br>
think about. It might not be such a good idea to be messing with<br>
signal handlers if the compositor itself also wants to handle SIGBUS.<br>
There are probably some threading concerns here too.<br>
<br>
I think you could do the patch without adding any extra API and just<br>
make it automatically work out which region to remap if you had a list<br>
of wl_shm_pools. The signal handler gets passed the address so it<br>
could check which pool contains it and just remap that one. However<br>
the end_access function seems like a nice place to post an event back<br>
to the client when it fails so I'm not sure which way is better. It's<br>
also probably a good idea to keep the signal handler as simple as<br>
possible.<br>
<br>
 src/wayland-server.h |  6 ++++<br>
 src/wayland-shm.c    | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++<br>
 2 files changed, 93 insertions(+)<br>
<br>
diff --git a/src/wayland-server.h b/src/wayland-server.h<br>
index c93987d..b371c9e 100644<br>
--- a/src/wayland-server.h<br>
+++ b/src/wayland-server.h<br>
@@ -412,6 +412,12 @@ wl_resource_get_destroy_listener(struct wl_resource *resource,<br>
<br>
 struct wl_shm_buffer;<br>
<br>
+void<br>
+wl_shm_buffer_begin_access(struct wl_shm_buffer *buffer);<br>
+<br>
+void<br>
+wl_shm_buffer_end_access(struct wl_shm_buffer *buffer);<br>
+<br>
 struct wl_shm_buffer *<br>
 wl_shm_buffer_get(struct wl_resource *resource);<br>
<br>
diff --git a/src/wayland-shm.c b/src/wayland-shm.c<br>
index eff29c3..95b1f33 100644<br>
--- a/src/wayland-shm.c<br>
+++ b/src/wayland-shm.c<br>
@@ -32,15 +32,22 @@<br>
 #include <string.h><br>
 #include <sys/mman.h><br>
 #include <unistd.h><br>
+#include <assert.h><br>
+#include <signal.h><br>
<br>
 #include "wayland-private.h"<br>
 #include "wayland-server.h"<br>
<br>
+static struct wl_shm_pool *wl_shm_current_pool;<br>
+static struct sigaction wl_shm_old_sigbus_action;<br>
+<br>
 struct wl_shm_pool {<br>
        struct wl_resource *resource;<br>
        int refcount;<br>
        char *data;<br>
        int size;<br>
+       int access_count;<br>
+       int fallback_mapping_used;<br>
 };<br>
<br>
 struct wl_shm_buffer {<br>
@@ -219,6 +226,7 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource,<br>
<br>
        pool->refcount = 1;<br>
        pool->size = size;<br>
+       pool->fallback_mapping_used = 0;<br>
        pool->data = mmap(NULL, size,<br>
                          PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);<br>
        if (pool->data == MAP_FAILED) {<br>
@@ -368,3 +376,82 @@ wl_shm_buffer_get_height(struct wl_shm_buffer *buffer)<br>
 {<br>
        return buffer->height;<br>
 }<br>
+<br>
+static void<br>
+unset_sigbus_handler(void)<br>
+{<br>
+       sigaction(SIGBUS, &wl_shm_old_sigbus_action, NULL);<br>
+}<br>
+<br>
+static void<br>
+sigbus_handler(int signum, siginfo_t *info, void *context)<br>
+{<br>
+       struct wl_shm_pool *pool = wl_shm_current_pool;<br>
+<br>
+       unset_sigbus_handler();<br>
+<br>
+       /* If the offending address is outside the mapped space for<br>
+        * the pool then the error is a real problem so we'll reraise<br>
+        * the signal */<br>
+       if ((char *) info->si_addr < pool->data ||<br>
+           (char *) info->si_addr >= pool->data + pool->size)<br>
+               raise(signum);<br>
+<br>
+       pool->fallback_mapping_used = 1;<br>
+<br>
+       /* This should replace the previous mapping */<br>
+       if (mmap(pool->data, pool->size,<br>
+                PROT_READ | PROT_WRITE,<br>
+                MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS,<br>
+                0, 0) == (void *) -1)<br>
+               raise(signum);<br>
+}<br>
+<br>
+static void<br>
+set_sigbus_handler(void)<br>
+{<br>
+       struct sigaction new_action = {<br>
+               .sa_sigaction = sigbus_handler,<br>
+               .sa_flags = SA_SIGINFO | SA_NODEFER<br>
+       };<br>
+<br>
+       sigemptyset(&new_action.sa_mask);<br>
+<br>
+       sigaction(SIGBUS, &new_action, &wl_shm_old_sigbus_action);<br>
+}<br>
+<br>
+WL_EXPORT void<br>
+wl_shm_buffer_begin_access(struct wl_shm_buffer *buffer)<br>
+{<br>
+       struct wl_shm_pool *pool = buffer->pool;<br>
+<br>
+       assert(wl_shm_current_pool == NULL || wl_shm_current_pool == pool);<br>
+<br>
+       if (pool->access_count == 0) {<br>
+               wl_shm_current_pool = pool;<br>
+<br>
+               if (!pool->fallback_mapping_used)<br>
+                       set_sigbus_handler();<br>
+       }<br>
+<br>
+       pool->access_count++;<br>
+}<br>
+<br>
+WL_EXPORT void<br>
+wl_shm_buffer_end_access(struct wl_shm_buffer *buffer)<br>
+{<br>
+       struct wl_shm_pool *pool = buffer->pool;<br>
+<br>
+       assert(pool->access_count >= 1);<br>
+<br>
+       if (--pool->access_count == 0) {<br>
+               if (pool->fallback_mapping_used) {<br>
+                       wl_resource_post_error(buffer->resource,<br>
+                                              WL_SHM_ERROR_INVALID_FD,<br>
+                                              "error accessing SHM buffer");<br>
+               } else {<br>
+                       unset_sigbus_handler();<br>
+               }<br>
+               wl_shm_current_pool = NULL;<br>
+       }<br>
+}<br>
--<br>
1.8.3.1<br>
<br>
_______________________________________________<br>
wayland-devel mailing list<br>
<a href="mailto:wayland-devel@lists.freedesktop.org">wayland-devel@lists.freedesktop.org</a><br>
<a href="http://lists.freedesktop.org/mailman/listinfo/wayland-devel" target="_blank">http://lists.freedesktop.org/mailman/listinfo/wayland-devel</a><br>
</blockquote></div>