api: fix use-after-free in nvim_chan_send

This commit is contained in:
Björn Linse
2021-03-23 20:09:36 +01:00
parent 8e496b9dfd
commit 3d6831a30a
4 changed files with 26 additions and 16 deletions

View File

@@ -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);
}

View File

@@ -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:
retfree:
if (data_owned) {
xfree(data);
return 0;
}
return written;
}
/// Convert binary byte array to a readfile()-style list

View File

@@ -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);
}

View File

@@ -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)