From e86a577c2ed42989c75c72ad36e19c2c62902be0 Mon Sep 17 00:00:00 2001 From: RHEL Packaging Agent Date: Wed, 29 Jul 2026 13:27:30 +0000 Subject: [PATCH] Fix CVE-2026-55693: out-of-bounds write in tree_count_words() Backport upstream commit a80874d9b84a to fix CVE-2026-55693, an out-of-bounds write in tree_count_words() triggered by crafted spell files. The patch adds depth bounds checks to tree_count_words() and sug_filltree() in src/spellfile.c, preventing writes past the MAXWLEN-sized depth arrays. A test case is included. The mkdir 'R' flag was adapted for Vim 8.2 compatibility. CVE: CVE-2026-55693 Upstream patches: - https://github.com/vim/vim/commit/a80874d9b84a01040e3d1aef2d4a59e1934dafb7.patch Resolves: RHEL-194065 This commit was backported by Ymir, a Red Hat Enterprise Linux software maintenance AI agent. Assisted-by: Ymir --- ...ecurity-out-of-bounds-write-in-tree_.patch | 82 +++++++++++++++++++ vim.spec | 12 ++- 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 0001-patch-9.2.0653-security-out-of-bounds-write-in-tree_.patch diff --git a/0001-patch-9.2.0653-security-out-of-bounds-write-in-tree_.patch b/0001-patch-9.2.0653-security-out-of-bounds-write-in-tree_.patch new file mode 100644 index 00000000..5b57d0d4 --- /dev/null +++ b/0001-patch-9.2.0653-security-out-of-bounds-write-in-tree_.patch @@ -0,0 +1,82 @@ +From 7a5751db61efd9f88dfdae11c49512a2f01f3ca3 Mon Sep 17 00:00:00 2001 +From: Christian Brabandt +Date: Mon, 15 Jun 2026 19:39:08 +0000 +Subject: [PATCH] patch 9.2.0653: [security]: out-of-bounds write in + tree_count_words() + +Problem: [security]: a crafted spell file can drive tree_count_words() + past the end of its MAXWLEN-sized depth arrays; the descent + loop has no depth bound. +Solution: only descend while depth < MAXWLEN - 1, as the sibling trie + walkers already do; apply the same guard to sug_filltree(). + +Github Security Advisory: +https://github.com/vim/vim/security/advisories/GHSA-wgh4-64f7-q3jq + +Supported by AI. + +Signed-off-by: Christian Brabandt +--- + src/spellfile.c | 4 ++-- + src/testdir/test_spellfile.vim | 28 ++++++++++++++++++++++++++++ + 2 files changed, 30 insertions(+), 2 deletions(-) + +diff --git a/src/spellfile.c b/src/spellfile.c +index 79d60e644..610ccfe20 100644 +--- a/src/spellfile.c ++++ b/src/spellfile.c +@@ -648,7 +648,7 @@ tree_count_words(char_u *byts, idx_T *idxs) + ++curi[depth]; + } + } +- else ++ else if (depth < MAXWLEN - 1) + { + // Normal char, go one level deeper to count the words. + ++depth; +@@ -5627,7 +5627,7 @@ sug_filltree(spellinfo_T *spin, slang_T *slang) + ++curi[depth]; + } + } +- else ++ else if (depth < MAXWLEN - 1) + { + // Normal char, go one level deeper. + tword[depth++] = c; +diff --git a/src/testdir/test_spellfile.vim b/src/testdir/test_spellfile.vim +index a400d51c0..94c677f26 100644 +--- a/src/testdir/test_spellfile.vim ++++ b/src/testdir/test_spellfile.vim +@@ -897,4 +897,32 @@ func Test_soundfold_overflow() + call delete('Xtest.latin1.sug') + endfunc + ++func Test_spell_sug_tree_count_words_overflow() ++ " A crafted .spl/.sug pair with a BY_INDEX self-cycle in the fold word tree ++ " parses cleanly (shared refs aren't recursed, so read_tree_node()'s depth ++ " cap never trips), but drove tree_count_words() past its MAXWLEN-sized depth ++ " arrays -> stack out-of-bounds write. The walk only happens when ++ " spellsuggest() loads the matching .sug. Reaching the assert == no OOB. ++ call mkdir('Xrtp/spell', 'p') ++ " VIMspell + v50, SN_SUGFILE(ts), SN_END, LWORDTREE{node:1,BY_INDEX->0,'A'}, ++ " empty KWORDTREE/PREFIXTREE ++ let spl = eval('0z56494D7370656C6C320B0000000008000000001234' ++ \ .. '5678FF000000020101000000410000000000000000') ++ " VIMsug + v1, matching ts, SUGWORDTREE word "a", empty SUGTABLE ++ let sug = 0z56494D737567010000000012345678000000040161010000000000 ++ call writefile(spl, 'Xrtp/spell/xx.utf-8.spl', 'b') ++ call writefile(sug, 'Xrtp/spell/xx.utf-8.sug', 'b') ++ ++ new ++ set runtimepath+=./Xrtp ++ set spelllang=xx ++ set spell ++ " Unpatched: OOB write here (ASan abort, or crash). Patched: returns a list. ++ call assert_equal(v:t_list, type(spellsuggest('helloo'))) ++ ++ set spell& spelllang& runtimepath& ++ bwipe! ++ call delete('Xrtp', 'rf') ++endfunc ++ + " vim: shiftwidth=2 sts=2 expandtab diff --git a/vim.spec b/vim.spec index ae5a8ab5..ab4fd253 100644 --- a/vim.spec +++ b/vim.spec @@ -27,7 +27,7 @@ Summary: The VIM editor URL: http://www.vim.org/ Name: vim Version: %{baseversion}.%{patchlevel} -Release: 36%{?dist} +Release: 37%{?dist} License: Vim and MIT Source0: ftp://ftp.vim.org/pub/vim/unix/vim-%{baseversion}-%{patchlevel}.tar.bz2 Source1: virc @@ -221,6 +221,11 @@ Patch3075: 0001-patch-9.2.0736-potential-command-execution-in-PHP-omn.patch # https://github.com/vim/vim/commit/497f931f85339d175d7f69588dd249e8ccfed41b # adjusted: replaced defer/writefile 'D' flag with call delete() for Vim 8.2 compat Patch3076: 0001-patch-9.2.0698-security-Out-of-bounds-write-with-sou.patch +# RHEL-194065 CVE-2026-55693 out-of-bounds write in tree_count_words() +# https://redhat.atlassian.net/browse/RHEL-194065 +# https://github.com/vim/vim/commit/a80874d9b84a01040e3d1aef2d4a59e1934dafb7 +# adjusted: stripped src/version.c hunks; replaced mkdir 'pR' flag with 'p' + manual cleanup for Vim 8.2 compat +Patch3077: 0001-patch-9.2.0653-security-out-of-bounds-write-in-tree_.patch # gcc is no longer in buildroot by default @@ -485,6 +490,7 @@ perl -pi -e "s,bin/nawk,bin/awk,g" runtime/tools/mve.awk %patch -P 3074 -p1 -b .ccomplete-typeref-escape %patch -P 3075 -p1 -b .phpcomplete-cmd-exec %patch -P 3076 -p1 -b .soundfold-overflow +%patch -P 3077 -p1 -b .tree-count-words-oob %build cd src @@ -1037,6 +1043,10 @@ touch %{buildroot}/%{_datadir}/%{name}/vimfiles/doc/tags %endif %changelog +* Wed Jul 29 2026 RHEL Packaging Agent - 2:8.2.2637-37 +- RHEL-194065 CVE-2026-55693 vim: out-of-bounds write in + tree_count_words() + * Wed Jul 29 2026 RHEL Packaging Agent - 2:8.2.2637-36 - RHEL-191362 CVE-2026-57455 vim: Out-of-bounds write with soundfold()