changeset 11709:c3227699ad4d v8.0.0737

patch 8.0.0737: crash when X11 selection is very big commit https://github.com/vim/vim/commit/cdb7e1b7f9e18a7b165ff09103a9994f84966123 Author: Bram Moolenaar <Bram@vim.org> Date: Wed Jul 19 19:55:58 2017 +0200 patch 8.0.0737: crash when X11 selection is very big Problem: Crash when X11 selection is very big. Solution: Use static items instead of allocating them. Add callbacks. (Ozaki Kiichi)
author Christian Brabandt <cb@256bit.org>
date Wed, 19 Jul 2017 20:00:03 +0200
parents 1bb52ba993d9
children 1a494055c8a1
files src/testdir/shared.vim src/testdir/test_quotestar.vim src/ui.c src/version.c
diffstat 4 files changed, 57 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/shared.vim
+++ b/src/testdir/shared.vim
@@ -111,14 +111,15 @@ endfunc
 " Wait for up to a second for "expr" to become true.
 " Return time slept in milliseconds.  With the +reltime feature this can be
 " more than the actual waiting time.  Without +reltime it can also be less.
-func WaitFor(expr)
+func WaitFor(expr, ...)
+  let timeout = get(a:000, 0, 1000)
   " using reltime() is more accurate, but not always available
   if has('reltime')
     let start = reltime()
   else
     let slept = 0
   endif
-  for i in range(100)
+  for i in range(timeout / 10)
     try
       if eval(a:expr)
 	if has('reltime')
@@ -133,7 +134,7 @@ func WaitFor(expr)
     endif
     sleep 10m
   endfor
-  return 1000
+  return timeout
 endfunc
 
 " Wait for up to a given milliseconds.
--- a/src/testdir/test_quotestar.vim
+++ b/src/testdir/test_quotestar.vim
@@ -88,6 +88,18 @@ func Do_test_quotestar_for_x11()
   call WaitFor('@* == "yes"')
   call assert_equal('yes', @*)
 
+  " Handle the large selection over 262040 byte.
+  let length = 262044
+  let sample = 'a' . repeat('b', length - 2) . 'c'
+  let @* = sample
+  call WaitFor('remote_expr("' . name . '", "len(@*) >= ' . length . '", "", 1)', 3000)
+  let res = remote_expr(name, "@*", "", 2)
+  call assert_equal(length, len(res))
+  " Check length to prevent a large amount of output at assertion failure.
+  if length == len(res)
+    call assert_equal(sample, res)
+  endif
+
   if has('unix') && has('gui') && !has('gui_running')
     let @* = ''
 
--- a/src/ui.c
+++ b/src/ui.c
@@ -2042,10 +2042,11 @@ x11_setup_atoms(Display *dpy)
  * X Selection stuff, for cutting and pasting text to other windows.
  */
 
-static Boolean	clip_x11_convert_selection_cb(Widget, Atom *, Atom *, Atom *, XtPointer *, long_u *, int *);
-static void  clip_x11_lose_ownership_cb(Widget, Atom *);
+static Boolean	clip_x11_convert_selection_cb(Widget w, Atom *sel_atom, Atom *target, Atom *type, XtPointer *value, long_u *length, int *format);
+static void clip_x11_lose_ownership_cb(Widget w, Atom *sel_atom);
+static void clip_x11_notify_cb(Widget w, Atom *sel_atom, Atom *target);
 static void clip_x11_timestamp_cb(Widget w, XtPointer n, XEvent *event, Boolean *cont);
