diff --git a/src/compositor.c b/src/compositor.c index f410214d..480b013e 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -2670,6 +2670,71 @@ weston_surface_build_buffer_matrix(const struct weston_surface *surface, weston_matrix_scale(matrix, vp->buffer.scale, vp->buffer.scale, 1); } +/** + * Compute a + b > c while being safe to overflows. + */ +static bool +fixed_sum_gt(wl_fixed_t a, wl_fixed_t b, wl_fixed_t c) +{ + return (int64_t)a + (int64_t)b > (int64_t)c; +} + +static bool +weston_surface_is_pending_viewport_source_valid( + const struct weston_surface *surface) +{ + const struct weston_surface_state *pend = &surface->pending; + const struct weston_buffer_viewport *vp = &pend->buffer_viewport; + int width_from_buffer = 0; + int height_from_buffer = 0; + wl_fixed_t w; + wl_fixed_t h; + + /* If viewport source rect is not set, it is always ok. */ + if (vp->buffer.src_width == wl_fixed_from_int(-1)) + return true; + + if (pend->newly_attached) { + if (pend->buffer) { + convert_size_by_transform_scale(&width_from_buffer, + &height_from_buffer, + pend->buffer->width, + pend->buffer->height, + vp->buffer.transform, + vp->buffer.scale); + } else { + /* No buffer: viewport is irrelevant. */ + return true; + } + } else { + width_from_buffer = surface->width_from_buffer; + height_from_buffer = surface->height_from_buffer; + } + + assert((width_from_buffer == 0) == (height_from_buffer == 0)); + assert(width_from_buffer >= 0 && height_from_buffer >= 0); + + /* No buffer: viewport is irrelevant. */ + if (width_from_buffer == 0 || height_from_buffer == 0) + return true; + + /* overflow checks for wl_fixed_from_int() */ + if (width_from_buffer > wl_fixed_to_int(INT32_MAX)) + return false; + if (height_from_buffer > wl_fixed_to_int(INT32_MAX)) + return false; + + w = wl_fixed_from_int(width_from_buffer); + h = wl_fixed_from_int(height_from_buffer); + + if (fixed_sum_gt(vp->buffer.src_x, vp->buffer.src_width, w)) + return false; + if (fixed_sum_gt(vp->buffer.src_y, vp->buffer.src_height, h)) + return false; + + return true; +} + /* Translate pending damage in buffer co-ordinates to surface * co-ordinates and union it with a pixman_region32_t. * This should only be called after the buffer is attached. @@ -2809,6 +2874,16 @@ surface_commit(struct wl_client *client, struct wl_resource *resource) struct weston_surface *surface = wl_resource_get_user_data(resource); struct weston_subsurface *sub = weston_surface_to_subsurface(surface); + if (!weston_surface_is_pending_viewport_source_valid(surface)) { + assert(surface->viewport_resource); + + wl_resource_post_error(surface->viewport_resource, + WP_VIEWPORT_ERROR_OUT_OF_BUFFER, + "wl_surface@%d has viewport source outside buffer", + wl_resource_get_id(resource)); + return; + } + if (sub) { weston_subsurface_commit(sub); return;