mirror of
https://github.com/neovim/neovim.git
synced 2024-12-24 05:05:00 -07:00
fix(treesitter): fix another TSNode:tree() double free
Unfortunately the gc=false objects can refer to a dangling tree if the gc=true tree was freed first. This reuses the same tree object as the node itself is keeping alive via the uservalue of the node userdata. (wrapped in a table due to lua 5.1 restrictions)
This commit is contained in:
parent
6e45567b49
commit
50a03c0e99
@ -53,7 +53,6 @@ typedef struct {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
TSTree *tree;
|
TSTree *tree;
|
||||||
bool gc;
|
|
||||||
} TSLuaTree;
|
} TSLuaTree;
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
@ -477,13 +476,12 @@ static int parser_parse(lua_State *L)
|
|||||||
return luaL_error(L, "An error occurred when parsing.");
|
return luaL_error(L, "An error occurred when parsing.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// The new tree will be pushed to the stack, without copy, ownership is now to
|
// The new tree will be pushed to the stack, without copy, ownership is now to the lua GC.
|
||||||
// the lua GC.
|
// Old tree is owned by lua GC since before
|
||||||
// Old tree is still owned by the lua GC.
|
|
||||||
uint32_t n_ranges = 0;
|
uint32_t n_ranges = 0;
|
||||||
TSRange *changed = old_tree ? ts_tree_get_changed_ranges(old_tree, new_tree, &n_ranges) : NULL;
|
TSRange *changed = old_tree ? ts_tree_get_changed_ranges(old_tree, new_tree, &n_ranges) : NULL;
|
||||||
|
|
||||||
push_tree(L, new_tree, true); // [tree]
|
push_tree(L, new_tree); // [tree]
|
||||||
|
|
||||||
push_ranges(L, changed, n_ranges, include_bytes); // [tree, ranges]
|
push_ranges(L, changed, n_ranges, include_bytes); // [tree, ranges]
|
||||||
|
|
||||||
@ -509,7 +507,7 @@ static int tree_copy(lua_State *L)
|
|||||||
}
|
}
|
||||||
|
|
||||||
TSTree *copy = ts_tree_copy(ud->tree);
|
TSTree *copy = ts_tree_copy(ud->tree);
|
||||||
push_tree(L, copy, true); // [tree]
|
push_tree(L, copy); // [tree]
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -779,8 +777,9 @@ static int parser_get_logger(lua_State *L)
|
|||||||
|
|
||||||
/// push tree interface on lua stack.
|
/// push tree interface on lua stack.
|
||||||
///
|
///
|
||||||
/// The tree is garbage collected if gc is true
|
/// The tree is not copied. Ownership of the tree is transfered from c code to
|
||||||
void push_tree(lua_State *L, TSTree *tree, bool gc)
|
/// lua. if needed use ts_tree_copy() in the caller
|
||||||
|
void push_tree(lua_State *L, TSTree *tree)
|
||||||
{
|
{
|
||||||
if (tree == NULL) {
|
if (tree == NULL) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
@ -789,7 +788,6 @@ void push_tree(lua_State *L, TSTree *tree, bool gc)
|
|||||||
TSLuaTree *ud = lua_newuserdata(L, sizeof(TSLuaTree)); // [udata]
|
TSLuaTree *ud = lua_newuserdata(L, sizeof(TSLuaTree)); // [udata]
|
||||||
|
|
||||||
ud->tree = tree;
|
ud->tree = tree;
|
||||||
ud->gc = gc;
|
|
||||||
|
|
||||||
lua_getfield(L, LUA_REGISTRYINDEX, TS_META_TREE); // [udata, meta]
|
lua_getfield(L, LUA_REGISTRYINDEX, TS_META_TREE); // [udata, meta]
|
||||||
lua_setmetatable(L, -2); // [udata]
|
lua_setmetatable(L, -2); // [udata]
|
||||||
@ -812,7 +810,7 @@ static TSLuaTree *tree_check(lua_State *L, int index)
|
|||||||
static int tree_gc(lua_State *L)
|
static int tree_gc(lua_State *L)
|
||||||
{
|
{
|
||||||
TSLuaTree *ud = tree_check(L, 1);
|
TSLuaTree *ud = tree_check(L, 1);
|
||||||
if (ud && ud->gc) {
|
if (ud) {
|
||||||
ts_tree_delete(ud->tree);
|
ts_tree_delete(ud->tree);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -1322,7 +1320,9 @@ static int node_tree(lua_State *L)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
push_tree(L, (TSTree *)node.tree, false);
|
lua_getfenv(L, 1); // [udata, reftable]
|
||||||
|
lua_rawgeti(L, -1, 1); // [udata, reftable, tree_udata]
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,20 @@ describe('treesitter node API', function()
|
|||||||
assert_alive()
|
assert_alive()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('double free tree 2', function()
|
||||||
|
exec_lua([[
|
||||||
|
parser = vim.treesitter.get_parser(0, "c")
|
||||||
|
local x = parser:parse()[1]:root():tree()
|
||||||
|
vim.api.nvim_buf_set_text(0, 0,0, 0,0, {'y'})
|
||||||
|
parser:parse()
|
||||||
|
vim.api.nvim_buf_set_text(0, 0,0, 0,1, {'z'})
|
||||||
|
parser:parse()
|
||||||
|
collectgarbage()
|
||||||
|
x:root()
|
||||||
|
]])
|
||||||
|
assert_alive()
|
||||||
|
end)
|
||||||
|
|
||||||
it('can move between siblings', function()
|
it('can move between siblings', function()
|
||||||
insert([[
|
insert([[
|
||||||
int main(int x, int y, int z) {
|
int main(int x, int y, int z) {
|
||||||
|
Loading…
Reference in New Issue
Block a user