compositor: fix wp_viewport use after free
If a client destroyed the wl_surface before the wp_viewport, Weston core
would access freed memory, because the weston_surface pointer stored in
the wp_viewport wl_resource's user data was stale.
Fix this by setting the user data to NULL on wl_surface destruction. It
is specifically about wl_surface and not weston_surface destruction,
because this is about client-visible behaviour. Something internal might
keep weston_surface alive past the wl_surface.
Add checks to all wp_viewport request handlers.
At the same time, implement the new error conditions in wp_viewport:
calling any request except destroy must result in a protocol error.
Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
Reviewed-by: Bryce Harrington <bryce@osg.samsung.com>
diff --git a/src/compositor.c b/src/compositor.c
index 870cd47..f410214 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -1852,6 +1852,10 @@
* dangling pointer if the surface was refcounted and survives
* the weston_surface_destroy() call. */
surface->resource = NULL;
+
+ if (surface->viewport_resource)
+ wl_resource_set_user_data(surface->viewport_resource, NULL);
+
weston_surface_destroy(surface);
}
@@ -4219,6 +4223,9 @@
struct weston_surface *surface =
wl_resource_get_user_data(resource);
+ if (!surface)
+ return;
+
surface->viewport_resource = NULL;
surface->pending.buffer_viewport.buffer.src_width =
wl_fixed_from_int(-1);
@@ -4244,7 +4251,14 @@
struct weston_surface *surface =
wl_resource_get_user_data(resource);
- assert(surface->viewport_resource != NULL);
+ if (!surface) {
+ wl_resource_post_error(resource,
+ WP_VIEWPORT_ERROR_NO_SURFACE,
+ "wl_surface for this viewport is no longer exists");
+ return;
+ }
+
+ assert(surface->viewport_resource == resource);
if (src_width == wl_fixed_from_int(-1) &&
src_height == wl_fixed_from_int(-1)) {
@@ -4280,7 +4294,14 @@
struct weston_surface *surface =
wl_resource_get_user_data(resource);
- assert(surface->viewport_resource != NULL);
+ if (!surface) {
+ wl_resource_post_error(resource,
+ WP_VIEWPORT_ERROR_NO_SURFACE,
+ "wl_surface for this viewport no longer exists");
+ return;
+ }
+
+ assert(surface->viewport_resource == resource);
if (dst_width == -1 && dst_height == -1) {
/* unset destination size */