tree-sitter / tree-sitter-html

HTML grammar for Tree-sitter
MIT License
136 stars 72 forks source link

Segmentation fault editing a tag attribute #99

Closed vakorol closed 6 months ago

vakorol commented 6 months ago

Hi!

I'm having trouble with the html parser for tree-sitter in Neovim.

Consider the following trivial HTML code:

<html lang="fr"></html>

Attempting to edit the "fr" value - for example, changing it to "en" - makes Neovim crash after trying to type in the second char (right after the attribute value becomes "e").

I managed to capture the crash with GDB:

Thread 1 "nvim" received signal SIGSEGV, Segmentation fault.
0x00007767496a881e in __GI___libc_free (mem=0x20) at ./malloc/malloc.c:3368

#0  0x00007767496a881e in __GI___libc_free (mem=0x20) at ./malloc/malloc.c:3368
#1  0x0000776749a3ec98 in tree_sitter_html_external_scanner_destroy ()
   from /home/hiroshima/.local/share/nvim/lazy/nvim-treesitter/parser/html.so
#2  0x00005da66b0ca943 in ts_parser_reset ()
#3  0x00005da66b0cf740 in ts_parser_parse ()
#4  0x00005da66af58c99 in ?? ()
#5  0x00007767499b53e6 in ?? () from /lib/x86_64-linux-gnu/libluajit-5.1.so.2
#6  0x00007767499c7cb2 in lua_pcall () from /lib/x86_64-linux-gnu/libluajit-5.1.so.2
#7  0x00005da66af49a30 in nlua_pcall ()
#8  0x00005da66af543aa in nlua_call_ref ()
#9  0x00005da66ae7adaa in ?? ()
#10 0x00005da66ae7b5ab in decor_providers_invoke_win ()
#11 0x00005da66ae8bd1e in ?? ()
#12 0x00005da66ae8e0a5 in update_screen ()
#13 0x00005da66ae8e697 in ins_redraw ()
#14 0x00005da66ae91496 in ?? ()
#15 0x00005da66b0424b1 in state_enter ()
#16 0x00005da66ae9388c in ?? ()
#17 0x00005da66ae93aa6 in edit ()
#18 0x00005da66afa4153 in ?? ()
#19 0x00005da66afa526e in ?? ()
#20 0x00005da66af9ec1d in ?? ()
#21 0x00005da66b0424e1 in state_enter ()
#22 0x00005da66af9bd8f in normal_enter ()
#23 0x00005da66af5d0ef in main ()

Neovim version: 0.10.0-dev

I double-checked that the problem does not exist if the html parser is uninstalled.

vakorol commented 6 months ago

Here's also the full backtrace from GDB when running Neovim v0.10.0 compiled with debug info:

