Fix layer shell subsurface use after free

This occurs when a layer surface is destroyed , prompting an unmap event
for all its children subsurfaces (if it has any). Since this happens after
the parent surface is destroyed, any pointers to it will become dangling.
This commit is contained in:
Vlad Pănăzan 2020-12-13 18:32:04 +02:00
parent 1dbb699036
commit 53e4a88bdb
2 changed files with 18 additions and 0 deletions

View file

@ -48,6 +48,7 @@ struct sway_layer_subsurface {
struct wl_listener unmap; struct wl_listener unmap;
struct wl_listener destroy; struct wl_listener destroy;
struct wl_listener commit; struct wl_listener commit;
struct wl_listener layer_surface_destroy;
}; };
struct sway_output; struct sway_output;

View file

@ -398,6 +398,8 @@ static void subsurface_damage(struct sway_layer_subsurface *subsurface,
static void subsurface_handle_unmap(struct wl_listener *listener, void *data) { static void subsurface_handle_unmap(struct wl_listener *listener, void *data) {
struct sway_layer_subsurface *subsurface = struct sway_layer_subsurface *subsurface =
wl_container_of(listener, subsurface, unmap); wl_container_of(listener, subsurface, unmap);
if (subsurface->layer_surface == NULL)
return;
subsurface_damage(subsurface, true); subsurface_damage(subsurface, true);
} }
@ -413,6 +415,14 @@ static void subsurface_handle_commit(struct wl_listener *listener, void *data) {
subsurface_damage(subsurface, false); subsurface_damage(subsurface, false);
} }
static void subsurface_handle_layer_surface_destroy(
struct wl_listener *listener,
void *data) {
struct sway_layer_subsurface *subsurface =
wl_container_of(listener, subsurface, layer_surface_destroy);
subsurface->layer_surface = NULL;
}
static void subsurface_handle_destroy(struct wl_listener *listener, static void subsurface_handle_destroy(struct wl_listener *listener,
void *data) { void *data) {
struct sway_layer_subsurface *subsurface = struct sway_layer_subsurface *subsurface =
@ -422,12 +432,14 @@ static void subsurface_handle_destroy(struct wl_listener *listener,
wl_list_remove(&subsurface->unmap.link); wl_list_remove(&subsurface->unmap.link);
wl_list_remove(&subsurface->destroy.link); wl_list_remove(&subsurface->destroy.link);
wl_list_remove(&subsurface->commit.link); wl_list_remove(&subsurface->commit.link);
wl_list_remove(&subsurface->layer_surface_destroy.link);
free(subsurface); free(subsurface);
} }
static struct sway_layer_subsurface *create_subsurface( static struct sway_layer_subsurface *create_subsurface(
struct wlr_subsurface *wlr_subsurface, struct wlr_subsurface *wlr_subsurface,
struct sway_layer_surface *layer_surface) { struct sway_layer_surface *layer_surface) {
sway_log(SWAY_DEBUG, "new layer subsurface");
struct sway_layer_subsurface *subsurface = struct sway_layer_subsurface *subsurface =
calloc(1, sizeof(struct sway_layer_surface)); calloc(1, sizeof(struct sway_layer_surface));
if (subsurface == NULL) { if (subsurface == NULL) {
@ -446,6 +458,11 @@ static struct sway_layer_subsurface *create_subsurface(
subsurface->commit.notify = subsurface_handle_commit; subsurface->commit.notify = subsurface_handle_commit;
wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit); wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit);
subsurface->layer_surface_destroy.notify =
subsurface_handle_layer_surface_destroy;
wl_signal_add(&layer_surface->layer_surface->events.destroy,
&subsurface->layer_surface_destroy);
return subsurface; return subsurface;
} }