Blame SOURCES/fltk-1.3.4-fix-segfault-if-using-very-large-selections.patch

694158
From 5e46e3380887dcd30dc7ea582496bbd9a6a4d70c Mon Sep 17 00:00:00 2001
694158
From: Albrecht Schlosser <albrechts.fltk@online.de>
694158
Date: Fri, 1 Jul 2022 18:49:57 +0200
694158
Subject: [PATCH] Fix "Segfault if using very large selections" (issue 451)
694158
694158
Backported from 'master' (fltk 1.4.0). For more information see
694158
  commit c5556291624eec58ed9de186474dfcc858dca691 and issue 451
694158
  https://github.com/fltk/fltk/issues/451
694158
---
694158
 src/Fl_x.cxx | 66 +++++++++++++++++++++++++++++++++++++++-------------
694158
 1 file changed, 50 insertions(+), 16 deletions(-)
694158
694158
diff --git a/src/Fl_x.cxx b/src/Fl_x.cxx
694158
index 7c03d33..c8b9c06 100644
694158
--- a/src/Fl_x.cxx
694158
+++ b/src/Fl_x.cxx
694158
@@ -1273,19 +1273,24 @@ static bool getNextEvent(XEvent *event_return)
694158
   return true;
694158
 }
694158
 
694158
-static long getIncrData(uchar* &data, const XSelectionEvent& selevent, long lower_bound)
694158
-{
694158
-//fprintf(stderr,"Incremental transfer starting due to INCR property\n");
694158
+static long getIncrData(uchar* &data, const XSelectionEvent& selevent, size_t lower_bound) {
694158
+  // fprintf(stderr,"Incremental transfer starting due to INCR property\n");
694158
   size_t total = 0;
694158
   XEvent event;
694158
-  XDeleteProperty(fl_display, selevent.requestor, selevent.property);  
694158
+  XDeleteProperty(fl_display, selevent.requestor, selevent.property);
694158
   data = (uchar*)realloc(data, lower_bound);
694158
-  for (;;)
694158
-  {
694158
-    if (!getNextEvent(&event)) break;
694158
-    if (event.type == PropertyNotify)
694158
-    {
694158
-      if (event.xproperty.state != PropertyNewValue) continue;
694158
+  if (!data) {
694158
+    // fprintf(stderr, "[getIncrData:%d] realloc() FAILED, size = %ld\n", __LINE__, lower_bound);
694158
+    Fl::fatal("Clipboard data transfer failed, size %ld too large.", lower_bound);
694158
+  }
694158
+  for (;;) {
694158
+    if (!getNextEvent(&event)) {
694158
+      // This is unexpected but may happen if the sender (clipboard owner) no longer sends data
694158
+      // fprintf(stderr, "[getIncrData:%d] Failed to get next event (timeout) *** break! ***\n", __LINE__);
694158
+      break;
694158
+    }
694158
+    if (event.type == PropertyNotify) {
694158
+      if (event.xproperty.state != PropertyNewValue) continue; // ignore PropertyDelete
694158
       Atom actual_type;
694158
       int actual_format;
694158
       unsigned long nitems;
694158
@@ -1300,15 +1305,39 @@ static long getIncrData(uchar* &data, const XSelectionEvent& selevent, long lowe
694158
 			   AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop);
694158
 	num_bytes = nitems * (actual_format / 8);
694158
 	offset += num_bytes/4;
694158
-	//slice_size += num_bytes;
694158
-	if (total + num_bytes > (size_t)lower_bound) data = (uchar*)realloc(data, total + num_bytes);
694158
-	memcpy(data + total, prop, num_bytes); total += num_bytes;
694158
+	// slice_size += num_bytes;
694158
+        if (total + num_bytes > lower_bound) {
694158
+	  data = (uchar*)realloc(data, total + num_bytes);
694158
+          if (!data) {
694158
+            // fprintf(stderr, "[getIncrData():%d] realloc() FAILED, size = %ld\n", __LINE__, total + num_bytes);
694158
+            Fl::fatal("Clipboard data transfer failed, size %ld too large.", total + num_bytes);
694158
+	  }
694158
+	}
694158
+        memcpy(data + total, prop, num_bytes);
694158
+        total += num_bytes;
694158
 	if (prop) XFree(prop);
694158
       } while (bytes_after != 0);
694158
 //fprintf(stderr,"INCR data size:%ld\n", slice_size);
694158
       if (num_bytes == 0) break;
694158
     }
694158
-    else break;
694158
+    else {
694158
+      // Unexpected next event. At this point we're handling the INCR protocol and can't deal with
694158
+      // *some* other events due to potential recursions. We *could* call fl_handle(event) to handle
694158
+      // *selected* other events but for the time being we ignore all other events!
694158
+      // Handling the INCR protocol for very large data may take some time and multiple events.
694158
+      // Interleaving "other" events are possible, for instance the KeyRelease event of the
694158
+      // ctrl/v key pressed to insert the clipboard. This solution is not perfect but it can
694158
+      // handle the INCR protocol with very large selections in most cases, although with potential
694158
+      // side effects because other events may be ignored.
694158
+      // See GitHub Issue #451: "Segfault if using very large selections".
694158
+      // Note: the "fix" for Issue #451 is basically to use 'continue' rather than 'break'
694158
+      // Debug:
694158
+      // fprintf(stderr,
694158
+      //   "[getIncrData:%d] getNextEvent() returned %d, not PropertyNotify (%d). Event ignored.\n",
694158
+      //   __LINE__, event.type, PropertyNotify);
694158
+
694158
+      continue;
694158
+    }
694158
   }
694158
   XDeleteProperty(fl_display, selevent.requestor, selevent.property);
694158
   return (long)total;
694158
@@ -1376,7 +1405,9 @@ int fl_handle(const XEvent& thisevent)
694158
   case SelectionNotify: {
694158
     static unsigned char* sn_buffer = 0;
694158
     //static const char *buffer_format = 0;
694158
-    if (sn_buffer) {XFree(sn_buffer); sn_buffer = 0;}
694158
+    if (sn_buffer) {
694158
+      free(sn_buffer); sn_buffer = 0;
694158
+    }
694158
     long bytesread = 0;
694158
     if (fl_xevent->xselection.property) for (;;) {
694158
       // The Xdnd code pastes 64K chunks together, possibly to avoid
694158
@@ -1457,7 +1488,10 @@ fprintf(stderr,"\n");*/
694158
 	return true;
694158
       }
694158
 	if (actual == fl_INCR) {
694158
-	  bytesread = getIncrData(sn_buffer, xevent.xselection, *(long*)portion);
694158
+	  // an X11 "integer" (32 bit), the "lower bound" of the clipboard size (see ICCCM)
694158
+	  size_t lower_bound = (*(unsigned long *)portion) & 0xFFFFFFFF;
694158
+	  // fprintf(stderr, "[fl_handle:%d] INCR: lower_bound = %ld\n", __LINE__, lower_bound);
694158
+	  bytesread = getIncrData(sn_buffer, xevent.xselection, lower_bound);
694158
 	  XFree(portion);
694158
 	  break;
694158
 	}