show the full backtrace ``` #0 0x000072be898a881e in __GI___libc_free (mem=0x295c3d) at ./malloc/malloc.c:3368 ar_ptr = p = err = #1 0x000072be89c4cc98 in tree_sitter_html_external_scanner_destroy () from /home/hiroshima/.local/share/nvim/lazy/nvim-treesitter/parser/html.so No symbol table info available. #2 0x0000618437875e2b in ts_parser__external_scanner_destroy (self=0x618438401140) at /home/hiroshima/src/neovim/.deps/build/src/treesitter/lib/src/./parser.c:383 No locals. #3 0x000061843787b512 in ts_parser_reset (self=0x618438401140) at /home/hiroshima/src/neovim/.deps/build/src/treesitter/lib/src/./parser.c:1970 No locals. #4 0x000061843787bfe7 in ts_parser_parse (self=0x618438401140, old_tree=0x6184384ac100, input=...) at /home/hiroshima/src/neovim/.deps/build/src/treesitter/lib/src/./parser.c:2114 result = 0x61843848d320 position = 24 last_position = 24 version_count = 1 __PRETTY_FUNCTION__ = "ts_parser_parse" #5 0x0000618437647e28 in parser_parse (L=0x72be89b17380) at /home/hiroshima/src/neovim/src/nvim/lua/treesitter.c:359 p = 0x618438401140 old_tree = 0x6184384ac100 new_tree = 0x0 len = 368 str = 0x0 bufnr = 1 buf = 0x6184382e1690 input = {payload = 0x6184382e1690, read = 0x61843764772e , encoding = TSInputEncodingUTF8} include_bytes = false n_ranges = 2307551218 changed = 0xa99634aaac1d4800 #6 0x00006184378b9146 in lj_BC_FUNCC () No symbol table info available. #7 0x00006184378a5169 in lua_pcall (L=0x72be89b17380, nargs=, nresults=1, errfunc=) at lj_api.c:1122 g = 0x72be89b173e0 oldh = 0 '\000' ef = status = __func__ = "lua_pcall" #8 0x000061843763e2b3 in nlua_pcall (lstate=0x72be89b17380, nargs=5, nresults=1) at /home/hiroshima/src/neovim/src/nvim/lua/executor.c:173 status = 29374 #9 0x000061843764208e in nlua_call_ref (ref=105, name=0x618437948ca9 "win", args=..., mode=kRetNilBool, arena=0x0, err=0x7fff2733b950) at /home/hiroshima/src/neovim/src/nvim/lua/executor.c:1606 lstate = 0x72be89b17380 nargs = 5 #10 0x0000618437512eb1 in decor_provider_invoke (provider_idx=0, name=0x618437948ca9 "win", ref=105, args=..., default_true=true) at /home/hiroshima/src/neovim/src/nvim/decoration_provider.c:51 err = {type = kErrorTypeNone, msg = 0x0} ret = {type = 657701280, data = {boolean = 165, integer = 107220493253541, floating = 5.2973962246727752e-310, string = { data = 0x61843769bba5 "\220H\213E\370dH+\004%(", size = 126162681020416}, array = {size = 107220493253541, capacity = 126162681020416, items = 0x6184382de9b0}, dictionary = {size = 107220493253541, capacity = 126162681020416, items = 0x6184382de9b0}, luaref = 929676197}} provider = 0x6184382de9b0 #11 0x0000618437513715 in decor_providers_invoke_win (wp=0x6184382de9b0) at /home/hiroshima/src/neovim/src/nvim/decoration_provider.c:144 args = {size = 4, capacity = 4, items = 0x7fff2733ba20} args__items = {{type = kObjectTypeWindow, data = {boolean = 232, integer = 1000, floating = 4.9406564584124654e-321, string = { data = 0x3e8 , size = 0}, array = { size = 1000, capacity = 0, items = 0x0}, dictionary = {size = 1000, capacity = 0, items = 0x0}, luaref = 1000}}, {type = kObjectTypeBuffer, data = {boolean = true, integer = 1, floating = 4.9406564584124654e-324, string = { data = 0x1 , size = 0}, array = {size = 1, capacity = 0, items = 0x0}, dictionary = {size = 1, capacity = 0, items = 0x0}, luaref = 1}}, {type = kObjectTypeInteger, data = {boolean = false, integer = 0, floating = 0, string = {data = 0x0, size = 0}, array = {size = 0, capacity = 0, items = 0x0}, dictionary = {size = 0, capacity = 0, items = 0x0}, luaref = 0}}, { type = kObjectTypeInteger, data = {boolean = false, integer = 0, floating = 0, string = { data = 0x0, size = 0}, array = {size = 0, capacity = 0, items = 0x0}, dictionary = { size = 0, capacity = 0, items = 0x0}, luaref = 0}}} p = 0x6184382ecc00 i = 0 __PRETTY_FUNCTION__ = "decor_providers_invoke_win" botline = 1 #12 0x000061843752a129 in win_update (wp=0x6184382de9b0) at /home/hiroshima/src/neovim/src/nvim/drawscreen.c:1512 top_end = 0 mid_start = 999 mid_end = 0 bot_start = 999 scrolled_down = false top_to_mod = false bot_scroll_start = 999 recursive = false DID_NONE = DID_NONE DID_LINE = DID_LINE DID_FOLD = DID_FOLD did_update = DID_NONE syntax_last_parsed = 0 mod_top = 0 mod_bot = 0 type = 10 buf = 0x6184382e1690 save_got_int = 0 syntax_tm = 8500551162258 nrwidth_before = 932840560 nrwidth_new = -1 cursorline_fi = {fi_lnum = 657701952, fi_level = 32767, fi_low_level = 928999980, fi_lines = 24964} spv = {spv_has_spell = 64, spv_unchanged = 188, spv_checked_col = 32767, spv_checked_lnum = 928976028, spv_cap_col = 24964, spv_capcol_lnum = 657701952} lnum = 657701728 idx = 32767 row = 929138363 srow = 24964 eof = false didline = false old_botline = 1 #13 0x0000618437527d8d in update_screen () at /home/hiroshima/src/neovim/src/nvim/drawscreen.c:647 wp = 0x6184382de9b0 still_may_intro = false is_stl_global = false type = 10 hl_changed = false did_one = true #14 0x0000618437530a53 in ins_redraw (ready=true) at /home/hiroshima/src/neovim/src/nvim/edit.c:1395 No locals. #15 0x000061843752edf6 in insert_check (state=0x7fff2733bea0) at /home/hiroshima/src/neovim/src/nvim/edit.c:499 s = 0x7fff2733bea0 #16 0x000061843778cbc5 in state_enter (s=0x7fff2733bea0) at /home/hiroshima/src/neovim/src/nvim/state.c:40 check_result = 1 key = 110 keyname = 0x618437a7b8a0 "" execute_result = 1 __func__ = "state_enter" #17 0x000061843752e8f4 in insert_enter (s=0x7fff2733bea0) at /home/hiroshima/src/neovim/src/nvim/edit.c:345 No locals. #18 0x00006184375305c0 in edit (cmdchar=105, startln=false, count=1) at /home/hiroshima/src/neovim/src/nvim/edit.c:1294 s = {{state = {check = 0x61843752e9d8 , execute = 0x61843752eea3 }, ca = 0x0, mincol = 0, cmdchar = 105, cmdchar_todo = 105, startln = 0, count = 1, c = 110, lastc = 101, i = 12, did_backspace = false, line_is_white = false, old_topline = 1, old_topfill = 0, inserted_space = 0, replaceState = 272, did_restart_edit = 0, nomove = false, ptr = 0x6184383ae400 "\200\374\004P"}} #19 0x00006184376b634f in invoke_edit (cap=0x7fff2733c090, repl=0, cmd=105, startln=0) at /home/hiroshima/src/neovim/src/nvim/normal.c:6229 restart_edit_save = 0 #20 0x00006184376b62a3 in nv_edit (cap=0x7fff2733c090) at /home/hiroshima/src/neovim/src/nvim/normal.c:6201 No locals. #21 0x00006184376aaae3 in normal_execute (state=0x7fff2733c020, key=105) at /home/hiroshima/src/neovim/src/nvim/normal.c:1229 s = 0x7fff2733c020 __PRETTY_FUNCTION__ = "normal_execute" #22 0x000061843778cd64 in state_enter (s=0x7fff2733c020) at /home/hiroshima/src/neovim/src/nvim/state.c:101 check_result = 1 key = 105 keyname = 0x618437a7b8a0 "" execute_result = 1 __func__ = "state_enter" #23 0x00006184376a8d0f in normal_enter (cmdwin=false, noexmode=false) at /home/hiroshima/src/neovim/src/nvim/normal.c:518 state = {state = {check = 0x6184376ab004 , execute = 0x6184376aa385 }, command_finished = false, ctrl_w = false, need_flushbuf = true, set_prevcount = false, previous_got_int = false, cmdwin = false, noexmode = false, toplevel = true, oa = {op_type = 0, regname = 0, motion_type = kMTCharWise, motion_force = 0, use_reg_one = false, inclusive = true, end_adjusted = false, start = { lnum = 1, col = 12, coladd = 0}, end = {lnum = 1, col = 13, coladd = 0}, cursor_start = { lnum = 0, col = 0, coladd = 0}, line_count = 1, empty = false, is_VIsual = false, start_vcol = 0, end_vcol = 0, prev_opcount = 0, prev_count0 = 0, excl_tr_ws = false}, ca = { oap = 0x7fff2733c038, prechar = 0, cmdchar = 105, nchar = 0, ncharC1 = 0, ncharC2 = 0, extra_char = 0, opcount = 0, count0 = 0, count1 = 1, arg = 0, retval = 0, searchbuf = 0x0}, mapped_len = 0, old_mapped_len = 0, idx = 106, c = 105, old_col = 12, old_pos = {lnum = 1, col = 12, coladd = 0}} prev_oap = 0x0 #24 0x000061843764dec0 in main (argc=3, argv=0x7fff2733c408) at /home/hiroshima/src/neovim/src/nvim/main.c:664 fname = 0x6184382f3900 "\005" params = {argc = 3, argv = 0x7fff2733c408, use_vimrc = 0x0, clean = false, n_commands = 0, commands = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, cmds_tofree = "\000\000\000\000\000\000\000\000\000", n_pre_commands = 0, pre_commands = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, luaf = 0x0, lua_arg0 = -1, edit_type = 1, tagname = 0x0, use_ef = 0x0, input_istext = false, no_swap_file = 0, use_debug_break_level = -1, window_count = 1, window_layout = 0, diff_mode = 0, listen_addr = 0x0, remote = 0, server_addr = 0x0, scriptin = 0x0, scriptout = 0x0, scriptout_append = false, had_stdin_file = false} cwd = 0x0 has_term = true use_builtin_ui = false remote_ui = false __PRETTY_FUNCTION__ = "main" use_remote_ui = true listen_and_embed = false vimrc_none = false __func__ = "main" ```

