wayland-shm: Don’t set SIGBUS handlers on unshrinkable fd
authorEmmanuel Gil Peyrot <linkmauve@linkmauve.fr>
Mon, 22 Jul 2019 13:58:49 +0000 (15:58 +0200)
committerSimon Ser <contact@emersion.fr>
Mon, 14 Oct 2019 21:49:08 +0000 (21:49 +0000)
If a client set the F_SEAL_SHRINK seal on the fd before passing it to
the compositor, the kernel will ensure this fd won’t be able to shrink,
ever.  This allows us to avoid setting up the SIGBUS handlers on such
file descriptors.

Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
src/wayland-shm.c

index 967b875..b85e5a7 100644 (file)
@@ -30,6 +30,8 @@
 
 #define _GNU_SOURCE
 
+#include "config.h"
+
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -41,6 +43,7 @@
 #include <signal.h>
 #include <pthread.h>
 #include <errno.h>
+#include <fcntl.h>
 
 #include "wayland-util.h"
 #include "wayland-private.h"
@@ -60,6 +63,7 @@ struct wl_shm_pool {
        char *data;
        int32_t size;
        int32_t new_size;
+       bool sigbus_is_impossible;
 };
 
 struct wl_shm_buffer {
@@ -259,6 +263,7 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource,
                uint32_t id, int fd, int32_t size)
 {
        struct wl_shm_pool *pool;
+       int seals;
 
        if (size <= 0) {
                wl_resource_post_error(resource,
@@ -273,6 +278,15 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource,
                goto err_close;
        }
 
+#ifdef HAVE_MEMFD_CREATE
+       seals = fcntl(fd, F_GET_SEALS);
+       if (seals == -1)
+               seals = 0;
+       pool->sigbus_is_impossible = (seals & F_SEAL_SHRINK) ? true : false;
+#else
+       pool->sigbus_is_impossible = false;
+#endif
+
        pool->internal_refcount = 1;
        pool->external_refcount = 0;
        pool->size = size;
@@ -571,6 +585,9 @@ wl_shm_buffer_begin_access(struct wl_shm_buffer *buffer)
        struct wl_shm_pool *pool = buffer->pool;
        struct wl_shm_sigbus_data *sigbus_data;
 
+       if (pool->sigbus_is_impossible)
+               return;
+
        pthread_once(&wl_shm_sigbus_once, init_sigbus_data_key);
 
        sigbus_data = pthread_getspecific(wl_shm_sigbus_data_key);
@@ -603,9 +620,13 @@ wl_shm_buffer_begin_access(struct wl_shm_buffer *buffer)
 WL_EXPORT void
 wl_shm_buffer_end_access(struct wl_shm_buffer *buffer)
 {
-       struct wl_shm_sigbus_data *sigbus_data =
-               pthread_getspecific(wl_shm_sigbus_data_key);
+       struct wl_shm_pool *pool = buffer->pool;
+       struct wl_shm_sigbus_data *sigbus_data;
 
+       if (pool->sigbus_is_impossible)
+               return;
+
+       sigbus_data = pthread_getspecific(wl_shm_sigbus_data_key);
        assert(sigbus_data && sigbus_data->access_count >= 1);
 
        if (--sigbus_data->access_count == 0) {