[PATCH weston] compositor-wayland: Fix a use after free

Dima Ryazanov dima at gmail.com
Thu Nov 24 06:07:20 UTC 2016


When a window is being closed, the frame_done callback often runs after
the output is already destroyed, i.e:

  wayland_output_start_repaint_loop
  input_handle_button
    wayland_output_destroy
  frame_done

To fix this, destroy the output from an idle handler (same as compositor-x11),
and also stop creating new frame_done callbacks.

Signed-off-by: Dima Ryazanov <dima at gmail.com>
---
 libweston/compositor-wayland.c | 59 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 50 insertions(+), 9 deletions(-)

diff --git a/libweston/compositor-wayland.c b/libweston/compositor-wayland.c
index d9cbde5..fc5daf4 100644
--- a/libweston/compositor-wayland.c
+++ b/libweston/compositor-wayland.c
@@ -128,6 +128,13 @@ struct wayland_output {
 
 	struct weston_mode mode;
 	uint32_t scale;
+
+	bool destroy_pending;
+};
+
+struct output_destroy_data {
+	struct wayland_backend *backend;
+	struct wayland_output *output;
 };
 
 struct wayland_parent_output {
@@ -460,6 +467,9 @@ wayland_output_start_repaint_loop(struct weston_output *output_base)
 		to_wayland_backend(output->base.compositor);
 	struct wl_callback *callback;
 
+	if (output->destroy_pending)
+		return;
+
 	/* If this is the initial frame, we need to attach a buffer so that
 	 * the compositor can map the surface and include it in its render
 	 * loop. If the surface doesn't end up in the render loop, the frame
@@ -485,6 +495,9 @@ wayland_output_repaint_gl(struct weston_output *output_base,
 	struct weston_compositor *ec = output->base.compositor;
 	struct wl_callback *callback;
 
+	if (output->destroy_pending)
+		return 0;
+
 	callback = wl_surface_frame(output->parent.surface);
 	wl_callback_add_listener(callback, &frame_listener, output);
 
@@ -696,6 +709,41 @@ wayland_output_destroy(struct weston_output *base)
 	free(output);
 }
 
+static void
+output_destroy_cb(void *data)
+{
+	struct output_destroy_data *dd = data;
+
+	assert(dd->output->destroy_pending);
+
+	wayland_output_destroy(&dd->output->base);
+	if (wl_list_empty(&dd->backend->compositor->output_list))
+		weston_compositor_exit(dd->backend->compositor);
+
+	free(dd);
+}
+
+static void
+handle_window_closed(struct wayland_backend *backend, struct wayland_output *output)
+{
+	struct wl_event_loop *loop;
+	struct output_destroy_data *data;
+
+	assert(!output->destroy_pending);
+
+	data = malloc(sizeof *data);
+	if (!data)
+		return;  /* Not much we can do here. */
+
+	data->backend = backend;
+	data->output = output;
+
+	loop = wl_display_get_event_loop(backend->compositor->wl_display);
+	wl_event_loop_add_idle(loop, output_destroy_cb, data);
+
+	output->destroy_pending = true;
+}
+
 static const struct wl_shell_surface_listener shell_surface_listener;
 
 static int
@@ -1600,13 +1648,9 @@ input_handle_button(void *data, struct wl_pointer *pointer,
 		}
 
 		if (frame_status(input->output->frame) & FRAME_STATUS_CLOSE) {
-			wayland_output_destroy(&input->output->base);
+			handle_window_closed(input->backend, input->output);
 			input->output = NULL;
 			input->keyboard_focus = NULL;
-
-			if (wl_list_empty(&input->backend->compositor->output_list))
-				weston_compositor_exit(input->backend->compositor);
-
 			return;
 		}
 
@@ -1964,12 +2008,9 @@ input_handle_touch_up(void *data, struct wl_touch *wl_touch,
 		frame_touch_up(output->frame, input, id);
 
 		if (frame_status(output->frame) & FRAME_STATUS_CLOSE) {
-			wayland_output_destroy(&output->base);
+			handle_window_closed(input->backend, output);
 			input->touch_focus = NULL;
 			input->keyboard_focus = NULL;
-			if (wl_list_empty(&input->backend->compositor->output_list))
-				weston_compositor_exit(input->backend->compositor);
-
 			return;
 		}
 		if (frame_status(output->frame) & FRAME_STATUS_REPAINT)
-- 
2.9.3



More information about the wayland-devel mailing list