mirror of
https://github.com/neovim/neovim.git
synced 2025-09-16 08:18:17 +00:00
provider/RPC: apply_autocmds_group(): fix double-free
During provider dispatch, eval_call_provider() saves global
state--including pointers, such as `autocmd_fname`--into
`provider_caller_scope` which is later restored by f_rpcrequest().
But `autocmd_fname` is special-cased in eval_vars(), for performance
(see Vim patch 7.2.021; this is also the singular purpose of the
`autocmd_fname_full` global. Yay!)
If eval_vars() frees `autocmd_fname` then its provider-RPC-scoped alias
becomes a problem.
Solution: Don't free autocmd_fname in eval_vars(), just copy into it.
closes #5245
closes #5617
Reference
------------------------------------------------------------------------
Vim patch 7.2.021
f6dad43c98
Problem: When executing autocommands getting the full file name may be
slow. (David Kotchan)
Solution: Postpone calling FullName_save() until autocmd_fname is used.
vim_dev discussion (2008): "Problem with CursorMoved AutoCommand when
Editing Files on a Remote WIndows Share"
https://groups.google.com/d/msg/vim_dev/kj95weZa_eE/GTgj4aq5sIgJ
This commit is contained in:
@@ -13828,7 +13828,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
save_autocmd_fname_full = autocmd_fname_full;
|
save_autocmd_fname_full = autocmd_fname_full;
|
||||||
save_autocmd_bufnr = autocmd_bufnr;
|
save_autocmd_bufnr = autocmd_bufnr;
|
||||||
save_funccalp = save_funccal();
|
save_funccalp = save_funccal();
|
||||||
//
|
|
||||||
current_SID = provider_caller_scope.SID;
|
current_SID = provider_caller_scope.SID;
|
||||||
sourcing_name = provider_caller_scope.sourcing_name;
|
sourcing_name = provider_caller_scope.sourcing_name;
|
||||||
sourcing_lnum = provider_caller_scope.sourcing_lnum;
|
sourcing_lnum = provider_caller_scope.sourcing_lnum;
|
||||||
@@ -22481,7 +22481,8 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
|
|||||||
restore_funccal(provider_caller_scope.funccalp);
|
restore_funccal(provider_caller_scope.funccalp);
|
||||||
provider_caller_scope = saved_provider_caller_scope;
|
provider_caller_scope = saved_provider_caller_scope;
|
||||||
provider_call_nesting--;
|
provider_call_nesting--;
|
||||||
|
assert(provider_call_nesting >= 0);
|
||||||
|
|
||||||
return rettv;
|
return rettv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8547,15 +8547,16 @@ eval_vars (
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SPEC_AFILE: /* file name for autocommand */
|
case SPEC_AFILE: /* file name for autocommand */
|
||||||
result = autocmd_fname;
|
if (autocmd_fname != NULL && !autocmd_fname_full) {
|
||||||
if (result != NULL && !autocmd_fname_full) {
|
// Still need to turn the fname into a full path. It was
|
||||||
/* Still need to turn the fname into a full path. It is
|
// postponed to avoid a delay when <afile> is not used.
|
||||||
* postponed to avoid a delay when <afile> is not used. */
|
autocmd_fname_full = true;
|
||||||
autocmd_fname_full = TRUE;
|
result = (char_u *)FullName_save((char *)autocmd_fname, false);
|
||||||
result = (char_u *)FullName_save((char *)autocmd_fname, FALSE);
|
// Copy into `autocmd_fname`, don't reassign it. #8165
|
||||||
xfree(autocmd_fname);
|
xstrlcpy((char *)autocmd_fname, (char *)result, MAXPATHL);
|
||||||
autocmd_fname = result;
|
xfree(result);
|
||||||
}
|
}
|
||||||
|
result = autocmd_fname;
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
*errormsg = (char_u *)_(
|
*errormsg = (char_u *)_(
|
||||||
"E495: no autocommand file name to substitute for \"<afile>\"");
|
"E495: no autocommand file name to substitute for \"<afile>\"");
|
||||||
|
@@ -6755,7 +6755,8 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
|
|||||||
autocmd_fname = fname_io;
|
autocmd_fname = fname_io;
|
||||||
}
|
}
|
||||||
if (autocmd_fname != NULL) {
|
if (autocmd_fname != NULL) {
|
||||||
autocmd_fname = vim_strsave(autocmd_fname);
|
// Allocate MAXPATHL for when eval_vars() resolves the fullpath.
|
||||||
|
autocmd_fname = vim_strnsave(autocmd_fname, MAXPATHL);
|
||||||
}
|
}
|
||||||
autocmd_fname_full = false; // call FullName_save() later
|
autocmd_fname_full = false; // call FullName_save() later
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@ local eval, command, feed = helpers.eval, helpers.command, helpers.feed
|
|||||||
local eq, clear, insert = helpers.eq, helpers.clear, helpers.insert
|
local eq, clear, insert = helpers.eq, helpers.clear, helpers.insert
|
||||||
local expect, write_file = helpers.expect, helpers.write_file
|
local expect, write_file = helpers.expect, helpers.write_file
|
||||||
local feed_command = helpers.feed_command
|
local feed_command = helpers.feed_command
|
||||||
|
local source = helpers.source
|
||||||
local missing_provider = helpers.missing_provider
|
local missing_provider = helpers.missing_provider
|
||||||
|
|
||||||
do
|
do
|
||||||
@@ -13,7 +14,7 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe('python3 commands and functions', function()
|
describe('python3 provider', function()
|
||||||
before_each(function()
|
before_each(function()
|
||||||
clear()
|
clear()
|
||||||
command('python3 import vim')
|
command('python3 import vim')
|
||||||
@@ -82,4 +83,20 @@ describe('python3 commands and functions', function()
|
|||||||
it('py3eval', function()
|
it('py3eval', function()
|
||||||
eq({1, 2, {['key'] = 'val'}}, eval([[py3eval('[1, 2, {"key": "val"}]')]]))
|
eq({1, 2, {['key'] = 'val'}}, eval([[py3eval('[1, 2, {"key": "val"}]')]]))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('RPC call to expand("<afile>") during BufDelete #5245 #5617', function()
|
||||||
|
source([=[
|
||||||
|
python3 << EOF
|
||||||
|
import vim
|
||||||
|
def foo():
|
||||||
|
vim.eval('expand("<afile>:p")')
|
||||||
|
vim.eval('bufnr(expand("<afile>:p"))')
|
||||||
|
EOF
|
||||||
|
autocmd BufDelete * python3 foo()
|
||||||
|
autocmd BufUnload * python3 foo()]=])
|
||||||
|
feed_command("exe 'split' tempname()")
|
||||||
|
feed_command("bwipeout!")
|
||||||
|
feed_command('help help')
|
||||||
|
eq(2, eval('1+1')) -- Still alive?
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
@@ -1,16 +1,18 @@
|
|||||||
local helpers = require('test.functional.helpers')(after_each)
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
|
|
||||||
local eq = helpers.eq
|
|
||||||
local feed = helpers.feed
|
|
||||||
local clear = helpers.clear
|
local clear = helpers.clear
|
||||||
local funcs = helpers.funcs
|
|
||||||
local meths = helpers.meths
|
|
||||||
local insert = helpers.insert
|
|
||||||
local expect = helpers.expect
|
|
||||||
local command = helpers.command
|
local command = helpers.command
|
||||||
local write_file = helpers.write_file
|
|
||||||
local curbufmeths = helpers.curbufmeths
|
local curbufmeths = helpers.curbufmeths
|
||||||
|
local eq = helpers.eq
|
||||||
|
local eval = helpers.eval
|
||||||
|
local expect = helpers.expect
|
||||||
|
local feed = helpers.feed
|
||||||
|
local feed_command = helpers.feed_command
|
||||||
|
local funcs = helpers.funcs
|
||||||
|
local insert = helpers.insert
|
||||||
|
local meths = helpers.meths
|
||||||
local missing_provider = helpers.missing_provider
|
local missing_provider = helpers.missing_provider
|
||||||
|
local write_file = helpers.write_file
|
||||||
|
|
||||||
do
|
do
|
||||||
clear()
|
clear()
|
||||||
@@ -90,3 +92,11 @@ describe(':rubydo command', function()
|
|||||||
eq(false, curbufmeths.get_option('modified'))
|
eq(false, curbufmeths.get_option('modified'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('ruby provider', function()
|
||||||
|
it('RPC call to expand("<afile>") during BufDelete #5245 #5617', function()
|
||||||
|
command([=[autocmd BufDelete * ruby VIM::evaluate('expand("<afile>")')]=])
|
||||||
|
feed_command('help help')
|
||||||
|
eq(2, eval('1+1')) -- Still alive?
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
Reference in New Issue
Block a user