Skip to content

Commit

Permalink
data-control: Stop receive handlers on destroy
Browse files Browse the repository at this point in the history
Previously the receive handlers were kept alive after the client was
closed. These can just be cancelled when the client is closed. This also
fixes a use-after-free because a reference to data_control (part of
wayvnc_client) is in the receive_context.
  • Loading branch information
layercak3 authored and any1 committed Sep 18, 2024
1 parent 34a1d25 commit aa8a8d3
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 10 deletions.
7 changes: 7 additions & 0 deletions include/data-control.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,16 @@

#include "wlr-data-control-unstable-v1.h"

#include "sys/queue.h"

struct receive_context;

LIST_HEAD(receive_context_list, receive_context);

struct data_control {
struct wl_display* wl_display;
struct nvnc* server;
struct receive_context_list receive_contexts;
struct zwlr_data_control_manager_v1* manager;
struct zwlr_data_control_device_v1* device;
struct zwlr_data_control_source_v1* selection;
Expand Down
38 changes: 28 additions & 10 deletions src/data-control.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,26 @@ static const char custom_mime_type_data[] = "wayvnc";

struct receive_context {
struct data_control* data_control;
struct aml_handler* handler;
LIST_ENTRY(receive_context) link;
struct zwlr_data_control_offer_v1* offer;
int fd;
FILE* mem_fp;
size_t mem_size;
char* mem_data;
};

static void destroy_receive_context(void* raw_ctx)
static void destroy_receive_context(struct receive_context* ctx)
{
struct receive_context* ctx = raw_ctx;
int fd = ctx->fd;
aml_stop(aml_get_default(), ctx->handler);
aml_unref(ctx->handler);

if (ctx->mem_fp)
fclose(ctx->mem_fp);
free(ctx->mem_data);
zwlr_data_control_offer_v1_destroy(ctx->offer);
close(fd);
close(ctx->fd);
LIST_REMOVE(ctx, link);
free(ctx);
}

Expand All @@ -71,7 +74,7 @@ static void on_receive(void* handler)
nvnc_send_cut_text(ctx->data_control->server, ctx->mem_data,
ctx->mem_size);

aml_stop(aml_get_default(), handler);
destroy_receive_context(ctx);
}

static void receive_data(void* data,
Expand All @@ -82,6 +85,7 @@ static void receive_data(void* data,

if (pipe(pipe_fd) == -1) {
nvnc_log(NVNC_LOG_ERROR, "pipe() failed: %m");
zwlr_data_control_offer_v1_destroy(offer);
return;
}

Expand All @@ -90,6 +94,7 @@ static void receive_data(void* data,
nvnc_log(NVNC_LOG_ERROR, "OOM");
close(pipe_fd[0]);
close(pipe_fd[1]);
zwlr_data_control_offer_v1_destroy(offer);
return;
}

Expand All @@ -102,22 +107,32 @@ static void receive_data(void* data,
ctx->offer = offer;
ctx->mem_fp = open_memstream(&ctx->mem_data, &ctx->mem_size);
if (!ctx->mem_fp) {
zwlr_data_control_offer_v1_destroy(ctx->offer);
close(ctx->fd);
free(ctx);
nvnc_log(NVNC_LOG_ERROR, "open_memstream() failed: %m");
return;
}

struct aml_handler* handler = aml_handler_new(ctx->fd, on_receive,
ctx, destroy_receive_context);
if (!handler) {
ctx->handler = aml_handler_new(ctx->fd, on_receive, ctx, NULL);
if (!ctx->handler) {
fclose(ctx->mem_fp);
zwlr_data_control_offer_v1_destroy(ctx->offer);
close(ctx->fd);
free(ctx);
return;
}

if (aml_start(aml_get_default(), ctx->handler) < 0) {
aml_unref(ctx->handler);
fclose(ctx->mem_fp);
zwlr_data_control_offer_v1_destroy(ctx->offer);
close(ctx->fd);
free(ctx);
return;
}

aml_start(aml_get_default(), handler);
aml_unref(handler);
LIST_INSERT_HEAD(&self->receive_contexts, ctx, link);
}

static void data_control_offer(void* data,
Expand Down Expand Up @@ -267,6 +282,7 @@ void data_control_init(struct data_control* self, struct wl_display* wl_display,
{
self->wl_display = wl_display;
self->server = server;
LIST_INIT(&self->receive_contexts);
self->device = zwlr_data_control_manager_v1_get_data_device(self->manager, seat);
zwlr_data_control_device_v1_add_listener(self->device, &data_control_device_listener, self);
self->selection = NULL;
Expand All @@ -283,6 +299,8 @@ void data_control_init(struct data_control* self, struct wl_display* wl_display,

void data_control_destroy(struct data_control* self)
{
while (!LIST_EMPTY(&self->receive_contexts))
destroy_receive_context(LIST_FIRST(&self->receive_contexts));
if (self->selection) {
zwlr_data_control_source_v1_destroy(self->selection);
self->selection = NULL;
Expand Down

0 comments on commit aa8a8d3

Please sign in to comment.