Sorry, tree-sitter-html was not compiled for debugging, thus "No symbol table info available".

amaanq commented 6 months ago

can't reproduce - what version of the parser is it?

vakorol commented 6 months ago

can't reproduce - what version of the parser is it?

TBH, I don't know how to check the version. It is installed automatically by Treesitter. Can you give me a hint what to look for?

Meanwhile, I tried disabling all other Neovim plugins (except for Lualine) and all other Treesitter parsers - the problem persists.

amaanq commented 6 months ago

can you reinstall the latest parser revision and confirm it still persists? an older version will crash with the newer tree-sitter library runtime.

vakorol commented 6 months ago

can you reinstall the latest parser revision and confirm it still persists? an older version will crash with the newer tree-sitter library runtime.

I have tried :TSUninstall html and :TSUninstall all already, and then installed the parser again.

3lbsofSalt commented 6 months ago

I am having a similar issue. For me, neovim crashes whenever I edit anything inside of valid html tags or between them, but only if all of the html in the file is valid. If I break one of the tags so that it no longer constitutes a valid html tag, the whole file seems to work just fine, I can do whatever I want to edit the tags.

I confirmed that I'm using the most recent tree-sitter html parser by uninstalling and reinstalling it, and I also confirmed that it was for sure the tree-sitter html parser by disabling the parser and confirming that the error no longer persisted. I'm on neovim 0.9.5.

