pixman: Destroy pixman images when underlying buffer is destroyed

While the pixman image might be attached, the underlying buffer might be
already gone under certain circumstances. This is easily reproduced by
attempting to resize gnome-terminal on a fbdev backend.

$ WAYLAND_DEBUG=1 strace -emunmap weston --backend=fbdev-backend.so
...
[1524826.942] wl_shm@7.create_pool(new id wl_shm_pool@23, fd 40, 1563540)
[1524827.315] wl_shm_pool@23.create_buffer(new id wl_buffer@24, 0, 759, 515, 3036, 0)
...
[1524829.488] wl_surface@14.attach(wl_buffer@24, 0, 0)
[1524829.766] wl_surface@14.set_buffer_scale(1)
[1524829.904] wl_surface@14.damage(0, 0, 759, 515)
[1524830.248] wl_surface@14.frame(new id wl_callback@25)
[1524830.450] wl_surface@14.commit()
...
[1524846.706] wl_shm@7.create_pool(new id wl_shm_pool@26, fd 40, 1545000)
[1524847.215] wl_shm_pool@26.create_buffer(new id wl_buffer@27, 0, 750, 515, 3000, 0)
[1524847.735] wl_buffer@24.destroy()
[1524847.953]  -> wl_display@1.delete_id(24)
[1524848.144] wl_shm_pool@23.destroy()
munmap(0xb5b2e000, 1563540)             = 0
[1524849.021]  -> wl_display@1.delete_id(23)
[1524849.425] wl_surface@14.attach(wl_buffer@27, 0, 0)
[1524849.730] wl_surface@14.set_buffer_scale(1)
[1524849.821] wl_surface@14.damage(0, 0, 750, 515)
<No commit yet, so drawing is attempted from older buffer that used to be
 attached to the surface, which happens to come from a destroyed pool,
 resulting it an invalid read from address 0xb5b2e000>

Signed-off-by: Lubomir Rintel <lkundrak@v3.sk>
dev
Lubomir Rintel 11 years ago committed by Kristian Høgsberg
parent 24dff2b704
commit ddc2b1ec32
  1. 32
      src/pixman-renderer.c

@ -42,6 +42,7 @@ struct pixman_surface_state {
pixman_image_t *image;
struct weston_buffer_reference buffer_ref;
struct wl_listener buffer_destroy_listener;
struct wl_listener surface_destroy_listener;
struct wl_listener renderer_destroy_listener;
};
@ -467,6 +468,22 @@ pixman_renderer_flush_damage(struct weston_surface *surface)
/* No-op for pixman renderer */
}
static void
buffer_state_handle_buffer_destroy(struct wl_listener *listener, void *data)
{
struct pixman_surface_state *ps;
ps = container_of(listener, struct pixman_surface_state,
buffer_destroy_listener);
if (ps->image) {
pixman_image_unref(ps->image);
ps->image = NULL;
}
ps->buffer_destroy_listener.notify = NULL;
}
static void
pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
{
@ -476,6 +493,11 @@ pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
weston_buffer_reference(&ps->buffer_ref, buffer);
if (ps->buffer_destroy_listener.notify) {
wl_list_remove(&ps->buffer_destroy_listener.link);
ps->buffer_destroy_listener.notify = NULL;
}
if (ps->image) {
pixman_image_unref(ps->image);
ps->image = NULL;
@ -517,6 +539,11 @@ pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
buffer->width, buffer->height,
wl_shm_buffer_get_data(shm_buffer),
wl_shm_buffer_get_stride(shm_buffer));
ps->buffer_destroy_listener.notify =
buffer_state_handle_buffer_destroy;
wl_signal_add(&buffer->destroy_signal,
&ps->buffer_destroy_listener);
}
static void
@ -524,7 +551,10 @@ pixman_renderer_surface_state_destroy(struct pixman_surface_state *ps)
{
wl_list_remove(&ps->surface_destroy_listener.link);
wl_list_remove(&ps->renderer_destroy_listener.link);
if (ps->buffer_destroy_listener.notify) {
wl_list_remove(&ps->buffer_destroy_listener.link);
ps->buffer_destroy_listener.notify = NULL;
}
ps->surface->renderer_state = NULL;

Loading…
Cancel
Save