diff options
| author | Paul Eggert <eggert@cs.ucla.edu> | 2026-05-23 09:34:30 -0700 |
|---|---|---|
| committer | Paul Eggert <eggert@cs.ucla.edu> | 2026-05-23 19:18:53 -0700 |
| commit | fbd2f781b2a75554cc5571490198f7dfff18f9f3 (patch) | |
| tree | cd11d3c0109956478918972440b3ddf542efe527 | |
| parent | 1bee33c1c801adf5e3deeba7328b811b80c55f70 (diff) | |
Be more careful about X selection sizes
* src/xselect.c (selection_data_for_offset):
Offset is size_t, not long, since that’s what caller passes.
(selection_data_size): Truncate large selection sizes to a value
that is more likely to work without involving undefined behavior.
Do not exceed X_ULONG_MAX which is all X can handle, or
PTRDIFF_MAX which can confuse underlying code.
(x_start_selection_transfer): Invoke selection_data_size just once.
| -rw-r--r-- | src/xselect.c | 29 |
1 files changed, 18 insertions, 11 deletions
diff --git a/src/xselect.c b/src/xselect.c index 5be009af0d7..9e343f16544 100644 --- a/src/xselect.c +++ b/src/xselect.c @@ -712,7 +712,7 @@ x_size_for_format (int format) static unsigned char * selection_data_for_offset (struct selection_data *data, - long offset, size_t *remaining) + size_t offset, size_t *remaining) { unsigned char *base; size_t size; @@ -741,12 +741,17 @@ selection_data_for_offset (struct selection_data *data, /* Return the size, in bytes transferred to the X server, of data->size items of selection data in data->format-bit - quantities. */ + quantities. If this size is too large, silently return + the largest supported size in bytes for this format. + + FIXME: Silent truncation is bad. */ static size_t selection_data_size (struct selection_data *data) { size_t scratch; + ptrdiff_t max_selection_size = min (min (PTRDIFF_MAX, SIZE_MAX), + X_ULONG_MAX); if (!NILP (data->string)) return SBYTES (data->string); @@ -754,17 +759,19 @@ selection_data_size (struct selection_data *data) switch (data->format) { case 8: - return (size_t) data->size; + return min (data->size, max_selection_size); case 16: - if (ckd_mul (&scratch, data->size, 2)) - return SIZE_MAX; + if (ckd_mul (&scratch, data->size, 2) + || max_selection_size - max_selection_size % 2 < scratch) + return max_selection_size - max_selection_size % 2; return scratch; case 32: - if (ckd_mul (&scratch, data->size, 4)) - return SIZE_MAX; + if (ckd_mul (&scratch, data->size, 4) + || max_selection_size - max_selection_size % 4 < scratch) + return max_selection_size - max_selection_size % 4; return scratch; } @@ -885,12 +892,12 @@ x_start_selection_transfer (struct x_display_info *dpyinfo, Window requestor, max_size = selection_quantum (dpyinfo->display); + size_t seldata_size = selection_data_size (&transfer->data); TRACE3 (" x_start_selection_transfer: transferring to 0x%lx. " "transfer consists of %zu bytes, quantum being %zu", - requestor, selection_data_size (&transfer->data), - max_size); + requestor, seldata_size, max_size); - if (selection_data_size (&transfer->data) > max_size) + if (max_size < seldata_size) { /* Begin incremental selection transfer. First, calculate how many elements it is ok to write for every ChangeProperty @@ -918,7 +925,7 @@ x_start_selection_transfer (struct x_display_info *dpyinfo, Window requestor, /* Now, write the INCR property to begin incremental selection transfer. offset is currently 0. */ - data_size = selection_data_size (&transfer->data); + data_size = seldata_size; /* Set SELECTED_EVENTS before the actual XSelectInput request. */ |
