mirror of
https://github.com/neovim/neovim.git
synced 2026-07-18 15:11:40 +00:00
backport: fix(api): leak preview callback LuaRef in nvim_create_user_command (#39377)
Problem:
Invalid `nvim_create_user_command` calls can leak the
`preview` callback reference after Neovim has taken ownership of it.
1. build with {a,l}san
2. run:
```sh
<path/to/nvim> --headless -u NONE --clean +'lua
for i = 1, 100 do
pcall(vim.api.nvim_create_user_command,
"some very epic stuff" .. i,
{}, -- NOTE: this is INVALID (not a function or string)
{ preview = function() end })
end
vim.cmd("qa!")
' +qa
```
3. see:
```
100 lua references were leaked!
```
Solution:
Clear `preview_luaref` in `err:`.
(cherry picked from commit 393f687503)
Co-authored-by: Barrett Ruth <62671086+barrettruth@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
4df16ecdb9
commit
e5d6d2e769
@@ -1279,7 +1279,8 @@ void create_user_command(uint64_t channel_id, String name, Union(String, LuaRef)
|
|||||||
if (uc_add_command(name.data, name.size, rep, argt, def, flags, context, compl_arg,
|
if (uc_add_command(name.data, name.size, rep, argt, def, flags, context, compl_arg,
|
||||||
compl_luaref, preview_luaref, addr_type_arg, luaref, force) != OK) {
|
compl_luaref, preview_luaref, addr_type_arg, luaref, force) != OK) {
|
||||||
api_set_error(err, kErrorTypeException, "Failed to create user command");
|
api_set_error(err, kErrorTypeException, "Failed to create user command");
|
||||||
// Do not goto err, since uc_add_command now owns luaref, compl_luaref, and compl_arg
|
// Do not goto err, since uc_add_command now owns luaref, compl_luaref, preview_luaref,
|
||||||
|
// and compl_arg
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1288,6 +1289,7 @@ void create_user_command(uint64_t channel_id, String name, Union(String, LuaRef)
|
|||||||
err:
|
err:
|
||||||
NLUA_CLEAR_REF(luaref);
|
NLUA_CLEAR_REF(luaref);
|
||||||
NLUA_CLEAR_REF(compl_luaref);
|
NLUA_CLEAR_REF(compl_luaref);
|
||||||
|
NLUA_CLEAR_REF(preview_luaref);
|
||||||
xfree(compl_arg);
|
xfree(compl_arg);
|
||||||
}
|
}
|
||||||
/// Gets a map of global (non-buffer-local) Ex commands.
|
/// Gets a map of global (non-buffer-local) Ex commands.
|
||||||
|
|||||||
@@ -274,6 +274,25 @@ describe('nvim_create_user_command', function()
|
|||||||
eq(42, api.nvim_eval('g:command_fired'))
|
eq(42, api.nvim_eval('g:command_fired'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('does not leak `preview` LuaRef on invalid `cmd`', function()
|
||||||
|
local released = exec_lua(function()
|
||||||
|
local weak = setmetatable({}, { __mode = 'v' })
|
||||||
|
for i = 1, 10 do
|
||||||
|
local cb = function() end
|
||||||
|
weak[i] = cb
|
||||||
|
pcall(vim.api.nvim_create_user_command, 'Bogus' .. i, {}, { preview = cb })
|
||||||
|
end
|
||||||
|
collectgarbage('collect')
|
||||||
|
collectgarbage('collect')
|
||||||
|
local n = 0
|
||||||
|
for _ in pairs(weak) do
|
||||||
|
n = n + 1
|
||||||
|
end
|
||||||
|
return n
|
||||||
|
end)
|
||||||
|
eq(0, released)
|
||||||
|
end)
|
||||||
|
|
||||||
it('works with Lua functions', function()
|
it('works with Lua functions', function()
|
||||||
exec_lua [[
|
exec_lua [[
|
||||||
result = {}
|
result = {}
|
||||||
|
|||||||
Reference in New Issue
Block a user