I am working on reproducing the bug with a minimal configuration right now.

vakorol commented 6 months ago

I haven't looked very deep in the code, but the function that crashes is (according to the GDB backtrace):

void tree_sitter_html_external_scanner_destroy(void *payload) {
    Scanner *scanner = (Scanner *)payload;                     
    for (unsigned i = 0; i < scanner->tags.size; i++) {        
        tag_free(&scanner->tags.contents[i]);                  
    }                                                          
    array_delete(&scanner->tags);                              
    ts_free(scanner);                                          
}                                                              

I believe, ts_free is defined in alloc.h:

#ifndef ts_free        
#define ts_free    free
#endif                 

So, if this is the standard Glibc free, it should be called after malloc? But there is no explicit allocation for scanner - ts_malloc is only called for the contents of the Scanner instance (which is basically an Array)..

Is this correct?

vakorol commented 6 months ago

An update:

I have just compiled tree-sitter-html from the master branch, and substituted ~/.local/nvim/share/lazy/nvim-treesitter/parser/html.so with the resulting .so file. The problem disappeared.

Therefore the issue does not seem to exist in main.

The question is: which version of the parser does treesitter pull out with :TSInstall?

amaanq commented 6 months ago

The question is: which version of the parser does treesitter pull out with :TSInstall?

Whatever is in lockfile.json

I'm closing this because it's getting off topic, and seems to not be an issue on master.