From 353fbb5c2d6850ef740f21165f79ebda017d5982 Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Mon, 12 Oct 2026 17:20:08 +0300 Subject: x11: chunk selection paste property reads and guard against INCR transfers --- src/x11/window.c | 57 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 12 deletions(-) (limited to 'src/x11/window.c') diff --git a/src/x11/window.c b/src/x11/window.c index 4b6f6fd..1a0db7d 100644 --- a/src/x11/window.c +++ b/src/x11/window.c @@ -90,7 +90,7 @@ struct x11_display { int depth; XIM im; Window selection_window; - Atom protocols, delete_window, utf8, clipboard, targets, paste_property; + Atom protocols, delete_window, utf8, clipboard, targets, paste_property, incr; Atom net_name, net_state, fullscreen, maximized_h, maximized_v; Atom xdnd_aware, xdnd_enter, xdnd_position, xdnd_status, xdnd_leave, xdnd_drop, xdnd_finished; Atom xdnd_selection, xdnd_action_copy, xdnd_type_list, uri_list; @@ -1031,18 +1031,50 @@ static void selection_notify(struct x11_display *d, XSelectionEvent *event) free(r); return; } - Atom actual; - int format; - unsigned long count, left; + Atom actual = None; + int format = 0; + unsigned long count = 0, left = 0; unsigned char *data = NULL; - if (XGetWindowProperty(d->native, r->window, event->property, - 0, TEXT_LIMIT / 4, True, AnyPropertyType, - &actual, &format, &count, &left, &data) == Success && - (actual == d->utf8 || actual == XA_STRING) && format == 8 && !left) { - push(s, (struct yasl_input_event){.kind = YASL_INPUT_PASTE}, - (const char *)data, count); - } - if (data) XFree(data); + char *buf = NULL; + size_t total = 0; + unsigned long offset = 0; + + /* Read up to TEXT_LIMIT bytes in 64KB chunks to avoid huge single-request limits */ + while (total < TEXT_LIMIT) { + unsigned long chunk = 16384; /* 64 KB (in 32-bit multiples) */ + if (XGetWindowProperty(d->native, r->window, event->property, + (long)offset, (long)chunk, True, AnyPropertyType, + &actual, &format, &count, &left, &data) != Success || !actual) { + if (data) { XFree(data); data = NULL; } + break; + } + if (actual == d->incr) { + /* Unsupported INCR streaming transfer */ + if (data) { XFree(data); data = NULL; } + break; + } + if ((actual != d->utf8 && actual != XA_STRING) || format != 8 || count == 0) { + if (data) { XFree(data); data = NULL; } + break; + } + char *nb = realloc(buf, total + count + 1); + if (!nb) { + XFree(data); + break; + } + buf = nb; + memcpy(buf + total, data, count); + total += count; + buf[total] = '\0'; + offset += count / 4; + XFree(data); + data = NULL; + if (left == 0) break; + } + if (buf && total > 0) { + push(s, (struct yasl_input_event){.kind = YASL_INPUT_PASTE}, buf, total); + free(buf); + } else free(buf); XDestroyWindow(d->native, r->window); free(r); } @@ -1531,6 +1563,7 @@ static struct yasl_display *display_open(struct yasl_loop *loop, ATOM(clipboard, "CLIPBOARD"); ATOM(targets, "TARGETS"); ATOM(paste_property, "YASL_SELECTION"); + ATOM(incr, "INCR"); ATOM(net_name, "_NET_WM_NAME"); ATOM(net_state, "_NET_WM_STATE"); ATOM(fullscreen, "_NET_WM_STATE_FULLSCREEN"); -- cgit v1.2.3