On Sat, Aug 22, 2015 at 10:04 AM, Bram Moolenaar <b...@moolenaar.net> wrote:

>
> James Kolb wrote:
>
> > On Friday, August 21, 2015 at 1:39:10 PM UTC-4, Bram Moolenaar wrote:
> > > James Kolb wrote:
> > >
> > > > The current regex code may call mch_breakcheck which can process X
> events.
> > > > One of these events could be a remote_expr that makes its own call
> into the
> > > > regex engine. This usually crashes because the regex engine isn't
> > > > reentrant. This is probably also a problem for anything else that
> runs long
> > > > enough to make breakchecks and isn't reentrant.
> > > >
> > > > This crash can usually be reproduced in linux by running vim with a
> > > > --servername argument and typing the command:
> > > > :call system("sleep 1 && vim --servername ".v:servername."
> --remote-expr
> > > > 'substitute(string(range(5000)), \"a\", \"b\", \"g\")' &") | call
> > > > substitute(string(range(5000)), '\(,.*\)\@<!,', '', 'g')
> > > >
> > > > The attached patch fixes the problem by preventing RealWaitForChar
> from
> > > > processing X events if it is called from mch_breakcheck.
> > >
> > > Thanks for the patch.  However, I think the proper solution would be to
> > > make the regexp code reentrant.  That means getting rid of the global
> > > variables.  It's been messy like that for a long time.
> > >
> > > I think your patch fixes one specific situation, but it can happen in
> > > other situations as well.
> >
> > I agree that the regexp code should be reentrant, but I think regexes
> > are just one mine in a larger breakchecks-can-run-arbitrary-code
> > minefield.
> >
> > Most of the places that call breakchecks don't seem to assume that
> > they can call arbitrary vim commands. Commonly run functions like
> > buflist_list() or searchit(), for example, can read-after-free if
> > somebody deletes a buffer using remote-expr. Anything that calls the
> > regex engine will have the same problem that the regex engine can call
> > arbitrary commands, even if the regex engine were reentrant.
>
> OK, thus even though your patch refers to regexp, it has a wider target.
>
> I'm not sure if your patch also introduces a problem.  If we don't
> handle X events while busy doing something, the application becomes
> unresponsive from an X server point of view.  E.g. for releasing the
> selection.
>
> If we are calling breakcheck, then we do not expect all kinds of things
> to happen.  Some kind of global lock, or a queue to put postponed
> commands on would work better.  The netbeans feature already does this,
> we could build on top of it.  Specifically netbeans_parse_messages().
>

I wrote a new patch that adds queuing for clientserver similar to the
netbeans queue.

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 7633d54..5fc8014 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -8984,11 +8984,11 @@ do_sleep(msec)
     {
        ui_delay(msec - done > 1000L ? 1000L : msec - done, TRUE);
        ui_breakcheck();
-#ifdef FEAT_NETBEANS_INTG
-       /* Process the netbeans messages that may have been received in the
-        * call to ui_breakcheck() when the GUI is in use. This may occur when
-        * running a test case. */
-       netbeans_parse_messages();
+#if (defined(FEAT_NETBEANS_INTG) || defined(FEAT_CLIENTSERVER))
+       /* Process the netbeans and clientserver messages that may have been
+        * received in the call to ui_breakcheck() when the GUI is in use. This
+        * may occur when running a test case. */
+       parse_queued_messages();
 #endif
     }
 }
diff --git a/src/getchar.c b/src/getchar.c
index a80432f..31128ec 100644
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -3034,9 +3034,8 @@ inchar(buf, maxlen, wait_time, tb_change_cnt)
            )
     {
 
-#if defined(FEAT_NETBEANS_INTG)
-       /* Process the queued netbeans messages. */
-       netbeans_parse_messages();
+#if (defined(FEAT_NETBEANS_INTG) || defined(FEAT_CLIENTSERVER))
+       parse_queued_messages();
 #endif
 
        if (got_int || (script_char = getc(scriptin[curscript])) < 0)
diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c
index 5538446..910508e 100644
--- a/src/gui_gtk_x11.c
+++ b/src/gui_gtk_x11.c
@@ -650,7 +650,7 @@ property_event(GtkWidget *widget,
        xev.xproperty.atom = commProperty;
        xev.xproperty.window = commWindow;
        xev.xproperty.state = PropertyNewValue;
-       serverEventProc(GDK_WINDOW_XDISPLAY(widget->window), &xev);
+       serverEventProc(GDK_WINDOW_XDISPLAY(widget->window), &xev, 0);
     }
     return FALSE;
 }
