Skip to content

Commit 3bece9f

Browse files
committed
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.
1 parent 71b0f7b commit 3bece9f

5 files changed

Lines changed: 53 additions & 9 deletions

File tree

src/channel.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,8 +1217,6 @@ channel_close(channel_T *channel)
12171217
#endif
12181218

12191219
channel->ch_close_cb = NULL;
1220-
vim_free(channel->ch_callback);
1221-
channel->ch_callback = NULL;
12221220
channel_clear(channel);
12231221
}
12241222

@@ -1298,6 +1296,9 @@ channel_clear(channel_T *channel)
12981296
free_tv(json_head->jq_next->jq_value);
12991297
remove_json_node(json_head, json_head->jq_next);
13001298
}
1299+
1300+
vim_free(channel->ch_callback);
1301+
channel->ch_callback = NULL;
13011302
}
13021303

13031304
#if defined(EXITFREE) || defined(PROTO)
@@ -1802,15 +1803,28 @@ channel_select_check(int ret_in, void *rfds_in)
18021803
int
18031804
channel_parse_messages(void)
18041805
{
1805-
channel_T *channel;
1806-
int ret = FALSE;
1806+
channel_T *channel = first_channel;
1807+
int ret = FALSE;
1808+
int r;
18071809

1808-
for (channel = first_channel; channel != NULL; channel = channel->ch_next)
1809-
while (may_invoke_callback(channel) == OK)
1810+
while (channel != NULL)
1811+
{
1812+
/* Increase the refcount, in case the handler causes the channel to be
1813+
* unreferenced or closed. */
1814+
++channel->ch_refcount;
1815+
r = may_invoke_callback(channel);
1816+
if (channel_unref(channel))
1817+
/* channel was freed, start over */
1818+
channel = first_channel;
1819+
1820+
if (r == OK)
18101821
{
1811-
channel = first_channel; /* start over */
1822+
channel = first_channel; /* something was done, start over */
18121823
ret = TRUE;
18131824
}
1825+
else
1826+
channel = channel->ch_next;
1827+
}
18141828
return ret;
18151829
}
18161830

src/eval.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7730,12 +7730,21 @@ get_dict_tv(char_u **arg, typval_T *rettv, int evaluate)
77307730
return OK;
77317731
}
77327732

7733-
#ifdef FEAT_CHANNEL
7734-
static void
7733+
#if defined(FEAT_CHANNEL) || defined(PROTO)
7734+
/*
7735+
* Decrement the reference count on "channel" and free it when it goes down to
7736+
* zero.
7737+
* Returns TRUE when the channel was freed.
7738+
*/
7739+
int
77357740
channel_unref(channel_T *channel)
77367741
{
77377742
if (channel != NULL && --channel->ch_refcount <= 0)
7743+
{
77387744
channel_free(channel);
7745+
return TRUE;
7746+
}
7747+
return FALSE;
77397748
}
77407749
#endif
77417750

src/proto/eval.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ int dict_add_list(dict_T *d, char *key, list_T *list);
7979
dictitem_T *dict_find(dict_T *d, char_u *key, int len);
8080
char_u *get_dict_string(dict_T *d, char_u *key, int save);
8181
long get_dict_number(dict_T *d, char_u *key);
82+
int channel_unref(channel_T *channel);
8283
int string2float(char_u *text, float_T *value);
8384
char_u *get_function_name(expand_T *xp, int idx);
8485
char_u *get_expr_name(expand_T *xp, int idx);

src/testdir/test_channel.vim

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,21 @@ func Test_pipe()
295295
call job_stop(job)
296296
endtry
297297
endfunc
298+
299+
let s:unletResponse = ''
300+
func s:UnletHandler(handle, msg)
301+
let s:unletResponse = a:msg
302+
unlet s:channelfd
303+
endfunc
304+
305+
" Test that "unlet handle" in a handler doesn't crash Vim.
306+
func s:unlet_handle(port)
307+
let s:channelfd = ch_open('localhost:' . a:port, s:chopt)
308+
call ch_sendexpr(s:channelfd, "test", function('s:UnletHandler'))
309+
sleep 10m
310+
call assert_equal('what?', s:unletResponse)
311+
endfunc
312+
313+
func Test_unlet_handle()
314+
call s:run_server('s:unlet_handle')
315+
endfunc

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,8 @@ static char *(features[]) =
747747

748748
static int included_patches[] =
749749
{ /* Add new patch number below this line */
750+
/**/
751+
1322,
750752
/**/
751753
1321,
752754
/**/

0 commit comments

Comments
 (0)