Commit: patch 9.2.1011: [security]: arbitrary Ex command execution during C omni-completion

5 views
Skip to first unread message

Christian Brabandt

unread,
Aug 25, 2026, 5:30:16 PM (3 days ago) Aug 25
to vim...@googlegroups.com
patch 9.2.1011: [security]: arbitrary Ex command execution during C omni-completion

Commit: https://github.com/vim/vim/commit/331d5d67028505c5b6043603a57d1e8497b922b5
Author: Christian Brabandt <c...@256bit.org>
Date: Tue Aug 25 21:15:57 2026 +0000

patch 9.2.1011: [security]: arbitrary Ex command execution during C omni-completion

Problem: arbitrary Ex command execution during C omni-completion via
tag file names (Yazan Balawneh)
Solution: Escape the | for all returned tag files

Github Security Advisory:
https://github.com/vim/vim/security/advisories/GHSA-r77m-8m55-rpr6

Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/autoload/ccomplete.vim b/runtime/autoload/ccomplete.vim
index 593789a84..dfe154e46 100644
--- a/runtime/autoload/ccomplete.vim
+++ b/runtime/autoload/ccomplete.vim
@@ -3,7 +3,7 @@ vim9script noclear
# Vim completion script
# Language: C
# Maintainer: The Vim Project <https://github.com/vim/vim>
-# Last Change: 2026 Feb 18
+# Last Change: 2026 Aug 25
# Rewritten in Vim9 script by github user lacygoill
# Former Maintainer: Bram Moolenaar <Br...@vim.org>

@@ -575,7 +575,7 @@ def StructMembers( # {{{1

# Todo: What about local structures?
var fnames: string = tagfiles()
- ->map((_, v: string) => escape(v, ' \#%'))
+ ->map((_, v: string) => escape(v, ' \#%|'))
->join()
if fnames == ''
return []
diff --git a/src/testdir/test_plugin_ccomplete.vim b/src/testdir/test_plugin_ccomplete.vim
index c1754d17c..92cca050f 100644
--- a/src/testdir/test_plugin_ccomplete.vim
+++ b/src/testdir/test_plugin_ccomplete.vim
@@ -85,4 +85,34 @@ func Test_ccomplete_typeref_completion_still_works()
let &tags = save_tags
endfunc

+" The tags file names from tagfiles() are executed
+" via a single vimgrep command and trailing | does
+" not cause code execution
+func Test_ccomplete_no_exec_via_tagfile_name()
+ CheckUnix
+ let dir = "Xcc|&titlestring = 'INJECTED'|ls "
+ call mkdir(dir, 'pR')
+ let tagsfile = dir .. '/tags'
+ call writefile(["!_TAG_FILE_SORTED 0 /0/",
+ \ "myvar main.c /^x$/;\" v typeref:struct:mystruct",
+ \ "alpha main.c /^x$/;\" m struct:mystruct",
+ \ ], tagsfile)
+
+ let save_tags = &tags
+ let save_title = &titlestring
+ let &tags = fnamemodify(tagsfile, ':p')
+ set titlestring=orig
+
+ new
+ call ccomplete#Complete(1, '')
+ call ccomplete#Complete(0, 'myvar.x')
+
+ call assert_equal('orig', &titlestring,
+ \ 'tags file name was executed as an Ex command during omni-completion')
+
+ bwipe!
+ let &tags = save_tags
+ let &titlestring = save_title
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index 392e69d46..9d15a29b0 100644
--- a/src/version.c
+++ b/src/version.c
@@ -763,6 +763,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1011,
/**/
1010,
/**/
Reply all
Reply to author
Forward
0 new messages