@@ -5471,9 +5471,8 @@ gui_mch_wait_for_chars(long wtime)
            focus = gui.in_focus;
        }
 
-#if defined(FEAT_NETBEANS_INTG)
-       /* Process any queued netbeans messages. */
-       netbeans_parse_messages();
+#if (defined(FEAT_NETBEANS_INTG) || defined(FEAT_CLIENTSERVER))
+       parse_queued_messages();
 #endif
 
        /*
diff --git a/src/gui_w48.c b/src/gui_w48.c
index 1096ec8..e56a17c 100644
--- a/src/gui_w48.c
+++ b/src/gui_w48.c
@@ -2016,9 +2016,8 @@ gui_mch_wait_for_chars(int wtime)
            s_need_activate = FALSE;
        }
 
-#ifdef FEAT_NETBEANS_INTG
-       /* Process the queued netbeans messages. */
-       netbeans_parse_messages();
+#if (defined(FEAT_NETBEANS_INTG) || defined(FEAT_CLIENTSERVER))
+       parse_queued_messages();
 #endif
 
        /*
diff --git a/src/gui_x11.c b/src/gui_x11.c
index ed71b26..8b7dad5 100644
--- a/src/gui_x11.c
+++ b/src/gui_x11.c
@@ -2895,9 +2895,8 @@ gui_mch_wait_for_chars(wtime)
            focus = gui.in_focus;
        }
 
-#if defined(FEAT_NETBEANS_INTG)
-       /* Process any queued netbeans messages. */
-       netbeans_parse_messages();
+#if (defined(FEAT_NETBEANS_INTG) || defined(FEAT_CLIENTSERVER))
+       parse_queued_messages();
 #endif
 
        /*
@@ -3199,7 +3198,7 @@ gui_x11_send_event_handler(w, client_data, event, dum)
     if (e->type == PropertyNotify && e->window == commWindow
            && e->atom == commProperty &&  e->state == PropertyNewValue)
     {
-       serverEventProc(gui.dpy, event);
+       serverEventProc(gui.dpy, event, 0);
     }
 }
 #endif
diff --git a/src/if_xcmdsrv.c b/src/if_xcmdsrv.c
index d8c64ad..11a8ee8 100644
--- a/src/if_xcmdsrv.c
+++ b/src/if_xcmdsrv.c
@@ -169,6 +169,18 @@ enum ServerReplyOp { SROP_Find, SROP_Add, SROP_Delete };
 
 typedef int (*EndCond) __ARGS((void *));
 
+struct x_cmdqueue
+{
+    char_u             *buffer;
+    int                        len;
+    struct x_cmdqueue  *next;
+    struct x_cmdqueue  *prev;
+};
+
+typedef struct x_cmdqueue x_queue_T;
+
+static x_queue_T head;  /* dummy node, header for circular queue */
+
 /*
  * Forward declarations for procedures defined later in this file:
  */
