Drag and drop: Fix a use-after-free

This commit is contained in:
Kovid Goyal 2026-09-01 20:30:45 +05:30
parent 398fa49084
commit b54bfbb4f8
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
2 changed files with 34 additions and 9 deletions

View file

@ -279,6 +279,8 @@ Detailed list of changes
- Drag and drop protocol: Deny drag sources that send identically named symlink/dir entries with an appropriate error (:cve:`2026-80430`)
- Drag and drop protocol: Fix a use-after-free when a drag source item is aborted mid-transfer, where the freed remote item was still read from and written to after the drag offer was torn down
- Text sizing protocol: Fix a buffer overflow when a natural width text sizing escape code contains a grapheme cluster longer than four codepoints (:cve:`2026-80431`)
- ssh kitten askpass: Verify owner and permissions of SHM memory used for askpass

View file

@ -2289,7 +2289,20 @@ uniqify_dir_entries_for_case_insensitive_fs(DragRemoteItem *children, size_t cou
vt_cleanup(&seen);
}
static void
// The following functions can abort the drag when they encounter an error.
// Aborting calls cancel_drag() -> drag_free_offer(), which frees the offer
// including the DragRemoteItem the caller is holding. They therefore return
// false on abort (and true otherwise) so callers know to stop touching the now
// freed memory. abrt() is redefined here to return false instead of void; it is
// restored to its void form after subdir_data_for_drag().
#undef abrt
#define abrt(code, details) \
{ \
cancel_drag(w, code, details); \
return false; \
}
static bool
populate_dir_entries(Window *w, DragRemoteItem *ri) {
size_t num = count_occurrences((char *)ri->data, ri->data_sz, 0) + 1;
ri->children = calloc(num + 1, sizeof(ri->children[0]));
@ -2319,9 +2332,10 @@ populate_dir_entries(Window *w, DragRemoteItem *ri) {
ptr = p ? p + 1 : end;
}
if (tempdir_case_insensitive == 1) uniqify_dir_entries_for_case_insensitive_fs(ri->children, ri->children_sz);
return true;
}
static void
static bool
add_payload(Window *w, DragRemoteItem *ri, bool has_more, const uint8_t *payload, size_t payload_sz, int dirfd) {
if (payload_sz && payload) {
if (payload_sz > 4096) abrt(EINVAL, "drag source item data chunk too large");
@ -2391,7 +2405,7 @@ add_payload(Window *w, DragRemoteItem *ri, bool has_more, const uint8_t *payload
log_error("Failed to create directory for drag source item with name: %s and error: %s", ri->dir_entry_name, strerror(err));
abrt(err, "failed to create directory for drag source item");
}
populate_dir_entries(w, ri);
if (!populate_dir_entries(w, ri)) return false;
break;
}
free(ri->data);
@ -2399,6 +2413,7 @@ add_payload(Window *w, DragRemoteItem *ri, bool has_more, const uint8_t *payload
ri->data_capacity = 0;
ri->data_sz = 0;
}
return true;
}
static int
@ -2413,7 +2428,7 @@ ensure_base_dir_for_drag(Window *w) {
return 0;
}
static void
static bool
toplevel_data_for_drag(
Window *w,
DragRemoteItem *ri,
@ -2450,7 +2465,7 @@ toplevel_data_for_drag(
mi.uri_list[uri_item_idx] = as_file_url(ds.base_dir_for_remote_items, path, ri->dir_entry_name);
}
}
add_payload(w, ri, has_more, payload, payload_sz, ri->top_level_parent_dir_fd_plus_one - 1);
return add_payload(w, ri, has_more, payload, payload_sz, ri->top_level_parent_dir_fd_plus_one - 1);
}
static DragRemoteItem *
@ -2513,7 +2528,7 @@ open_subdir_of_drag(DragRemoteItem *root, DragRemoteItem *item, unsigned depth)
return ans;
}
static void
static bool
subdir_data_for_drag(
Window *w,
unsigned mime_item_idx,
@ -2555,9 +2570,17 @@ subdir_data_for_drag(
(*ri)->type = item_type;
base64_init_stream_decoder(&(*ri)->base64_state);
}
add_payload(w, *ri, has_more, payload, payload_sz, parent->fd_plus_one - 1);
return add_payload(w, *ri, has_more, payload, payload_sz, parent->fd_plus_one - 1);
}
// Restore abrt() to its void-returning form for the remaining functions.
#undef abrt
#define abrt(code, details) \
{ \
cancel_drag(w, code, details); \
return; \
}
void
drag_offer_start_to_child(Window *w, int32_t cell_x, int32_t cell_y, int32_t pixel_x, int32_t pixel_y) {
char buf[256];
@ -2619,7 +2642,7 @@ drag_remote_file_data(Window *w, int32_t x, int32_t y, int32_t X, int32_t Y, boo
ri = mi.remote_items + uri_item_idx;
}
if (!Y) {
toplevel_data_for_drag(w, ri, mime_item_idx, uri_item_idx, X, has_more, payload, payload_sz);
if (!toplevel_data_for_drag(w, ri, mime_item_idx, uri_item_idx, X, has_more, payload, payload_sz)) return;
if (all_data_received && all_children_complete(ri)) {
ri->completed = true;
if (ds.file_promises) finish_file_promise(w, uri_item_idx, ri);
@ -2627,7 +2650,7 @@ drag_remote_file_data(Window *w, int32_t x, int32_t y, int32_t X, int32_t Y, boo
}
} else {
if (y < 1) abrt(EINVAL, "drag source remote item y index cannot be less than 1");
subdir_data_for_drag(w, mime_item_idx, Y, y - 1, X, has_more, payload, payload_sz, &ri);
if (!subdir_data_for_drag(w, mime_item_idx, Y, y - 1, X, has_more, payload, payload_sz, &ri)) return;
if (all_data_received && ri && all_children_complete(ri)) {
ri->completed = true;
while (1) {