[PATCH wayland 2/2] shm: Defer wl_shm_pool_resize if a pool has external references

Derek Foreman derekf at osg.samsung.com
Tue Feb 9 22:03:48 UTC 2016


If a compositor is rendering in one thread while dispatching wayland
events in another, a wl_shm_pool_resize() could change the memory
mappings it's rendering from and cause a crash.

Now we defer wl_shm_pool_resize() if the compositor has references on a
pool, and perform the actual resize when it drops those references.

Signed-off-by: Derek Foreman <derekf at osg.samsung.com>
---
 src/wayland-shm.c | 47 +++++++++++++++++++++++++++++++++++------------
 1 file changed, 35 insertions(+), 12 deletions(-)

diff --git a/src/wayland-shm.c b/src/wayland-shm.c
index 6fbf34b..6351259 100644
--- a/src/wayland-shm.c
+++ b/src/wayland-shm.c
@@ -56,6 +56,7 @@ struct wl_shm_pool {
 	int external_refcount;
 	char *data;
 	int32_t size;
+	int32_t new_size;
 };
 
 struct wl_shm_buffer {
@@ -74,12 +75,35 @@ struct wl_shm_sigbus_data {
 };
 
 static void
+shm_pool_finish_resize(struct wl_shm_pool *pool)
+{
+	void *data;
+
+	if (pool->size == pool->new_size)
+		return;
+
+	data = mremap(pool->data, pool->size, pool->new_size, MREMAP_MAYMOVE);
+	if (data == MAP_FAILED) {
+		wl_resource_post_error(pool->resource,
+				       WL_SHM_ERROR_INVALID_FD,
+				       "failed mremap");
+		return;
+	}
+
+	pool->data = data;
+	pool->size = pool->new_size;
+}
+
+static void
 shm_pool_unref(struct wl_shm_pool *pool, bool external)
 {
-	if (external)
+	if (external) {
 		pool->external_refcount--;
-	else
+		if (pool->external_refcount == 0)
+			shm_pool_finish_resize(pool);
+	} else {
 		pool->internal_refcount--;
+	}
 
 	if (pool->internal_refcount + pool->external_refcount)
 		return;
@@ -202,7 +226,6 @@ shm_pool_resize(struct wl_client *client, struct wl_resource *resource,
 		int32_t size)
 {
 	struct wl_shm_pool *pool = wl_resource_get_user_data(resource);
-	void *data;
 
 	if (size < pool->size) {
 		wl_resource_post_error(resource,
@@ -211,16 +234,15 @@ shm_pool_resize(struct wl_client *client, struct wl_resource *resource,
 		return;
 	}
 
-	data = mremap(pool->data, pool->size, size, MREMAP_MAYMOVE);
-	if (data == MAP_FAILED) {
-		wl_resource_post_error(resource,
-				       WL_SHM_ERROR_INVALID_FD,
-				       "failed mremap");
-		return;
-	}
+	pool->new_size = size;
 
-	pool->data = data;
-	pool->size = size;
+	/* If the compositor has taken references on this pool it
+	 * may be caching pointers into it. In that case we
+	 * defer the resize (which may move the entire mapping)
+	 * until the compositor finishes dereferencing the pool.
+	 */
+	if (pool->external_refcount == 0)
+		shm_pool_finish_resize(pool);
 }
 
 struct wl_shm_pool_interface shm_pool_interface = {
@@ -251,6 +273,7 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource,
 	pool->internal_refcount = 1;
 	pool->external_refcount = 0;
 	pool->size = size;
+	pool->new_size = size;
 	pool->data = mmap(NULL, size,
 			  PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
 	if (pool->data == MAP_FAILED) {
-- 
2.7.0



More information about the wayland-devel mailing list