wayland: Avoid dereferencing a nil data offer

Pass the data device to the selection offer update functions instead of pulling it from the offer, as the offer may be nil.
This commit is contained in:
Frank Praznik
2026-09-07 11:35:33 -04:00
parent 25f4af8fcf
commit 84cd92d705
4 changed files with 31 additions and 22 deletions

View File

@@ -65,7 +65,7 @@ bool Wayland_SetClipboardData(SDL_VideoDevice *_this)
SDL_WaylandDataSource *source = Wayland_DataSourceCreate(video_data);
Wayland_DataSourceSetCallback(source, _this->clipboard_callback, _this->clipboard_userdata, _this->clipboard_sequence);
result = Wayland_DataDeviceSetSelection(data_device, source, (const char **)_this->clipboard_mime_types, _this->num_clipboard_mime_types);
result = Wayland_DataDeviceSetSelectionSource(data_device, source, (const char **)_this->clipboard_mime_types, _this->num_clipboard_mime_types);
if (!result) {
Wayland_DataSourceDestroy(source);
}

View File

@@ -463,6 +463,8 @@ void Wayland_PrimarySelectionSourceDestroy(SDL_WaylandPrimarySelectionSource *so
}
}
static void SelectionOfferNotifyFromMIMEs(SDL_WaylandDataDevice *data_device, bool check_origin);
static void offer_source_done_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
{
if (!callback) {
@@ -485,7 +487,7 @@ static void offer_source_done_handler(void *data, struct wl_callback *callback,
const bool source_is_external = SDL_strncmp(offer->data_device->id_str, id, length) != 0;
SDL_free(id);
if (source_is_external) {
Wayland_DataOfferNotifyFromMIMEs(offer, false);
SelectionOfferNotifyFromMIMEs(offer->data_device, false);
} else {
// Recursive data offer; just destroy it.
SDL_WaylandDataDevice *data_device = offer->data_device;
@@ -528,9 +530,9 @@ static void DataOfferCheckSource(SDL_WaylandDataOffer *offer, const char *mime_t
}
}
static void SetCurrentClipboardOffer(SDL_WaylandDataOffer *offer)
static void UpdateSeatOffers(SDL_WaylandDataDevice *data_device)
{
SDL_WaylandSeat *offer_seat = offer->data_device->seat;
SDL_WaylandSeat *offer_seat = data_device->seat;
SDL_VideoData *video_data = offer_seat->display;
// Clear any existing references to the existing clipboard data before replacing the current offer.
@@ -551,10 +553,12 @@ static void SetCurrentClipboardOffer(SDL_WaylandDataOffer *offer)
video_data->current_data_offer_seat = offer_seat;
}
void Wayland_DataOfferNotifyFromMIMEs(SDL_WaylandDataOffer *offer, bool check_origin)
static void SelectionOfferNotifyFromMIMEs(SDL_WaylandDataDevice *data_device, bool check_origin)
{
int nformats = 0;
SDL_WaylandDataOffer *offer = data_device->selection_offer;
char **new_mime_types = NULL;
size_t num_formats = 0;
if (offer) {
size_t alloc_size = 0;
@@ -571,11 +575,11 @@ void Wayland_DataOfferNotifyFromMIMEs(SDL_WaylandDataOffer *offer, bool check_or
return;
}
++nformats;
++num_formats;
alloc_size += SDL_strlen(item->mime_type) + 1;
}
alloc_size += (nformats + 1) * sizeof(char *);
alloc_size += (num_formats + 1) * sizeof(char *);
new_mime_types = SDL_AllocateTemporaryMemory(alloc_size);
if (!new_mime_types) {
@@ -584,7 +588,7 @@ void Wayland_DataOfferNotifyFromMIMEs(SDL_WaylandDataOffer *offer, bool check_or
}
// Second pass to fill.
char *strPtr = (char *)(new_mime_types + nformats + 1);
char *strPtr = (char *)(new_mime_types + num_formats + 1);
item = NULL;
int i = 0;
wl_list_for_each(item, &offer->mimes, link) {
@@ -596,11 +600,22 @@ void Wayland_DataOfferNotifyFromMIMEs(SDL_WaylandDataOffer *offer, bool check_or
strPtr = stpcpy(strPtr, item->mime_type) + 1;
i++;
}
new_mime_types[nformats] = NULL;
new_mime_types[num_formats] = NULL;
}
SetCurrentClipboardOffer(offer);
SDL_SendClipboardUpdate(false, new_mime_types, nformats);
UpdateSeatOffers(data_device);
SDL_SendClipboardUpdate(false, new_mime_types, num_formats);
}
void Wayland_DataDeviceSetSelectionOffer(SDL_WaylandDataDevice *data_device, SDL_WaylandDataOffer *offer)
{
// Don't notify when clearing the old selection offer if doing so will inadvertently clear the selection source.
const bool notify = offer || (!offer && data_device->selection_offer && (!data_device->selection_offer->callback || !data_device->selection_source));
Wayland_DataOfferDestroy(data_device->selection_offer);
data_device->selection_offer = offer;
if (notify) {
SelectionOfferNotifyFromMIMEs(data_device, true);
}
}
void *Wayland_DataOfferReceive(SDL_WaylandDataOffer *offer, const char *mime_type, size_t *length, bool extended_timeout)
@@ -726,7 +741,7 @@ void Wayland_PrimarySelectionOfferDestroy(SDL_WaylandPrimarySelectionOffer *offe
}
}
bool Wayland_DataDeviceSetSelection(SDL_WaylandDataDevice *data_device, SDL_WaylandDataSource *source, const char **mime_types, size_t mime_count)
bool Wayland_DataDeviceSetSelectionSource(SDL_WaylandDataDevice *data_device, SDL_WaylandDataSource *source, const char **mime_types, size_t mime_count)
{
if (!data_device) {
return SDL_SetError("Invalid Data Device");

View File

@@ -130,7 +130,7 @@ extern void Wayland_PrimarySelectionSourceDestroy(SDL_WaylandPrimarySelectionSou
extern void *Wayland_DataOfferReceive(SDL_WaylandDataOffer *offer, const char *mime_type, size_t *length, bool extended_timeout);
extern void *Wayland_PrimarySelectionOfferReceive(SDL_WaylandPrimarySelectionOffer *offer, const char *mime_type, size_t *length);
extern bool Wayland_DataOfferHasMIME(SDL_WaylandDataOffer *offer, const char *mime_type);
extern void Wayland_DataOfferNotifyFromMIMEs(SDL_WaylandDataOffer *offer, bool check_origin);
extern void Wayland_DataDeviceSetSelectionOffer(SDL_WaylandDataDevice *data_device, SDL_WaylandDataOffer *offer);
extern bool Wayland_PrimarySelectionOfferHasMIME(SDL_WaylandPrimarySelectionOffer *offer, const char *mime_type);
extern bool Wayland_DataOfferAddMIME(SDL_WaylandDataOffer *offer, const char *mime_type);
extern bool Wayland_PrimarySelectionOfferAddMIME(SDL_WaylandPrimarySelectionOffer *offer, const char *mime_type);
@@ -138,7 +138,7 @@ extern void Wayland_DataOfferDestroy(SDL_WaylandDataOffer *offer);
extern void Wayland_PrimarySelectionOfferDestroy(SDL_WaylandPrimarySelectionOffer *offer);
// Clipboard / Primary Selection
extern bool Wayland_DataDeviceSetSelection(SDL_WaylandDataDevice *device, SDL_WaylandDataSource *source, const char **mime_types, size_t mime_count);
extern bool Wayland_DataDeviceSetSelectionSource(SDL_WaylandDataDevice *device, SDL_WaylandDataSource *source, const char **mime_types, size_t mime_count);
extern bool Wayland_PrimarySelectionDeviceSetSelection(SDL_WaylandPrimarySelectionDevice *device, SDL_WaylandPrimarySelectionSource *source, const char *const *mime_types, size_t mime_count);
extern void Wayland_DataDeviceSetSerial(SDL_WaylandDataDevice *device, uint32_t serial);
extern void Wayland_PrimarySelectionDeviceSetSerial(SDL_WaylandPrimarySelectionDevice *device, uint32_t serial);

View File

@@ -2943,13 +2943,7 @@ static void data_device_handle_selection(void *data, struct wl_data_device *wl_d
". In data_device_listener . data_device_handle_selection on data_offer 0x%08x",
(id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1));
// Don't notify when clearing the old selection offer if doing so will inadvertently clear the selection source.
const bool notify = offer || (!offer && data_device->selection_offer && (!data_device->selection_offer->callback || !data_device->selection_source));
Wayland_DataOfferDestroy(data_device->selection_offer);
data_device->selection_offer = offer;
if (notify) {
Wayland_DataOfferNotifyFromMIMEs(offer, true);
}
Wayland_DataDeviceSetSelectionOffer(data_device, offer);
}
static const struct wl_data_device_listener data_device_listener = {