mirror of
https://github.com/neovim/neovim.git
synced 2025-09-20 18:28:19 +00:00
api: fix use-after-free in nvim_chan_send
This commit is contained in:
@@ -1333,7 +1333,8 @@ void nvim_chan_send(Integer chan, String data, Error *err)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
channel_send((uint64_t)chan, data.data, data.size, &error);
|
channel_send((uint64_t)chan, data.data, data.size,
|
||||||
|
false, &error);
|
||||||
if (error) {
|
if (error) {
|
||||||
api_set_error(err, kErrorTypeValidation, "%s", error);
|
api_set_error(err, kErrorTypeValidation, "%s", error);
|
||||||
}
|
}
|
||||||
|
@@ -499,48 +499,54 @@ uint64_t channel_from_stdio(bool rpc, CallbackReader on_output,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// @param data will be consumed
|
/// @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);
|
Channel *chan = find_channel(id);
|
||||||
|
size_t written = 0;
|
||||||
if (!chan) {
|
if (!chan) {
|
||||||
*error = _(e_invchan);
|
*error = _(e_invchan);
|
||||||
goto err;
|
goto retfree;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chan->streamtype == kChannelStreamStderr) {
|
if (chan->streamtype == kChannelStreamStderr) {
|
||||||
if (chan->stream.err.closed) {
|
if (chan->stream.err.closed) {
|
||||||
*error = _("Can't send data to closed stream");
|
*error = _("Can't send data to closed stream");
|
||||||
goto err;
|
goto retfree;
|
||||||
}
|
}
|
||||||
// unbuffered write
|
// unbuffered write
|
||||||
size_t written = fwrite(data, len, 1, stderr);
|
written = len * fwrite(data, len, 1, stderr);
|
||||||
xfree(data);
|
goto retfree;
|
||||||
return len * written;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chan->streamtype == kChannelStreamInternal && chan->term) {
|
if (chan->streamtype == kChannelStreamInternal && chan->term) {
|
||||||
terminal_receive(chan->term, data, len);
|
terminal_receive(chan->term, data, len);
|
||||||
return len;
|
written = len;
|
||||||
|
goto retfree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Stream *in = channel_instream(chan);
|
Stream *in = channel_instream(chan);
|
||||||
if (in->closed) {
|
if (in->closed) {
|
||||||
*error = _("Can't send data to closed stream");
|
*error = _("Can't send data to closed stream");
|
||||||
goto err;
|
goto retfree;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chan->is_rpc) {
|
if (chan->is_rpc) {
|
||||||
*error = _("Can't send raw data to rpc channel");
|
*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;
|
return wstream_write(in, buf) ? len : 0;
|
||||||
|
|
||||||
err:
|
retfree:
|
||||||
|
if (data_owned) {
|
||||||
xfree(data);
|
xfree(data);
|
||||||
return 0;
|
}
|
||||||
|
return written;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert binary byte array to a readfile()-style list
|
/// Convert binary byte array to a readfile()-style list
|
||||||
|
@@ -916,7 +916,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
uint64_t id = argvars[0].vval.v_number;
|
uint64_t id = argvars[0].vval.v_number;
|
||||||
const char *error = NULL;
|
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) {
|
if (error) {
|
||||||
EMSG(error);
|
EMSG(error);
|
||||||
}
|
}
|
||||||
|
@@ -963,8 +963,10 @@ describe('jobs', function()
|
|||||||
return rv
|
return rv
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local j
|
||||||
local function send(str)
|
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
|
end
|
||||||
|
|
||||||
before_each(function()
|
before_each(function()
|
||||||
@@ -979,6 +981,7 @@ describe('jobs', function()
|
|||||||
nvim('command', 'let g:job_opts.pty = 1')
|
nvim('command', 'let g:job_opts.pty = 1')
|
||||||
nvim('command', 'let exec = [expand("<cfile>:p")]')
|
nvim('command', 'let exec = [expand("<cfile>:p")]')
|
||||||
nvim('command', "let j = jobstart(exec, g:job_opts)")
|
nvim('command', "let j = jobstart(exec, g:job_opts)")
|
||||||
|
j = eval'j'
|
||||||
eq('tty ready', next_chunk())
|
eq('tty ready', next_chunk())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user