@@ -186,6 +198,8 @@ static struct ServerReply *ServerReplyFind __ARGS((Window 
w, enum ServerReplyOp
 static int     AppendPropCarefully __ARGS((Display *display, Window window, 
Atom property, char_u *value, int length));
 static int     x_error_check __ARGS((Display *dpy, XErrorEvent *error_event));
 static int     IsSerialName __ARGS((char_u *name));
+static void    save_in_queue __ARGS((char_u *buf, int len));
+static void    server_parse_message __ARGS((Display *dpy, char_u* propInfo, 
int numItems));
 
 /* Private variables for the "server" functionality */
 static Atom    registryProperty = None;
@@ -595,7 +609,7 @@ ServerWait(dpy, w, endCond, endData, localLoop, seconds)
     while (TRUE)
     {
        while (XCheckWindowEvent(dpy, commWindow, PropertyChangeMask, &event))
-           serverEventProc(dpy, &event);
+           serverEventProc(dpy, &event, 1);
 
        if (endCond(endData) != 0)
            break;
@@ -1127,22 +1141,25 @@ GetRegProp(dpy, regPropp, numItemsp, domsg)
     return OK;
 }
 
+
 /*
  * This procedure is invoked by the various X event loops throughout Vims when
  * a property changes on the communication window.  This procedure reads the
- * property and handles command requests and responses.
+ * property and enqueues command requests and responses. If immediate is true,
+ * it runs the event immediatly instead of enqueuing it. Immediate can cause
+ * unintended behavior and should only be used for code that blocks for a
+ * response.
  */
     void
-serverEventProc(dpy, eventPtr)
+serverEventProc(dpy, eventPtr, immediate)
     Display    *dpy;
     XEvent     *eventPtr;              /* Information about event. */
+    int immediate;                      /* Run event immediately. Should 
usually be 0. */
 {
     char_u     *propInfo;
-    char_u     *p;
-    int                result, actualFormat, code;
+    int                result, actualFormat;
     long_u     numItems, bytesAfter;
     Atom       actualType;
-    char_u     *tofree;
 
     if (eventPtr != NULL)
     {
@@ -1168,7 +1185,99 @@ serverEventProc(dpy, eventPtr)
            XFree(propInfo);
        return;
     }
+    if (immediate)
+    {
+       server_parse_message(dpy, propInfo, numItems);
+    }
+    else
+    {
+       save_in_queue(propInfo, numItems);
+    }
+}
+
+/*
+ * Saves x clientserver commands in a queue so that they can be called when 
vim is idle.
+ */
+    static void
+save_in_queue(buf, len)
+    char_u  *buf;
+    int            len;
+{
+    x_queue_T *node;
+
+    node = (x_queue_T *)alloc(sizeof(x_queue_T));
+    if (node == NULL)
+       return;     /* out of memory */
+    node->buffer = alloc(len + 1);
+    if (node->buffer == NULL)
+    {
+       vim_free(node);
+       return;     /* out of memory */
+    }
+    mch_memmove(node->buffer, buf, (size_t)len);
+    node->buffer[len] = NUL;
+    node->len = len;
+
+    if (head.next == NULL)   /* initialize circular queue */
+    {
+       head.next = &head;
+       head.prev = &head;
+    }
+
+    /* insert node at tail of queue */
+    node->next = &head;
+    node->prev = head.prev;
+    head.prev->next = node;
+    head.prev = node;
+}
+
+/*
+ * Parses queued clientserver messages.
+ */
+    void
+server_parse_messages()
+{
+    char_u     *p;
+    x_queue_T  *node;
+    int                own_node;
+
+    while (head.next != NULL && head.next != &head)
+    {
+       node = head.next;
+       if(!X_DISPLAY)
+       {
+           return;
+       }
+       server_parse_message(X_DISPLAY, node->buffer, node->len);
+       head.next = node->next;
+       node->next->prev = node->prev;
+       vim_free(node);
+    }
+}
 
+/*
+ * Returns a non-zero value if there are clientserver messages waiting
+ * int the queue.
+ */
+    int
+server_waiting()
+{
+    return head.next != NULL && head.next != &head;
+}
+
+/*
+ * Prases a single clientserver message. A single message may contain multiple
+ * commands.
+ */
+    static void
+server_parse_message(dpy, propInfo, numItems)
+    Display    *dpy;
+    char_u* propInfo; //A string containing 0 or more X commands
+    int numItems; //The size of propInfo in bytes.
+{
+    char_u     *p;
+    int code;
+    char_u     *tofree;
     /*
      * Several commands and results could arrive in the property at
      * one time;  each iteration through the outer loop handles a
diff --git a/src/misc2.c b/src/misc2.c
index 379916b..33c1c9d 100644
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -6328,3 +6328,21 @@ has_non_ascii(s)
     return FALSE;
 }
 #endif
+
+/*
+ * Process messages that have been queued for netbeans or clientserver.
+ * These functions can call arbitrary vimscript and should only be called when
+ * it is safe to do so.
+ */
+    void
+parse_queued_messages()
+{
+#ifdef FEAT_NETBEANS_INTG
+    /* Process the queued netbeans messages. */
+    netbeans_parse_messages();
+#endif
+#ifdef FEAT_CLIENTSERVER
+    /* Process the queued clientserver messages. */
+    server_parse_messages();
+#endif
+}
diff --git a/src/os_unix.c b/src/os_unix.c
index 2439c5e..55812c0 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -388,9 +388,8 @@ mch_inchar(buf, maxlen, wtime, tb_change_cnt)
 {
     int                len;
 
-#ifdef FEAT_NETBEANS_INTG
-    /* Process the queued netbeans messages. */
-    netbeans_parse_messages();
+#if (defined(FEAT_NETBEANS_INTG) || defined(FEAT_CLIENTSERVER))
+    parse_queued_messages();
 #endif
 
     /* Check if window changed size while we were busy, perhaps the ":set
@@ -405,9 +404,8 @@ mch_inchar(buf, maxlen, wtime, tb_change_cnt)
            if (!do_resize)     /* return if not interrupted by resize */
                return 0;
            handle_resize();
-#ifdef FEAT_NETBEANS_INTG
-           /* Process the queued netbeans messages. */
-           netbeans_parse_messages();
+#if (defined(FEAT_NETBEANS_INTG) || defined(FEAT_CLIENTSERVER))
+            parse_queued_messages();
 #endif
        }
     }
@@ -439,9 +437,8 @@ mch_inchar(buf, maxlen, wtime, tb_change_cnt)
        while (do_resize)    /* window changed size */
            handle_resize();
 
-#ifdef FEAT_NETBEANS_INTG
-       /* Process the queued netbeans messages. */
-       netbeans_parse_messages();
+#if (defined(FEAT_NETBEANS_INTG) || defined(FEAT_CLIENTSERVER))
+        parse_queued_messages();
 #endif
        /*
         * We want to be interrupted by the winch signal
@@ -5208,6 +5205,7 @@ WaitForChar(msec)
  * When a GUI is being used, this will not be used for input -- webb
  * Returns also, when a request from Sniff is waiting -- toni.
  * Or when a Linux GPM mouse event is waiting.
+ * Or when a clientserver message is on the queue.
  */
 #if defined(__BEOS__)
     int
@@ -5601,6 +5599,11 @@ select_eintr:
        if (finished || msec == 0)
            break;
 
+#ifdef FEAT_CLIENTSERVER
+        if (server_waiting())
+            break;
+#endif
+
        /* We're going to loop around again, find out for how long */
        if (msec > 0)
        {
@@ -7121,7 +7124,7 @@ xterm_update()
 
                if (e->type == PropertyNotify && e->window == commWindow
                   && e->atom == commProperty && e->state == PropertyNewValue)
-                serverEventProc(xterm_dpy, &event);
+                serverEventProc(xterm_dpy, &event, 0);
            }
 #endif
             XtDispatchEvent(&event);
diff --git a/src/proto/if_xcmdsrv.pro b/src/proto/if_xcmdsrv.pro
index dd6a120..fb937e5 100644
--- a/src/proto/if_xcmdsrv.pro
+++ b/src/proto/if_xcmdsrv.pro
@@ -7,5 +7,5 @@ Window serverStrToWin __ARGS((char_u *str));
 int serverSendReply __ARGS((char_u *name, char_u *str));
 int serverReadReply __ARGS((Display *dpy, Window win, char_u **str, int 
localLoop));
 int serverPeekReply __ARGS((Display *dpy, Window win, char_u **str));
-void serverEventProc __ARGS((Display *dpy, XEvent *eventPtr));
+void serverEventProc __ARGS((Display *dpy, XEvent *eventPtr, int immediate));
 /* vim: set ft=c : */

Raspunde prin e-mail lui