From dab3bfc2b39bf0b97d5f240604ff7a9a26ecb069 Mon Sep 17 00:00:00 2001 From: RHEL Packaging Agent Date: Tue, 14 Jul 2026 09:06:02 +0000 Subject: [PATCH] Fix CVE-2026-59858: arbitrary Ex command execution in C omni-completion Backport fix for CVE-2026-59858 from upstream commit 6b611b0d to vim 8.0. A crafted typeref field in a tags file could break out of the :vimgrep pattern in ccomplete.vim's s:StructMembers() and execute arbitrary Ex commands during C omni-completion. The fix escapes the typename variable with escape(typename, '/\') before interpolation into the pattern. The patch was adapted from vim9script syntax to legacy Vim script for the 8.0 codebase, and includes test coverage for both the security fix and regression testing of legitimate typeref completion. CVE: CVE-2026-59858 Upstream patches: - https://github.com/vim/vim/commit/6b611b0d15603c52ebdad17172b0232b4f65704e.patch Resolves: RHEL-203873 This commit was backported by Ymir, a Red Hat Enterprise Linux software maintenance AI agent. Assisted-by: Ymir --- ...ecurity-arbitrary-Ex-command-executi.patch | 116 ++++++++++++++++++ vim.spec | 12 +- 2 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 0001-patch-9.2.0735-security-arbitrary-Ex-command-executi.patch diff --git a/0001-patch-9.2.0735-security-arbitrary-Ex-command-executi.patch b/0001-patch-9.2.0735-security-arbitrary-Ex-command-executi.patch new file mode 100644 index 00000000..2b188e47 --- /dev/null +++ b/0001-patch-9.2.0735-security-arbitrary-Ex-command-executi.patch @@ -0,0 +1,116 @@ +From 3670c2d9794931e2c3f01dcc2b26165f31306b90 Mon Sep 17 00:00:00 2001 +From: RHEL Packaging Agent +Date: Tue, 14 Jul 2026 08:48:36 +0000 +Subject: [PATCH] patch 9.2.0735: [security]: arbitrary Ex command execution + during C omni-completion + +Problem: [security]: With C omni-completion, a crafted tags file can execute + arbitrary Ex commands when completing a struct/union member + (cipher-creator) +Solution: Escape the type field before inserting it into the :vimgrep + pattern so it cannot close the pattern and start a new command + (Hirohito Higashi). + +Adapted for vim 8.0: the ccomplete.vim in 8.0 uses legacy Vim script syntax +(exe with dot concatenation) instead of vim9script (execute with .. operator). +The same escape() call is applied to the typename variable. +Stripped src/version.c hunk as per downstream policy. +--- + runtime/autoload/ccomplete.vim | 2 +- + src/testdir/Make_all.mak | 1 + + src/testdir/test_plugin_ccomplete.vim | 62 +++++++++++++++++++++++++++ + 3 files changed, 64 insertions(+), 1 deletion(-) + create mode 100644 src/testdir/test_plugin_ccomplete.vim + +diff --git a/runtime/autoload/ccomplete.vim b/runtime/autoload/ccomplete.vim +index d5bfa07..cdbb1ca 100644 +--- a/runtime/autoload/ccomplete.vim ++++ b/runtime/autoload/ccomplete.vim +@@ -500,7 +500,7 @@ function! s:StructMembers(typename, items, all) + endif + if !cached + while 1 +- exe 'silent! keepj noautocmd ' . n . 'vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames ++ exe 'silent! keepj noautocmd ' . n . 'vimgrep /\t' . escape(typename, '/\') . '\(\t\|$\)/j ' . fnames + + let qflist = getqflist() + if len(qflist) > 0 || match(typename, "::") < 0 +diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak +index ed348a4..xxxxxxx 100644 +--- a/src/testdir/Make_all.mak ++++ b/src/testdir/Make_all.mak +@@ -146,5 +146,6 @@ NEW_TESTS = test_arabic.res \ + test_paste.res \ + test_perl.res \ ++ test_plugin_ccomplete.res \ + test_plugin_netrw.res \ + test_plugin_python3complete.res \ + test_plus_arg_edit.res \ +diff --git a/src/testdir/test_plugin_ccomplete.vim b/src/testdir/test_plugin_ccomplete.vim +new file mode 100644 +index 0000000..a635bd5 +--- /dev/null ++++ b/src/testdir/test_plugin_ccomplete.vim +@@ -0,0 +1,62 @@ ++" Tests for the C omni-completion plugin (runtime/autoload/ccomplete.vim). ++ ++func s:WriteTags(lines) ++ " Mark unsorted so lookup is a linear scan regardless of entry order. ++ let tagsfile = tempname() ++ call writefile(["!_TAG_FILE_SORTED\t0\t/0/"] + a:lines, tagsfile) ++ return tagsfile ++endfunc ++ ++" A crafted typeref field is interpolated into the :vimgrep pattern in ++" StructMembers(). Without escaping, "/" closes the pattern and "|" starts a ++" new Ex command, so the field runs as an Ex command during completion. ++func Test_ccomplete_no_exec_via_typeref() ++ unlet! g:ccomplete_injected ++ let tagsfile = s:WriteTags([ ++ \ "myvar\tmain.c\t/^x$/;\"\tv\ttyperef:x/|let g:ccomplete_injected = 1|\"", ++ \ ]) ++ ++ let save_tags = &tags ++ let &tags = tagsfile ++ ++ new ++ call ccomplete#Complete(1, '') ++ call ccomplete#Complete(0, 'myvar.x') ++ ++ call assert_false(exists('g:ccomplete_injected'), ++ \ 'typeref field was executed as an Ex command during omni-completion') ++ ++ bwipe! ++ let &tags = save_tags ++ unlet! g:ccomplete_injected ++endfunc ++ ++" A legitimate typeref must still drive struct-member completion: escaping the ++" field value must not break the normal path. ++func Test_ccomplete_typeref_completion_still_works() ++ let tagsfile = s:WriteTags([ ++ \ "myvar\tmain.c\t/^x$/;\"\tv\ttyperef:struct:mystruct", ++ \ "alpha\tmain.c\t/^x$/;\"\tm\tstruct:mystruct", ++ \ "beta\tmain.c\t/^x$/;\"\tm\tstruct:mystruct", ++ \ ]) ++ ++ let save_tags = &tags ++ let &tags = tagsfile ++ ++ new ++ call ccomplete#Complete(1, '') ++ let items = ccomplete#Complete(0, 'myvar.') ++ ++ call assert_equal(type([]), type(items), ++ \ 'ccomplete#Complete did not return a list') ++ let names = map(copy(items), 'v:val.word') ++ call assert_true(index(names, 'alpha') >= 0, ++ \ 'struct member "alpha" missing from completion: ' . string(names)) ++ call assert_true(index(names, 'beta') >= 0, ++ \ 'struct member "beta" missing from completion: ' . string(names)) ++ ++ bwipe! ++ let &tags = save_tags ++endfunc ++ ++" vim: shiftwidth=2 sts=2 expandtab diff --git a/vim.spec b/vim.spec index 9e68b5f3..f3d1880f 100644 --- a/vim.spec +++ b/vim.spec @@ -24,7 +24,7 @@ Summary: The VIM editor URL: http://www.vim.org/ Name: vim Version: %{baseversion}.%{patchlevel} -Release: 30%{?dist} +Release: 31%{?dist} License: Vim and MIT Source0: ftp://ftp.vim.org/pub/vim/unix/vim-%{baseversion}-%{patchlevel}.tar.bz2 Source1: vim.sh @@ -210,6 +210,11 @@ Patch3065: 0001-patch-9.2.0698-security-Out-of-bounds-write-with-sou.patch # literals with system printf, used mkdir 'p' with manual cleanup instead # of 'pR', added test to Make_all.mak) Patch3066: 0001-patch-9.2.0653-security-out-of-bounds-write-in-tree_.patch +# RHEL-203873 CVE-2026-59858 arbitrary Ex command execution during C omni-completion +# https://redhat.atlassian.net/browse/RHEL-203873 +# https://github.com/vim/vim/commit/6b611b0d15603c52ebdad17172b0232b4f65704e +# stripped src/version.c hunk, adapted ccomplete.vim fix for vim 8.0 legacy script syntax +Patch3067: 0001-patch-9.2.0735-security-arbitrary-Ex-command-executi.patch # gcc is no longer in buildroot by default @@ -458,6 +463,7 @@ perl -pi -e "s,bin/nawk,bin/awk,g" runtime/tools/mve.awk %patch -P 3064 -p1 -b .CVE-2026-57456 %patch -P 3065 -p1 -b .CVE-2026-57455 %patch -P 3066 -p1 -b .CVE-2026-55693 +%patch -P 3067 -p1 -b .CVE-2026-59858 %build @@ -977,6 +983,10 @@ touch %{buildroot}/%{_datadir}/%{name}/vimfiles/doc/tags %{_datadir}/icons/locolor/*/apps/* %changelog +* Tue Jul 14 2026 RHEL Packaging Agent - 2:8.0.1763-31 +- RHEL-203873 CVE-2026-59858 vim: arbitrary Ex command execution in + C omni-completion + * Mon Jul 13 2026 RHEL Packaging Agent - 2:8.0.1763-30 - RHEL-194055 CVE-2026-55693 vim: out-of-bounds write in tree_count_words()