diff options
author | Björn Linse <bjorn.linse@gmail.com> | 2021-03-23 20:09:36 +0100 |
---|---|---|
committer | Björn Linse <bjorn.linse@gmail.com> | 2021-04-04 18:11:48 +0200 |
commit | 3d6831a30a6248b44be86e3b149eaaf4bb05c0a4 (patch) | |
tree | e30d21336ed332f8d36be172e9883d3b0fbff180 | |
parent | 8e496b9dfd5b644f06536afea63e3088b9d83cf8 (diff) | |
download | rneovim-3d6831a30a6248b44be86e3b149eaaf4bb05c0a4.tar.gz rneovim-3d6831a30a6248b44be86e3b149eaaf4bb05c0a4.tar.bz2 rneovim-3d6831a30a6248b44be86e3b149eaaf4bb05c0a4.zip |
api: fix use-after-free in nvim_chan_send
-rw-r--r-- | src/nvim/api/vim.c | 3 | ||||
-rw-r--r-- | src/nvim/channel.c | 32 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 2 | ||||
-rw-r--r-- | test/functional/core/job_spec.lua | 5 |
4 files changed, 26 insertions, 16 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 3ae944de4d..09496f5d0a 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1333,7 +1333,8 @@ void nvim_chan_send(Integer chan, String data, Error *err) return; } - channel_send((uint64_t)chan, data.data, data.size, &error); + channel_send((uint64_t)chan, data.data, data.size, + false, &error); if (error) { api_set_error(err, kErrorTypeValidation, "%s", error); } diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 7a08ba58d0..22eb31513d 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -499,48 +499,54 @@ uint64_t channel_from_stdio(bool rpc, CallbackReader on_output, } /// @param data will be consumed -size_t channel_send(uint64_t id, char *data, size_t len, const char **error) +size_t channel_send(uint64_t id, char *data, size_t len, + bool data_owned, const char **error) { Channel *chan = find_channel(id); + size_t written = 0; if (!chan) { *error = _(e_invchan); - goto err; + goto retfree; } if (chan->streamtype == kChannelStreamStderr) { if (chan->stream.err.closed) { *error = _("Can't send data to closed stream"); - goto err; + goto retfree; } // unbuffered write - size_t written = fwrite(data, len, 1, stderr); - xfree(data); - return len * written; + written = len * fwrite(data, len, 1, stderr); + goto retfree; } if (chan->streamtype == kChannelStreamInternal && chan->term) { terminal_receive(chan->term, data, len); - return len; + written = len; + goto retfree; } Stream *in = channel_instream(chan); if (in->closed) { *error = _("Can't send data to closed stream"); - goto err; + goto retfree; } if (chan->is_rpc) { *error = _("Can't send raw data to rpc channel"); - goto err; + goto retfree; } - WBuffer *buf = wstream_new_buffer(data, len, 1, xfree); + // write can be delayed indefinitely, so always use an allocated buffer + WBuffer *buf = wstream_new_buffer(data_owned ? data : xmemdup(data, len), + len, 1, xfree); return wstream_write(in, buf) ? len : 0; -err: - xfree(data); - return 0; +retfree: + if (data_owned) { + xfree(data); + } + return written; } /// Convert binary byte array to a readfile()-style list diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 51e5a27348..a51618b994 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -916,7 +916,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } uint64_t id = argvars[0].vval.v_number; const char *error = NULL; - rettv->vval.v_number = channel_send(id, input, input_len, &error); + rettv->vval.v_number = channel_send(id, input, input_len, true, &error); if (error) { EMSG(error); } diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index b59d87eb12..9de0d08e79 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -963,8 +963,10 @@ describe('jobs', function() return rv end + local j local function send(str) - nvim('command', 'call jobsend(j, "'..str..'")') + -- check no nvim_chan_free double free with pty job (#14198) + meths.chan_send(j, str) end before_each(function() @@ -979,6 +981,7 @@ describe('jobs', function() nvim('command', 'let g:job_opts.pty = 1') nvim('command', 'let exec = [expand("<cfile>:p")]') nvim('command', "let j = jobstart(exec, g:job_opts)") + j = eval'j' eq('tty ready', next_chunk()) end) |