# HG changeset patch # User Christian Brabandt # Date 1708279203 -3600 # Node ID d8c69a7734569ec66d93238d2fe1d03475bfd174 # Parent 893d9f64c87bcc3a7e907d730f67bc349dbbc0c4 patch 9.1.0115: Using freed memory with full tag stack and user data Commit: https://github.com/vim/vim/commit/c86bff1771ed9c340f8f4433ae5530fd6de97980 Author: zeertzjq Date: Sun Feb 18 18:53:08 2024 +0100 patch 9.1.0115: Using freed memory with full tag stack and user data Problem: Using freed memory with full tag stack and user data (Konstantin Khlebnikov) Solution: Clear the user data pointer of the newest entry. (zeertzjq, Konstantin Khlebnikov) fixes: neovim/neovim#27498 closes: #14053 Co-authored-by: Konstantin Khlebnikov koct9i@gmail.com Signed-off-by: zeertzjq Signed-off-by: Konstantin Khlebnikov koct9i@gmail.com Signed-off-by: Christian Brabandt diff --git a/runtime/doc/testing.txt b/runtime/doc/testing.txt --- a/runtime/doc/testing.txt +++ b/runtime/doc/testing.txt @@ -1,4 +1,4 @@ -*testing.txt* For Vim version 9.1. Last change: 2024 Jan 23 +*testing.txt* For Vim version 9.1. Last change: 2024 Feb 18 VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/src/tag.c b/src/tag.c --- a/src/tag.c +++ b/src/tag.c @@ -395,7 +395,7 @@ do_tag( tagstack_clear_entry(&tagstack[0]); for (i = 1; i < tagstacklen; ++i) tagstack[i - 1] = tagstack[i]; - --tagstackidx; + tagstack[--tagstackidx].user_data = NULL; } /* diff --git a/src/testdir/test_tagjump.vim b/src/testdir/test_tagjump.vim --- a/src/testdir/test_tagjump.vim +++ b/src/testdir/test_tagjump.vim @@ -900,18 +900,33 @@ func Test_tag_stack() endfor call writefile(l, 'Xfoo', 'D') + enew " Jump to a tag when the tag stack is full. Oldest entry should be removed. - enew for i in range(10, 30) exe "tag var" .. i endfor - let l = gettagstack() - call assert_equal(20, l.length) - call assert_equal('var11', l.items[0].tagname) + let t = gettagstack() + call assert_equal(20, t.length) + call assert_equal('var11', t.items[0].tagname) + let full = deepcopy(t.items) tag var31 - let l = gettagstack() - call assert_equal('var12', l.items[0].tagname) - call assert_equal('var31', l.items[19].tagname) + let t = gettagstack() + call assert_equal('var12', t.items[0].tagname) + call assert_equal('var31', t.items[19].tagname) + + " Jump to a tag when the tag stack is full, but with user data this time. + call foreach(full, {i, item -> extend(item, {'user_data': $'udata{i}'})}) + call settagstack(0, {'items': full}) + let t = gettagstack() + call assert_equal(20, t.length) + call assert_equal('var11', t.items[0].tagname) + call assert_equal('udata0', t.items[0].user_data) + tag var31 + let t = gettagstack() + call assert_equal('var12', t.items[0].tagname) + call assert_equal('udata1', t.items[0].user_data) + call assert_equal('var31', t.items[19].tagname) + call assert_false(has_key(t.items[19], 'user_data')) " Use tnext with a single match call assert_fails('tnext', 'E427:') diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -705,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 115, +/**/ 114, /**/ 113,