[Bf-blender-cvs] [28f852ccc39] master: Fix T98758: Crash setting the clipboard in Wayland

Campbell Barton noreply at git.blender.org
Fri Jun 10 10:44:22 CEST 2022


Commit: 28f852ccc396eff600467b150b279ada43c00d6b
Author: Campbell Barton
Date:   Fri Jun 10 18:24:26 2022 +1000
Branches: master
https://developer.blender.org/rB28f852ccc396eff600467b150b279ada43c00d6b

Fix T98758: Crash setting the clipboard in Wayland

Accessing the clipboard in wayland wasn't thread safe,
use locks for the clipboard, drag & drop data access and when
setting the systems clipboard.

===================================================================

M	intern/ghost/intern/GHOST_SystemWayland.cpp

===================================================================

diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp
index b9696ab6354..c2240e34890 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -37,6 +37,7 @@
 #include <unistd.h>
 
 #include <cstring>
+#include <mutex>
 
 static GHOST_IWindow *get_window(struct wl_surface *surface);
 
@@ -91,8 +92,6 @@ struct data_offer_t {
 
 struct data_source_t {
   struct wl_data_source *data_source;
-  /** Last device that was active. */
-  uint32_t source_serial;
   char *buffer_out;
 };
 
@@ -138,10 +137,17 @@ struct input_t {
   struct wl_data_device *data_device = nullptr;
   /** Drag & Drop. */
   struct data_offer_t *data_offer_dnd;
+  std::mutex data_offer_dnd_lock;
+
   /** Copy & Paste. */
   struct data_offer_t *data_offer_copy_paste;
+  std::mutex data_offer_copy_paste_mutex;
 
   struct data_source_t *data_source;
+  std::mutex data_source_mutex;
+
+  /** Last device that was active. */
+  uint32_t data_source_serial;
 };
 
 struct display_t {
@@ -174,6 +180,9 @@ struct display_t {
 
 static GHOST_WindowManager *window_manager = nullptr;
 
+/** Check this lock before accessing `GHOST_SystemWayland::selection` from a thread. */
+static std::mutex system_selection_mutex;
+
 /**
  * Callback for WAYLAND to run when there is an error.
  *
@@ -509,6 +518,7 @@ static const zwp_relative_pointer_v1_listener relative_pointer_listener = {
 
 static void dnd_events(const input_t *const input, const GHOST_TEventType event)
 {
+  /* NOTE: `input->data_offer_dnd_lock` must already be locked. */
   const uint64_t time = input->system->getMilliSeconds();
   GHOST_IWindow *const window = static_cast<GHOST_WindowWayland *>(
       wl_surface_get_user_data(input->focus_dnd));
@@ -523,7 +533,9 @@ static void dnd_events(const input_t *const input, const GHOST_TEventType event)
   }
 }
 
-static std::string read_pipe(data_offer_t *data_offer, const std::string mime_receive)
+static std::string read_pipe(data_offer_t *data_offer,
+                             const std::string mime_receive,
+                             std::mutex *mutex)
 {
   int pipefd[2];
   if (pipe(pipefd) != 0) {
@@ -532,6 +544,13 @@ static std::string read_pipe(data_offer_t *data_offer, const std::string mime_re
   wl_data_offer_receive(data_offer->id, mime_receive.c_str(), pipefd[1]);
   close(pipefd[1]);
 
+  data_offer->in_use.store(false);
+
+  if (mutex) {
+    mutex->unlock();
+  }
+  /* WARNING: `data_offer` may be freed from now on. */
+
   std::string data;
   ssize_t len;
   char buffer[4096];
@@ -539,7 +558,6 @@ static std::string read_pipe(data_offer_t *data_offer, const std::string mime_re
     data.insert(data.end(), buffer, buffer + len);
   }
   close(pipefd[0]);
-  data_offer->in_use.store(false);
 
   return data;
 }
@@ -562,7 +580,10 @@ static void data_source_send(void *data,
                              const char * /*mime_type*/,
                              int32_t fd)
 {
-  const char *const buffer = static_cast<char *>(data);
+  input_t *input = static_cast<input_t *>(data);
+  std::lock_guard lock{input->data_source_mutex};
+
+  const char *const buffer = input->data_source->buffer_out;
   if (write(fd, buffer, strlen(buffer)) < 0) {
     GHOST_PRINT("error writing to clipboard: " << std::strerror(errno) << std::endl);
   }
@@ -679,6 +700,8 @@ static void data_device_enter(void *data,
                               struct wl_data_offer *id)
 {
   input_t *input = static_cast<input_t *>(data);
+  std::lock_guard lock{input->data_offer_dnd_lock};
+
   input->data_offer_dnd = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id));
   data_offer_t *data_offer = input->data_offer_dnd;
 
@@ -702,6 +725,7 @@ static void data_device_enter(void *data,
 static void data_device_leave(void *data, struct wl_data_device * /*wl_data_device*/)
 {
   input_t *input = static_cast<input_t *>(data);
+  std::lock_guard lock{input->data_offer_dnd_lock};
 
   dnd_events(input, GHOST_kEventDraggingExited);
   input->focus_dnd = nullptr;
@@ -720,6 +744,8 @@ static void data_device_motion(void *data,
                                wl_fixed_t y)
 {
   input_t *input = static_cast<input_t *>(data);
+  std::lock_guard lock{input->data_offer_dnd_lock};
+
   input->data_offer_dnd->dnd.x = wl_fixed_to_int(x);
   input->data_offer_dnd->dnd.y = wl_fixed_to_int(y);
   dnd_events(input, GHOST_kEventDraggingUpdated);
@@ -728,6 +754,8 @@ static void data_device_motion(void *data,
 static void data_device_drop(void *data, struct wl_data_device * /*wl_data_device*/)
 {
   input_t *input = static_cast<input_t *>(data);
+  std::lock_guard lock{input->data_offer_dnd_lock};
+
   data_offer_t *data_offer = input->data_offer_dnd;
 
   const std::string mime_receive = *std::find_first_of(mime_preference_order.begin(),
@@ -742,7 +770,7 @@ static void data_device_drop(void *data, struct wl_data_device * /*wl_data_devic
     const int x = data_offer->dnd.x;
     const int y = data_offer->dnd.y;
 
-    const std::string data = read_pipe(data_offer, mime_receive);
+    const std::string data = read_pipe(data_offer, mime_receive, nullptr);
 
     wl_data_offer_finish(data_offer->id);
     wl_data_offer_destroy(data_offer->id);
@@ -808,6 +836,9 @@ static void data_device_selection(void *data,
                                   struct wl_data_offer *id)
 {
   input_t *input = static_cast<input_t *>(data);
+
+  std::lock_guard lock{input->data_offer_copy_paste_mutex};
+
   data_offer_t *data_offer = input->data_offer_copy_paste;
 
   /* Delete old data offer. */
@@ -825,22 +856,28 @@ static void data_device_selection(void *data,
   data_offer = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id));
   input->data_offer_copy_paste = data_offer;
 
-  std::string mime_receive;
-  for (const std::string type : {mime_text_utf8, mime_text_plain}) {
-    if (data_offer->types.count(type)) {
-      mime_receive = type;
-      break;
+  auto read_selection = [](input_t *input) {
+    GHOST_SystemWayland *const system = input->system;
+    input->data_offer_copy_paste_mutex.lock();
+
+    data_offer_t *data_offer = input->data_offer_copy_paste;
+    std::string mime_receive;
+    for (const std::string type : {mime_text_utf8, mime_text_plain}) {
+      if (data_offer->types.count(type)) {
+        mime_receive = type;
+        break;
+      }
     }
-  }
+    const std::string data = read_pipe(
+        data_offer, mime_receive, &input->data_offer_copy_paste_mutex);
 
-  auto read_selection = [](GHOST_SystemWayland *const system,
-                           data_offer_t *data_offer,
-                           const std::string mime_receive) {
-    const std::string data = read_pipe(data_offer, mime_receive);
-    system->setSelection(data);
+    {
+      std::lock_guard lock{system_selection_mutex};
+      system->setSelection(data);
+    }
   };
 
-  std::thread read_thread(read_selection, input->system, data_offer, mime_receive);
+  std::thread read_thread(read_selection, input);
   read_thread.detach();
 }
 
@@ -1066,7 +1103,7 @@ static void pointer_button(void *data,
       break;
   }
 
-  input->data_source->source_serial = serial;
+  input->data_source_serial = serial;
   input->buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED);
   input->system->pushEvent(new GHOST_EventButton(
       input->system->getMilliSeconds(), etype, win, ebutton, GHOST_TABLET_DATA_NONE));
@@ -1241,7 +1278,7 @@ static void keyboard_key(void *data,
     key_data.utf8_buf[0] = '\0';
   }
 
-  input->data_source->source_serial = serial;
+  input->data_source_serial = serial;
 
   GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
       wl_surface_get_user_data(input->focus_keyboard));
@@ -1642,7 +1679,11 @@ void GHOST_SystemWayland::putClipboard(const char *buffer, bool /*selection*/) c
     return;
   }
 
-  data_source_t *data_source = d->inputs[0]->data_source;
+  input_t *input = d->inputs[0];
+
+  std::lock_guard lock{input->data_source_mutex};
+
+  data_source_t *data_source = input->data_source;
 
   /* Copy buffer. */
   free(data_source->buffer_out);
@@ -1652,16 +1693,15 @@ void GHOST_SystemWayland::putClipboard(const char *buffer, bool /*selection*/) c
 
   data_source->data_source = wl_data_device_manager_create_data_source(d->data_device_manager);
 
-  wl_data_source_add_listener(
-      data_source->data_source, &data_source_listener, data_source->buffer_out);
+  wl_data_source_add_listener(data_source->data_source, &data_source_listener, input);
 
   for (const std::string &type : mime_send) {
     wl_data_source_offer(data_source->data_source, type.c_str());
   }
 
-  if (!d->inputs.empty() && d->inputs[0]->data_device) {
+  if (input->data_device) {
     wl_data_device_set_selection(
-        d->inputs[0]->data_device, data_source->data_source, data_source->source_serial);
+        input->data_device, data_source->data_source, input->data_source_serial);
   }
 }



More information about the Bf-blender-cvs mailing list