-static void  clip_x11_request_selection_cb(Widget, XtPointer, Atom *, Atom *, XtPointer, long_u *, int *);
+static void clip_x11_request_selection_cb(Widget w, XtPointer success, Atom *sel_atom, Atom *type, XtPointer value, long_u *length, int *format);
 
 /*
  * Property callback to get a timestamp for XtOwnSelection.
@@ -2085,7 +2086,7 @@ clip_x11_timestamp_cb(
     /* Get the selection, using the event timestamp. */
     if (XtOwnSelection(w, xproperty->atom, xproperty->time,
 	    clip_x11_convert_selection_cb, clip_x11_lose_ownership_cb,
-	    NULL) == OK)
+	    clip_x11_notify_cb) == OK)
     {
 	/* Set the "owned" flag now, there may have been a call to
 	 * lose_ownership_cb in between. */
@@ -2276,9 +2277,9 @@ clip_x11_request_selection(
 	start_time = time(NULL);
 	while (success == MAYBE)
 	{
-	    if (XCheckTypedEvent(dpy, SelectionNotify, &event)
-		    || XCheckTypedEvent(dpy, SelectionRequest, &event)
-		    || XCheckTypedEvent(dpy, PropertyNotify, &event))
+	    if (XCheckTypedEvent(dpy, PropertyNotify, &event)
+		    || XCheckTypedEvent(dpy, SelectionNotify, &event)
+		    || XCheckTypedEvent(dpy, SelectionRequest, &event))
 	    {
 		/* This is where clip_x11_request_selection_cb() should be
 		 * called.  It may actually happen a bit later, so we loop
@@ -2331,11 +2332,12 @@ clip_x11_convert_selection_cb(
     long_u	*length,
     int		*format)
 {
-    char_u	*string;
-    char_u	*result;
-    int		motion_type;
-    VimClipboard	*cbd;
-    int		i;
+    static char_u   *save_result = NULL;
+    static long_u   save_length = 0;
+    char_u	    *string;
+    int		    motion_type;
+    VimClipboard    *cbd;
+    int		    i;
 
     if (*sel_atom == clip_plus.sel_atom)
 	cbd = &clip_plus;
@@ -2348,10 +2350,8 @@ clip_x11_convert_selection_cb(
     /* requestor wants to know what target types we support */
     if (*target == targets_atom)
     {
-	Atom *array;
+	static Atom array[7];
 
-	if ((array = (Atom *)XtMalloc((unsigned)(sizeof(Atom) * 7))) == NULL)
-	    return False;
 	*value = (XtPointer)array;
 	i = 0;
 	array[i++] = targets_atom;
@@ -2400,13 +2400,17 @@ clip_x11_convert_selection_cb(
 	*length += STRLEN(p_enc) + 2;
 #endif
 
-    *value = XtMalloc((Cardinal)*length);
-    result = (char_u *)*value;
-    if (result == NULL)
+    if (save_length < *length || save_length / 2 >= *length)
+	*value = XtRealloc((char *)save_result, (Cardinal)*length + 1);
+    else
+	*value = save_result;
+    if (*value == NULL)
     {
 	vim_free(string);
 	return False;
     }
+    save_result = (char_u *)*value;
+    save_length = *length;
 
     if (*target == XA_STRING
 #ifdef FEAT_MBYTE
@@ -2414,13 +2418,13 @@ clip_x11_convert_selection_cb(
 #endif
 	    )
     {
-	mch_memmove(result, string, (size_t)(*length));
+	mch_memmove(save_result, string, (size_t)(*length));
 	*type = *target;
     }
     else if (*target == compound_text_atom || *target == text_atom)
     {
 	XTextProperty	text_prop;
-	char		*string_nt = (char *)alloc((unsigned)*length + 1);
+	char		*string_nt = (char *)save_result;
 	int		conv_result;
 
 	/* create NUL terminated string which XmbTextListToTextProperty wants */
@@ -2428,8 +2432,6 @@ clip_x11_convert_selection_cb(
 	string_nt[*length] = NUL;
 	conv_result = XmbTextListToTextProperty(X_DISPLAY, (char **)&string_nt,
 					   1, XCompoundTextStyle, &text_prop);
-	vim_free(string_nt);
-	XtFree(*value);			/* replace with COMPOUND text */
 	if (conv_result != Success)
 	{
 	    vim_free(string);
@@ -2438,24 +2440,25 @@ clip_x11_convert_selection_cb(
 	*value = (XtPointer)(text_prop.value);	/*    from plain text */
 	*length = text_prop.nitems;
 	*type = compound_text_atom;
+	XtFree((char *)save_result);
+	save_result = (char_u *)*value;
+	save_length = *length;
     }
-
 #ifdef FEAT_MBYTE
     else if (*target == vimenc_atom)
     {
 	int l = STRLEN(p_enc);
 
-	result[0] = motion_type;
-	STRCPY(result + 1, p_enc);
-	mch_memmove(result + l + 2, string, (size_t)(*length - l - 2));
+	save_result[0] = motion_type;
+	STRCPY(save_result + 1, p_enc);
+	mch_memmove(save_result + l + 2, string, (size_t)(*length - l - 2));
 	*type = vimenc_atom;
     }
 #endif
-
     else
     {
-	result[0] = motion_type;
-	mch_memmove(result + 1, string, (size_t)(*length - 1));
+	save_result[0] = motion_type;
+	mch_memmove(save_result + 1, string, (size_t)(*length - 1));
 	*type = vim_atom;
     }
     *format = 8;	    /* 8 bits per char */
@@ -2479,6 +2482,12 @@ clip_x11_lose_selection(Widget myShell, 
 				XtLastTimestampProcessed(XtDisplay(myShell)));
 }
 
+    static void
+clip_x11_notify_cb(Widget w UNUSED, Atom *sel_atom UNUSED, Atom *target UNUSED)
+{
+    /* To prevent automatically freeing the selection value. */
+}
+
     int
 clip_x11_own_selection(Widget myShell, VimClipboard *cbd)
 {
@@ -2492,7 +2501,7 @@ clip_x11_own_selection(Widget myShell, V
 	if (XtOwnSelection(myShell, cbd->sel_atom,
 	       XtLastTimestampProcessed(XtDisplay(myShell)),
 	       clip_x11_convert_selection_cb, clip_x11_lose_ownership_cb,
-	       NULL) == False)
+	       clip_x11_notify_cb) == False)
 	    return FAIL;
     }
     else
--- a/src/version.c
+++ b/src/version.c
@@ -770,6 +770,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    737,
+/**/
     736,
 /**/
     735,