diff src/channel.c @ 8055:6db4b1c863ec v7.4.1322

commit https://github.com/vim/vim/commit/3bece9fee9c02934d3e295b29d253e13d4ef26a7 Author: Bram Moolenaar <Bram@vim.org> Date: Mon Feb 15 20:39:46 2016 +0100 patch 7.4.1322 Problem: Crash when unletting the variable that holds the channel in a callback function. (Christian Robinson) Solution: Increase the reference count while invoking the callback.
author Christian Brabandt <cb@256bit.org>
date Mon, 15 Feb 2016 20:45:04 +0100
parents 15253130abd8
children 19304db153bc
line wrap: on
line diff
--- a/src/channel.c
+++ b/src/channel.c
@@ -1217,8 +1217,6 @@ channel_close(channel_T *channel)
 #endif
 
     channel->ch_close_cb = NULL;
-    vim_free(channel->ch_callback);
-    channel->ch_callback = NULL;
     channel_clear(channel);
 }
 
@@ -1298,6 +1296,9 @@ channel_clear(channel_T *channel)
 	free_tv(json_head->jq_next->jq_value);
 	remove_json_node(json_head, json_head->jq_next);
     }
+
+    vim_free(channel->ch_callback);
+    channel->ch_callback = NULL;
 }
 
 #if defined(EXITFREE) || defined(PROTO)
@@ -1802,15 +1803,28 @@ channel_select_check(int ret_in, void *r
     int
 channel_parse_messages(void)
 {
-    channel_T *channel;
-    int	    ret = FALSE;
+    channel_T	*channel = first_channel;
+    int		ret = FALSE;
+    int		r;
 
-    for (channel = first_channel; channel != NULL; channel = channel->ch_next)
-	while (may_invoke_callback(channel) == OK)
+    while (channel != NULL)
+    {
+	/* Increase the refcount, in case the handler causes the channel to be
+	 * unreferenced or closed. */
+	++channel->ch_refcount;
+	r = may_invoke_callback(channel);
+	if (channel_unref(channel))
+	    /* channel was freed, start over */
+	    channel = first_channel;
+
+	if (r == OK)
 	{
-	    channel = first_channel;  /* start over */
+	    channel = first_channel;  /* something was done, start over */
 	    ret = TRUE;
 	}
+	else
+	    channel = channel->ch_next;
+    }
     return ret;
 }