summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-12 17:20:08 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-12 17:20:08 +0300
commit353fbb5c2d6850ef740f21165f79ebda017d5982 (patch)
tree0b8f581293994560e50ea513f5735467bbc13322 /src
parentb583befd7faff756ddc8d9cc201ca82f246ed09a (diff)
x11: chunk selection paste property reads and guard against INCR transfers
Diffstat (limited to 'src')
-rw-r--r--src/x11/window.c57
1 files changed, 45 insertions, 12 deletions
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");