# SDL: wayland: Avoid dereferencing a nil data offer

**URL:** <https://discourse.libsdl.org/t/sdl-wayland-avoid-dereferencing-a-nil-data-offer/69254>\
**Category:** SDL Commits\
**Created:** [September 8, 2026, 3:27pm UTC](https://discourse.libsdl.org/t/sdl-wayland-avoid-dereferencing-a-nil-data-offer/69254 "2026-09-08T15:27:06Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![SDLPushPostBot](https://discourse.libsdl.org/user_avatar/discourse.libsdl.org/sdlpushpostbot/32/4370_2.png) [@SDLPushPostBot](https://discourse.libsdl.org/u/SDLPushPostBot)\
**Post date:** [September 8, 2026, 3:27pm UTC](https://discourse.libsdl.org/t/sdl-wayland-avoid-dereferencing-a-nil-data-offer/69254/1 "2026-09-08T15:27:06Z")

</div>

> <https://github.com/libsdl-org/SDL/commit/84cd92d7055ca74c17a29d7b84aaab598f86bff9>
>
> Pass the data device to the selection offer update functions instead of pulling …it from the offer, as the offer may be nil.

```diff
From 84cd92d7055ca74c17a29d7b84aaab598f86bff9 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Mon, 7 Sep 2026 11:35:33 -0400
Subject: [PATCH] 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.
---
 src/video/wayland/SDL_waylandclipboard.c | 2 +-
 src/video/wayland/SDL_waylanddatamanager.c | 39 +++++++++++++++-------
 src/video/wayland/SDL_waylanddatamanager.h | 4 +--
 src/video/wayland/SDL_waylandevents.c | 8 +----
 4 files changed, 31 insertions(+), 22 deletions(-)

diff --git a/src/video/wayland/SDL_waylandclipboard.c b/src/video/wayland/SDL_waylandclipboard.c
index 9e786afd228f8..c5aa38318db54 100644
--- a/src/video/wayland/SDL_waylandclipboard.c
+++ b/src/video/wayland/SDL_waylandclipboard.c
@@ -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);
             }
diff --git a/src/video/wayland/SDL_waylanddatamanager.c b/src/video/wayland/SDL_waylanddatamanager.c
index cb54753192da2..f30719c361612 100644
--- a/src/video/wayland/SDL_waylanddatamanager.c
+++ b/src/video/wayland/SDL_waylanddatamanager.c
@@ -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");
diff --git a/src/video/wayland/SDL_waylanddatamanager.h b/src/video/wayland/SDL_waylanddatamanager.h
index 575cf37c123d2..aeba94d86626d 100644
--- a/src/video/wayland/SDL_waylanddatamanager.h
+++ b/src/video/wayland/SDL_waylanddatamanager.h
@@ -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);
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 7c5c35fe482f9..9c597ebfd25cf 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -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 = {

```
