From b0d37a0c3b56146006bafd32a957b2f72350a2a6 Mon Sep 17 00:00:00 2001 From: cunlongli Date: Wed, 11 Mar 2026 21:15:07 +0800 Subject: [PATCH] fix some CVEs fix CVE-2026-25749, CVE-2026-26269, CVE-2026-28417, CVE-2026-28418 CVE-2026-28419, CVE-2026-28420, CVE-2026-28421, CVE-2026-28422 --- ...ecurity-buffer-overflow-in-helpfile-.patch | 52 ++++++ ...ecurity-Buffer-overflow-in-netbeans-.patch | 123 ++++++++++++++ ...ecurity-possible-command-injection-u.patch | 107 ++++++++++++ ...time-netrw-upstream-snapshot-of-v179.patch | 109 ++++++++++++ ...ecurity-Crash-with-overlong-emacs-ta.patch | 59 +++++++ ...ecurity-Buffer-underflow-with-emacs-.patch | 66 ++++++++ ...ecurity-buffer-overflow-in-terminal-.patch | 157 ++++++++++++++++++ ...ecurity-Crash-when-recovering-a-corr.patch | 86 ++++++++++ ...ecurity-stack-buffer-overflow-in-bui.patch | 39 +++++ vim.spec | 25 ++- 10 files changed, 822 insertions(+), 1 deletion(-) create mode 100644 0001-patch-9.1.2132-security-buffer-overflow-in-helpfile-.patch create mode 100644 0002-patch-9.1.2148-security-Buffer-overflow-in-netbeans-.patch create mode 100644 0003-patch-9.2.0073-security-possible-command-injection-u.patch create mode 100644 0003-runtime-netrw-upstream-snapshot-of-v179.patch create mode 100644 0004-patch-9.2.0074-security-Crash-with-overlong-emacs-ta.patch create mode 100644 0005-patch-9.2.0075-security-Buffer-underflow-with-emacs-.patch create mode 100644 0006-patch-9.2.0076-security-buffer-overflow-in-terminal-.patch create mode 100644 0007-patch-9.2.0077-security-Crash-when-recovering-a-corr.patch create mode 100644 0008-patch-9.2.0078-security-stack-buffer-overflow-in-bui.patch diff --git a/0001-patch-9.1.2132-security-buffer-overflow-in-helpfile-.patch b/0001-patch-9.1.2132-security-buffer-overflow-in-helpfile-.patch new file mode 100644 index 0000000..8e35c14 --- /dev/null +++ b/0001-patch-9.1.2132-security-buffer-overflow-in-helpfile-.patch @@ -0,0 +1,52 @@ +From 0714b15940b245108e6e9d7aa2260dd849a26fa9 Mon Sep 17 00:00:00 2001 +From: Christian Brabandt +Date: Thu, 5 Feb 2026 18:51:54 +0000 +Subject: [PATCH] patch 9.1.2132: [security]: buffer-overflow in 'helpfile' + option handling + +Problem: [security]: buffer-overflow in 'helpfile' option handling by + using strcpy without bound checks (Rahul Hoysala) +Solution: Limit strncpy to the length of the buffer (MAXPATHL) + +Github Advisory: +https://github.com/vim/vim/security/advisories/GHSA-5w93-4g67-mm43 + +Signed-off-by: Christian Brabandt +--- + src/tag.c | 2 +- + src/testdir/test_help.vim | 9 +++++++++ + 2 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/src/tag.c b/src/tag.c +index 8003156..99276a3 100644 +--- a/src/tag.c ++++ b/src/tag.c +@@ -3344,7 +3344,7 @@ get_tagfname( + if (tnp->tn_hf_idx > tag_fnames.ga_len || *p_hf == NUL) + return FAIL; + ++tnp->tn_hf_idx; +- STRCPY(buf, p_hf); ++ vim_strncpy(buf, p_hf, MAXPATHL - 1); + STRCPY(gettail(buf), "tags"); + #ifdef BACKSLASH_IN_FILENAME + slash_adjust(buf); +diff --git a/src/testdir/test_help.vim b/src/testdir/test_help.vim +index 6c8b3ab..a6001ad 100644 +--- a/src/testdir/test_help.vim ++++ b/src/testdir/test_help.vim +@@ -206,4 +206,13 @@ func Test_help_using_visual_match() + endfunc + + ++" This caused a buffer overflow ++func Test_helpfile_overflow() ++ let _helpfile = &helpfile ++ let &helpfile = repeat('A', 5000) ++ help ++ helpclose ++ let &helpfile = _helpfile ++endfunc ++ + " vim: shiftwidth=2 sts=2 expandtab +-- +2.41.1 diff --git a/0002-patch-9.1.2148-security-Buffer-overflow-in-netbeans-.patch b/0002-patch-9.1.2148-security-Buffer-overflow-in-netbeans-.patch new file mode 100644 index 0000000..b4aaf9c --- /dev/null +++ b/0002-patch-9.1.2148-security-Buffer-overflow-in-netbeans-.patch @@ -0,0 +1,123 @@ +From c5f312aad8e4179e437f81ad39a860cd0ef11970 Mon Sep 17 00:00:00 2001 +From: Christian Brabandt +Date: Fri, 13 Feb 2026 10:27:12 +0100 +Subject: [PATCH] patch 9.1.2148: [security]: Buffer overflow in netbeans + interface + +Problem: [security]: Buffer overflow in netbeans special_keys() handling +Solution: Limit writing to max KEYBUFLEN bytes to prevent writing out of + bounds. + +Github Advisory: +https://github.com/vim/vim/security/advisories/GHSA-9w5c-hwr9-hc68 + +Signed-off-by: Christian Brabandt +--- + src/netbeans.c | 2 +- + src/testdir/test_netbeans.py | 4 ++- + src/testdir/test_netbeans.vim | 57 +++++++++++++++++++++++++++++++++++ + 3 files changed, 61 insertions(+), 2 deletions(-) + +diff --git a/src/netbeans.c b/src/netbeans.c +index a27148c..84c7689 100644 +--- a/src/netbeans.c ++++ b/src/netbeans.c +@@ -2299,7 +2299,7 @@ special_keys(char_u *args) + if ((sep = strchr(tok, '-')) != NULL) + { + *sep = NUL; +- while (*tok) ++ while (*tok && i + 2 < KEYBUFLEN) + { + switch (*tok) + { +diff --git a/src/testdir/test_netbeans.py b/src/testdir/test_netbeans.py +index 888a2d9..548a0f1 100644 +--- a/src/testdir/test_netbeans.py ++++ b/src/testdir/test_netbeans.py +@@ -112,7 +112,9 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): + 'startAtomic_Test' : '0:startAtomic!94\n', + 'endAtomic_Test' : '0:endAtomic!95\n', + 'AnnoScale_Test' : "".join(['2:defineAnnoType!60 ' + str(i) + ' "s' + str(i) + '" "x" "=>" blue none\n' for i in range(2, 26)]), +- 'detach_Test' : '2:close!96\n1:close!97\nDETACH\n' ++ 'detach_Test' : '2:close!96\n1:close!97\nDETACH\n', ++ 'specialKeys_overflow_Test' : '0:specialKeys!200 "' + 'A'*80 + '-X"\n' ++ + } + # execute the specified test + if cmd not in testmap: +diff --git a/src/testdir/test_netbeans.vim b/src/testdir/test_netbeans.vim +index e458e38..fad24d0 100644 +--- a/src/testdir/test_netbeans.vim ++++ b/src/testdir/test_netbeans.vim +@@ -961,6 +961,58 @@ func Nb_bwipe_buffer(port) + sleep 10m + endfunc + ++func Nb_specialKeys_overflow(port) ++ call delete("Xnetbeans") ++ call writefile([], "Xnetbeans") ++ ++ " Last line number in the Xnetbeans file. Used to verify the result of the ++ " communication with the netbeans server ++ let g:last = 0 ++ ++ " Establish the connection with the netbeans server ++ exe 'nbstart :localhost:' .. a:port .. ':bunny' ++ call WaitFor('len(ReadXnetbeans()) > (g:last + 2)') ++ let l = ReadXnetbeans() ++ call assert_equal(['AUTH bunny', ++ \ '0:version=0 "2.5"', ++ \ '0:startupDone=0'], l[-3:]) ++ let g:last += 3 ++ ++ " Open the command buffer to communicate with the server ++ split Xcmdbuf ++ let cmdbufnr = bufnr() ++ call WaitFor('len(ReadXnetbeans()) > (g:last + 2)') ++ let l = ReadXnetbeans() ++ call assert_equal('0:fileOpened=0 "Xcmdbuf" T F', ++ \ substitute(l[-3], '".*/', '"', '')) ++ call assert_equal('send: 1:putBufferNumber!15 "Xcmdbuf"', ++ \ substitute(l[-2], '".*/', '"', '')) ++ call assert_equal('1:startDocumentListen!16', l[-1]) ++ let g:last += 3 ++ ++ " Keep the command buffer loaded for communication ++ hide ++ ++ sleep 1m ++ ++ " Open the command buffer to communicate with the server ++ split Xcmdbuf ++ let cmdbufnr = bufnr() ++ call appendbufline(cmdbufnr, '$', 'specialKeys_overflow_Test') ++ call WaitFor('len(ReadXnetbeans()) >= (g:last + 6)') ++ call WaitForAssert({-> assert_match('send: 0:specialKeys!200 "A\{80}-X"', ++ \ ReadXnetbeans()[-1])}) ++ ++ " Verify that specialKeys test, still works after the previous junk ++ call appendbufline(cmdbufnr, '$', 'specialKeys_Test') ++ call WaitFor('len(ReadXnetbeans()) >= (g:last + 1)') ++ call WaitForAssert({-> assert_match('^send: 0:specialKeys!91 "F12 F13 C-F13"$', ++ \ ReadXnetbeans()[-1])}) ++ let g:last += 1 ++ ++ sleep 10m ++endfunc ++ + " This test used to reference a buffer after it was freed leading to an ASAN + " error. + func Test_nb_bwipe_buffer() +@@ -970,4 +1022,9 @@ func Test_nb_bwipe_buffer() + nbclose + endfunc + ++" Verify that the specialKeys argument does not overflow ++func Test_nb_specialKeys_overflow() ++ call s:run_server('Nb_specialKeys_overflow') ++endfunc ++ + " vim: shiftwidth=2 sts=2 expandtab +-- +2.41.1 diff --git a/0003-patch-9.2.0073-security-possible-command-injection-u.patch b/0003-patch-9.2.0073-security-possible-command-injection-u.patch new file mode 100644 index 0000000..4105f05 --- /dev/null +++ b/0003-patch-9.2.0073-security-possible-command-injection-u.patch @@ -0,0 +1,107 @@ +From 79348dbbc09332130f4c86045e1541d68514fcc1 Mon Sep 17 00:00:00 2001 +From: Christian Brabandt +Date: Sun, 22 Feb 2026 21:24:48 +0000 +Subject: [PATCH] patch 9.2.0073: [security]: possible command injection using + netrw + +Problem: [security]: Insufficient validation of hostname and port in + netrw URIs allows command injection via shell metacharacters + (ehdgks0627, un3xploitable). +Solution: Implement stricter RFC1123 hostname and IP validation. + Use shellescape() for the provided hostname and port. + +Github Advisory: +https://github.com/vim/vim/security/advisories/GHSA-m3xh-9434-g336 + +Signed-off-by: Christian Brabandt +--- + runtime/autoload/netrw.vim | 45 ++++++++++++++++++++----------- + src/testdir/test_plugin_netrw.vim | 11 ++++++++ + 2 files changed, 40 insertions(+), 16 deletions(-) + create mode 100644 src/testdir/test_plugin_netrw.vim + +diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim +index c98e236..50d052a 100644 +--- a/runtime/autoload/netrw.vim ++++ b/runtime/autoload/netrw.vim +@@ -3341,13 +3341,26 @@ endfun + + " s:NetrwValidateHostname: Validate that the hostname is valid {{{2 + " Input: +-" hostname ++" hostname, may include an optional username, e.g. user@hostname ++" allow a alphanumeric hostname or an IPv(4/6) address + " Output: + " true if g:netrw_machine is valid according to RFC1123 #Section 2 + fun! s:NetrwValidateHostname(hostname) +- " RFC1123#section-2 mandates, a valid hostname starts with letters or digits +- " so reject everyhing else +- return a:hostname =~? '^[a-z0-9]' ++ " Username: ++ let user_pat = '\%([a-zA-Z0-9._-]\+@\)\?' ++ " Hostname: 1-64 chars, alphanumeric/dots/hyphens. ++ " No underscores. No leading/trailing dots/hyphens. ++ let host_pat = '[a-zA-Z0-9]\%([-a-zA-Z0-9.]{,62}[a-zA-Z0-9]\)\?$' ++ ++ " IPv4: 1-3 digits separated by dots ++ let ipv4_pat = '\%(\d\{1,3}\.\)\{3\}\d\{1,3\}$' ++ ++ " IPv6: Hex, colons, and optional brackets ++ let ipv6_pat = '\[\?\%([a-fA-F0-9:]\{2,}\)\+\]\?$' ++ ++ return a:hostname =~? '^'.user_pat.host_pat || ++ \ a:hostname =~? '^'.user_pat.ipv4_pat || ++ \ a:hostname =~? '^'.user_pat.ipv6_pat + endfun + + " ------------------------------------------------------------------------ +@@ -11883,18 +11896,18 @@ endfun + " a correct command for use with a system() call + fun! s:MakeSshCmd(sshcmd) + " call Dfunc("s:MakeSshCmd(sshcmd<".a:sshcmd.">) user<".s:user."> machine<".s:machine.">") +- if s:user == "" +- let sshcmd = substitute(a:sshcmd,'\',s:machine,'') +- else +- let sshcmd = substitute(a:sshcmd,'\',s:user."@".s:machine,'') +- endif +- if exists("g:netrw_port") && g:netrw_port != "" +- let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.g:netrw_port,'') +- elseif exists("s:port") && s:port != "" +- let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.s:port,'') +- else +- let sshcmd= substitute(sshcmd,"USEPORT ",'','') +- endif ++ let machine = shellescape(s:machine, 1) ++ if s:user != '' ++ let machine = shellescape(s:user, 1).'@'.machine ++ endif ++ let sshcmd = substitute(a:sshcmd,'\',machine,'') ++ if exists("g:netrw_port") && g:netrw_port != "" ++ let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.shellescape(g:netrw_port,1),'') ++ elseif exists("s:port") && s:port != "" ++ let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.shellescape(s:port,1),'') ++ else ++ let sshcmd= substitute(sshcmd,"USEPORT ",'','') ++ endif + " call Dret("s:MakeSshCmd <".sshcmd.">") + return sshcmd + endfun +diff --git a/src/testdir/test_plugin_netrw.vim b/src/testdir/test_plugin_netrw.vim +new file mode 100644 +index 0000000..c512304 +--- /dev/null ++++ b/src/testdir/test_plugin_netrw.vim +@@ -0,0 +1,11 @@ ++" Tests for the netrw plugin ++ ++source check.vim ++ ++func Test_netrw_reject_evil_hostname() ++ let msg = execute(':e scp://x;touch RCE;x/dir/') ++ let msg = split(msg, "\n")[-1] ++ call assert_match('Rejecting invalid hostname', msg) ++endfunction ++ ++" vim:ts=8 sts=2 sw=2 et +-- +2.41.1 diff --git a/0003-runtime-netrw-upstream-snapshot-of-v179.patch b/0003-runtime-netrw-upstream-snapshot-of-v179.patch new file mode 100644 index 0000000..33b66e1 --- /dev/null +++ b/0003-runtime-netrw-upstream-snapshot-of-v179.patch @@ -0,0 +1,109 @@ +From 29d596c80ab08a1f966a16912788576a14c217ad Mon Sep 17 00:00:00 2001 +From: Luca Saccarola +Date: Tue, 4 Mar 2025 20:36:31 +0100 +Subject: [PATCH] runtime(netrw): upstream snapshot of v179 + +closes: #16787 + +Signed-off-by: Luca Saccarola +Signed-off-by: Christian Brabandt +--- + runtime/autoload/netrw.vim | 41 +++++++++++++++++++++++++++++++++++++- + 1 file changed, 40 insertions(+), 1 deletion(-) + +diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim +index d2df846..c98e236 100644 +--- a/runtime/autoload/netrw.vim ++++ b/runtime/autoload/netrw.vim +@@ -68,7 +68,7 @@ setl cpo&vim + " Usage: netrw#ErrorMsg(s:NOTE | s:WARNING | s:ERROR,"some message",error-number) + " netrw#ErrorMsg(s:NOTE | s:WARNING | s:ERROR,["message1","message2",...],error-number) + " (this function can optionally take a list of messages) +-" Dec 2, 2019 : max errnum currently is 106 ++" Mar 03, 2025 : max errnum currently is 107 + fun! netrw#ErrorMsg(level,msg,errnum) + " call Dfunc("netrw#ErrorMsg(level=".a:level." msg<".a:msg."> errnum=".a:errnum.") g:netrw_use_errorwindow=".g:netrw_use_errorwindow) + +@@ -1450,6 +1450,10 @@ fun! netrw#Obtain(islocal,fname,...) + call s:SetupNetrwStatusLine('%f %h%m%r%=%9*Obtaining '.a:fname) + endif + call s:NetrwMethod(b:netrw_curdir) ++ if !s:NetrwValidateHostname(g:netrw_machine) ++ call netrw#ErrorMsg(s:ERROR,"Rejecting invalid hostname: <" .. g:netrw_machine .. ">",107) ++ return ++ endif + + if b:netrw_method == 4 + " obtain file using scp +@@ -2140,6 +2144,10 @@ fun! netrw#NetRead(mode,...) + " call Dret("netrw#NetRead : unsupported method") + return + endif ++ if !s:NetrwValidateHostname(g:netrw_machine) ++ call netrw#ErrorMsg(s:ERROR,"Rejecting invalid hostname: <" .. g:netrw_machine .. ">",107) ++ return ++ endif + let tmpfile= s:GetTempfile(b:netrw_fname) " apply correct suffix + + " Check whether or not NetrwBrowse() should be handling this request +@@ -2562,6 +2570,10 @@ fun! netrw#NetWrite(...) range + " call Dfunc("netrw#NetWrite : unsupported method") + return + endif ++ if !s:NetrwValidateHostname(g:netrw_machine) ++ call netrw#ErrorMsg(s:ERROR,"Rejecting invalid hostname: <" .. g:netrw_machine .. ">",107) ++ return ++ endif + + " ============= + " NetWrite: Perform Protocol-Based Write {{{3 +@@ -3327,6 +3339,17 @@ fun! s:NetrwMethod(choice) + " call Dret("s:NetrwMethod : b:netrw_method=".b:netrw_method." g:netrw_port=".g:netrw_port) + endfun + ++" s:NetrwValidateHostname: Validate that the hostname is valid {{{2 ++" Input: ++" hostname ++" Output: ++" true if g:netrw_machine is valid according to RFC1123 #Section 2 ++fun! s:NetrwValidateHostname(hostname) ++ " RFC1123#section-2 mandates, a valid hostname starts with letters or digits ++ " so reject everyhing else ++ return a:hostname =~? '^[a-z0-9]' ++endfun ++ + " ------------------------------------------------------------------------ + " NetReadFixup: this sort of function is typically written by the user {{{2 + " to handle extra junk that their system's ftp dumps +@@ -8842,6 +8865,10 @@ fun! s:NetrwUpload(fname,tgt,...) + + elseif a:tgt =~ '^ftp:' + call s:NetrwMethod(a:tgt) ++ if !s:NetrwValidateHostname(g:netrw_machine) ++ call netrw#ErrorMsg(s:ERROR,"Rejecting invalid hostname: <" .. g:netrw_machine .. ">",107) ++ return ++ endif + + if b:netrw_method == 2 + " handle uploading a list of files via ftp+.netrc +@@ -11528,6 +11555,18 @@ fun! netrw#Call(funcname,...) + return call("s:".a:funcname,a:000) + endfun + ++" --------------------------------------------------------------------- ++" netrw#LogLevel: returns the specified loglevel ++fun! netrw#LogLevel(level) ++ if a:level == 'WARNING' ++ return s:WARNING ++ elseif a:level == 'NOTE' ++ return s:NOTE ++ elseif a:level == 'ERROR' ++ return s:ERROR ++ endif ++endfun ++ + " --------------------------------------------------------------------- + " netrw#Expose: allows UserMaps and pchk to look at otherwise script-local variables {{{2 + " I expect this function to be used in +-- +2.41.1 diff --git a/0004-patch-9.2.0074-security-Crash-with-overlong-emacs-ta.patch b/0004-patch-9.2.0074-security-Crash-with-overlong-emacs-ta.patch new file mode 100644 index 0000000..2ab0e9c --- /dev/null +++ b/0004-patch-9.2.0074-security-Crash-with-overlong-emacs-ta.patch @@ -0,0 +1,59 @@ +From f6a7f469a9c0d09e84cd6cb46c3a9e76f684da2d Mon Sep 17 00:00:00 2001 +From: Christian Brabandt +Date: Mon, 23 Feb 2026 18:30:11 +0000 +Subject: [PATCH] patch 9.2.0074: [security]: Crash with overlong emacs tag + file + +Problem: Crash with overlong emacs tag file, because of an OOB buffer + read (ehdgks0627, un3xploitable) +Solution: Check for end of buffer and return early. + +Github Advisory: +https://github.com/vim/vim/security/advisories/GHSA-h4mf-vg97-hj8j + +Signed-off-by: Christian Brabandt +--- + src/tag.c | 3 +++ + src/testdir/test_taglist.vim | 15 +++++++++++++++ + 2 files changed, 18 insertions(+) + +diff --git a/src/tag.c b/src/tag.c +index 99276a3..9653c66 100644 +--- a/src/tag.c ++++ b/src/tag.c +@@ -1898,6 +1898,9 @@ emacs_tags_new_filename(findtags_state_T *st) + + for (p = st->ebuf; *p && *p != ','; p++) + ; ++ // invalid ++ if (*p == NUL) ++ return; + *p = NUL; + + // check for an included tags file. +diff --git a/src/testdir/test_taglist.vim b/src/testdir/test_taglist.vim +index 39e78bc..5c339de 100644 +--- a/src/testdir/test_taglist.vim ++++ b/src/testdir/test_taglist.vim +@@ -260,4 +260,19 @@ func Test_tag_complete_with_overlong_line() + set tags& + endfunc + ++" This used to crash Vim ++func Test_evil_emacs_tagfile() ++ CheckFeature emacs_tags ++ let longline = repeat('a', 515) ++ call writefile([ ++ \ "\x0c", ++ \ longline ++ \ ], 'Xtags', 'D') ++ set tags=Xtags ++ ++ call assert_fails(':tag a', 'E426:') ++ ++ set tags& ++endfunc ++ + " vim: shiftwidth=2 sts=2 expandtab +-- +2.41.1 diff --git a/0005-patch-9.2.0075-security-Buffer-underflow-with-emacs-.patch b/0005-patch-9.2.0075-security-Buffer-underflow-with-emacs-.patch new file mode 100644 index 0000000..c044c52 --- /dev/null +++ b/0005-patch-9.2.0075-security-Buffer-underflow-with-emacs-.patch @@ -0,0 +1,66 @@ +From 9b7dfa2948c9e1e5e32a5812812d580c7879f4a0 Mon Sep 17 00:00:00 2001 +From: Christian Brabandt +Date: Mon, 23 Feb 2026 19:35:25 +0000 +Subject: [PATCH] patch 9.2.0075: [security]: Buffer underflow with emacs tag + file + +Problem: When parsing a malformed Emacs-style tags file, a 1-byte + heap-buffer-underflow read occurs if the 0x7f delimiter + appears at the very beginning of a line. This happens + because the code attempts to scan backward for a tag + name from the delimiter without checking if space exists. + (ehdgks0627, un3xploitable) +Solution: Add a check to ensure the delimiter (p_7f) is not at the + start of the buffer (lbuf) before attempting to isolate + the tag name. + +GitHub Advisory: +https://github.com/vim/vim/security/advisories/GHSA-xcc8-r6c5-hvwv + +Signed-off-by: Christian Brabandt +--- + src/tag.c | 3 +++ + src/testdir/test_taglist.vim | 16 ++++++++++++++++ + 2 files changed, 19 insertions(+) + +diff --git a/src/tag.c b/src/tag.c +index 9653c66..6d9e66b 100644 +--- a/src/tag.c ++++ b/src/tag.c +@@ -2019,6 +2019,9 @@ etag_fail: + } + else // second format: isolate tagname + { ++ if (p_7f == lbuf) ++ goto etag_fail; ++ + // find end of tagname + for (p = p_7f - 1; !vim_iswordc(*p); --p) + if (p == lbuf) +diff --git a/src/testdir/test_taglist.vim b/src/testdir/test_taglist.vim +index 5c339de..461e46e 100644 +--- a/src/testdir/test_taglist.vim ++++ b/src/testdir/test_taglist.vim +@@ -275,4 +275,20 @@ func Test_evil_emacs_tagfile() + set tags& + endfunc + ++" This used to crash Vim due to a heap-buffer-underflow ++func Test_emacs_tagfile_underflow() ++ CheckFeature emacs_tags ++ " The sequence from the crash artifact: ++ let lines = [ ++ \ "\x0c\xff\xffT\x19\x8a", ++ \ "\x19\x19\x0dtags\x19\x19\x19\x00\xff\xff\xff", ++ \ "\x7f3\x0c" ++ \ ] ++ call writefile(lines, 'Xtags', 'D') ++ set tags=Xtags ++ call assert_fails(':tag a', 'E431:') ++ ++ set tags& ++endfunc ++ + " vim: shiftwidth=2 sts=2 expandtab +-- +2.41.1 diff --git a/0006-patch-9.2.0076-security-buffer-overflow-in-terminal-.patch b/0006-patch-9.2.0076-security-buffer-overflow-in-terminal-.patch new file mode 100644 index 0000000..adecb57 --- /dev/null +++ b/0006-patch-9.2.0076-security-buffer-overflow-in-terminal-.patch @@ -0,0 +1,157 @@ +From bb6de2105b160e729c340631435cd62f3e69bd32 Mon Sep 17 00:00:00 2001 +From: Christian Brabandt +Date: Mon, 23 Feb 2026 20:29:43 +0000 +Subject: [PATCH] patch 9.2.0076: [security]: buffer-overflow in terminal + handling + +Problem: When processing terminal output with many combining characters + from supplementary planes (4-byte UTF-8), a heap-buffer + overflow occurs. Additionally, the loop iterating over + cell characters can read past the end of the vterm array + (ehdgks0627, un3xploitable). +Solution: Use VTERM_MAX_CHARS_PER_CELL * 4 for ga_grow() to ensure + sufficient space. Add a boundary check to the character + loop to prevent index out-of-bounds access. + +Github Advisory: +https://github.com/vim/vim/security/advisories/GHSA-rvj2-jrf9-2phg + +Signed-off-by: Christian Brabandt +--- + src/terminal.c | 5 +- + .../samples/terminal_max_combining_chars.txt | 80 +++++++++++++++++++ + src/testdir/test_terminal3.vim | 13 +++ + 3 files changed, 96 insertions(+), 2 deletions(-) + create mode 100644 src/testdir/samples/terminal_max_combining_chars.txt + +diff --git a/src/terminal.c b/src/terminal.c +index 37cd0f2..0eee0f6 100644 +--- a/src/terminal.c ++++ b/src/terminal.c +@@ -3502,12 +3502,13 @@ handle_pushline(int cols, const VTermScreenCell *cells, void *user) + { + for (col = 0; col < len; col += cells[col].width) + { +- if (ga_grow(&ga, MB_MAXBYTES) == FAIL) ++ if (ga_grow(&ga, VTERM_MAX_CHARS_PER_CELL * 4) == FAIL) + { + ga.ga_len = 0; + break; + } +- for (i = 0; (c = cells[col].chars[i]) > 0 || i == 0; ++i) ++ for (i = 0; i < VTERM_MAX_CHARS_PER_CELL && ++ ((c = cells[col].chars[i]) > 0 || i == 0); ++i) + ga.ga_len += utf_char2bytes(c == NUL ? ' ' : c, + (char_u *)ga.ga_data + ga.ga_len); + cell2cellattr(&cells[col], &p[col]); +diff --git a/src/testdir/samples/terminal_max_combining_chars.txt b/src/testdir/samples/terminal_max_combining_chars.txt +new file mode 100644 +index 0000000..a4f508d +--- /dev/null ++++ b/src/testdir/samples/terminal_max_combining_chars.txt +@@ -0,0 +1,80 @@ ++padding line 000 ++padding line 001 ++padding line 002 ++padding line 003 ++padding line 004 ++padding line 005 ++padding line 006 ++padding line 007 ++padding line 008 ++padding line 009 ++padding line 010 ++padding line 011 ++padding line 012 ++padding line 013 ++padding line 014 ++padding line 015 ++padding line 016 ++padding line 017 ++padding line 018 ++padding line 019 ++padding line 020 ++padding line 021 ++padding line 022 ++padding line 023 ++padding line 024 ++padding line 025 ++padding line 026 ++padding line 027 ++padding line 028 ++padding line 029 ++padding line 030 ++padding line 031 ++padding line 032 ++padding line 033 ++padding line 034 ++padding line 035 ++padding line 036 ++padding line 037 ++padding line 038 ++padding line 039 ++padding line 040 ++padding line 041 ++padding line 042 ++padding line 043 ++padding line 044 ++padding line 045 ++padding line 046 ++padding line 047 ++padding line 048 ++padding line 049 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 ++AAAAAAAAAAAAAAAAAAAAAAAAAAAA𐀀󠄀󠄁󠄂󠄃󠄄 +diff --git a/src/testdir/test_terminal3.vim b/src/testdir/test_terminal3.vim +index 96a9e63..c604af8 100644 +--- a/src/testdir/test_terminal3.vim ++++ b/src/testdir/test_terminal3.vim +@@ -931,5 +931,18 @@ func Test_terminal_term_start_error() + delfunc s:term_start_error + endfunc + ++func Test_terminal_max_combining_chars() ++ " somehow doesn't work on MS-Windows ++ CheckUnix ++ let cmd = "cat samples/terminal_max_combining_chars.txt\" ++ let buf = Run_shell_in_terminal({'term_rows': 15, 'term_cols': 35}) ++ call TermWait(buf) ++ call term_sendkeys(buf, cmd) ++ " last char is a space with many combining chars ++ call WaitForAssert({-> assert_match("AAAAAAAAAAAAAAAAAAAAAAAAAAAA.", term_getline(buf, 14))}) ++ ++ call term_sendkeys(buf, "exit\r") ++ exe buf . "bwipe!" ++endfunc + + " vim: shiftwidth=2 sts=2 expandtab +-- +2.41.1 diff --git a/0007-patch-9.2.0077-security-Crash-when-recovering-a-corr.patch b/0007-patch-9.2.0077-security-Crash-when-recovering-a-corr.patch new file mode 100644 index 0000000..d9edd8b --- /dev/null +++ b/0007-patch-9.2.0077-security-Crash-when-recovering-a-corr.patch @@ -0,0 +1,86 @@ +From ee48091fb5c722bf263364980595c7e8ae51ed41 Mon Sep 17 00:00:00 2001 +From: Christian Brabandt +Date: Mon, 23 Feb 2026 21:42:39 +0000 +Subject: [PATCH] patch 9.2.0077: [security]: Crash when recovering a corrupted + swap file + +Problem: memline: a crafted swap files with bogus pe_page_count/pe_bnum + values could cause a multi-GB allocation via mf_get(), and + invalid pe_old_lnum/pe_line_count values could cause a SEGV + when passed to readfile() (ehdgks0627, un3xploitable) +Solution: Add bounds checks on pe_page_count and pe_bnum against + mf_blocknr_max before descending into the block tree, and + validate pe_old_lnum >= 1 and pe_line_count > 0 before calling + readfile(). + +Github Advisory: +https://github.com/vim/vim/security/advisories/GHSA-r2gw-2x48-jj5p + +Signed-off-by: Christian Brabandt +--- + Filelist | 1 + + src/memline.c | 29 +++++++++++++++++-- + 2 files changed, 30 insertions(+), 2 deletions(-) + +diff --git a/Filelist b/Filelist +index 8a7b1e9..eea6f0c 100644 +--- a/Filelist ++++ b/Filelist +@@ -212,6 +212,7 @@ SRC_ALL = \ + src/testdir/pyxfile/*.py \ + src/testdir/dumps/*.dump \ + src/testdir/dumps/*.vim \ ++ src/testdir/samples/*.swp \ + src/testdir/samples/*.txt \ + src/testdir/samples/test000 \ + src/testdir/color_ramp.vim \ +diff --git a/src/memline.c b/src/memline.c +index cf2dc8c..0fb7a8b 100644 +--- a/src/memline.c ++++ b/src/memline.c +@@ -1597,8 +1597,12 @@ ml_recover(int checkext) + if (!cannot_open) + { + line_count = pp->pb_pointer[idx].pe_line_count; +- if (readfile(curbuf->b_ffname, NULL, lnum, +- pp->pb_pointer[idx].pe_old_lnum - 1, ++ linenr_T pe_old_lnum = pp->pb_pointer[idx].pe_old_lnum; ++ // Validate pe_line_count and pe_old_lnum from the ++ // untrusted swap file before passing to readfile(). ++ if (line_count <= 0 || pe_old_lnum < 1 || ++ readfile(curbuf->b_ffname, NULL, lnum, ++ pe_old_lnum - 1, + line_count, NULL, 0) != OK) + cannot_open = TRUE; + else +@@ -1629,6 +1633,27 @@ ml_recover(int checkext) + bnum = pp->pb_pointer[idx].pe_bnum; + line_count = pp->pb_pointer[idx].pe_line_count; + page_count = pp->pb_pointer[idx].pe_page_count; ++ // Validate pe_bnum and pe_page_count from the untrusted ++ // swap file before passing to mf_get(), which uses ++ // page_count to calculate allocation size. A bogus value ++ // (e.g. 0x40000000) would cause a multi-GB allocation. ++ // pe_page_count must be >= 1 and bnum + page_count must ++ // not exceed the number of pages in the swap file. ++ if (page_count < 1 ++ || bnum + page_count > mfp->mf_blocknr_max + 1) ++ { ++ ++error; ++ ml_append(lnum++, ++ (char_u *)_("???ILLEGAL BLOCK NUMBER"), ++ (colnr_T)0, TRUE); ++ // Skip this entry and pop back up the stack to keep ++ // recovering whatever else we can. ++ idx = ip->ip_index + 1; ++ bnum = ip->ip_bnum; ++ page_count = 1; ++ --buf->b_ml.ml_stack_top; ++ continue; ++ } + idx = 0; + continue; + } +-- +2.41.1 + diff --git a/0008-patch-9.2.0078-security-stack-buffer-overflow-in-bui.patch b/0008-patch-9.2.0078-security-stack-buffer-overflow-in-bui.patch new file mode 100644 index 0000000..9ac755d --- /dev/null +++ b/0008-patch-9.2.0078-security-stack-buffer-overflow-in-bui.patch @@ -0,0 +1,39 @@ +From 4e5b9e31cb7484ad156fba995fdce3c9b075b5fd Mon Sep 17 00:00:00 2001 +From: Christian Brabandt +Date: Tue, 24 Feb 2026 20:29:20 +0000 +Subject: [PATCH] patch 9.2.0078: [security]: stack-buffer-overflow in + build_stl_str_hl() + +Problem: A stack-buffer-overflow occurs when rendering a statusline + with a multi-byte fill character on a very wide terminal. + The size check in build_stl_str_hl() uses the cell width + rather than the byte length, allowing the subsequent fill + loop to write beyond the 4096-byte MAXPATHL buffer + (ehdgks0627, un3xploitable). +Solution: Update the size check to account for the byte length of + the fill character (using MB_CHAR2LEN). + +Github Advisory: +https://github.com/vim/vim/security/advisories/GHSA-gmqx-prf2-8mwf + +Signed-off-by: Christian Brabandt +--- + src/buffer.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/buffer.c b/src/buffer.c +index 70c70c7..f98ea9f 100644 +--- a/src/buffer.c ++++ b/src/buffer.c +@@ -5133,7 +5133,8 @@ build_stl_str_hl( + } + width = maxwidth; + } +- else if (width < maxwidth && STRLEN(out) + maxwidth - width + 1 < outlen) ++ else if (width < maxwidth && ++ STRLEN(out) + (maxwidth - width) * MB_CHAR2LEN(fillchar) + 1 < outlen) + { + // Find how many separators there are, which we will use when + // figuring out how many groups there are. +-- +2.41.1 diff --git a/vim.spec b/vim.spec index 1d92138..9519cf7 100644 --- a/vim.spec +++ b/vim.spec @@ -28,7 +28,7 @@ Summary: The VIM editor URL: http://www.vim.org/ Name: vim Version: 9.0.2092 -Release: 12%{?dist} +Release: 13%{?dist} License: Vim and MIT and GPL v2+ Source0: https://github.com/vim/vim/archive/refs/tags/v%{version}.tar.gz Source1: virc @@ -77,6 +77,24 @@ Patch0021: CVE-2025-53905-patch-9.1.1552-security-path-traversal-issue-in-tar..p # CVE-2025-53906 Patch0022: CVE-2025-53906-patch-9.1.1551-security-path-traversal-issue-in-zip..patch +# CVE-2026-25749 +Patch0023: 0001-patch-9.1.2132-security-buffer-overflow-in-helpfile-.patch +# CVE-2026-26269 +Patch0024: 0002-patch-9.1.2148-security-Buffer-overflow-in-netbeans-.patch +# CVE-2026-28417 +Patch0025: 0003-runtime-netrw-upstream-snapshot-of-v179.patch +Patch0026: 0003-patch-9.2.0073-security-possible-command-injection-u.patch +# CVE-2026-28418 +Patch0027: 0004-patch-9.2.0074-security-Crash-with-overlong-emacs-ta.patch +# CVE-2026-28419 +Patch0028: 0005-patch-9.2.0075-security-Buffer-underflow-with-emacs-.patch +# CVE-2026-28420 +Patch0029: 0006-patch-9.2.0076-security-buffer-overflow-in-terminal-.patch +# CVE-2026-28421 +Patch0030: 0007-patch-9.2.0077-security-Crash-when-recovering-a-corr.patch +# CVE-2026-28422 +Patch0031: 0008-patch-9.2.0078-security-stack-buffer-overflow-in-bui.patch + Patch3000: vim-7.3-manpage-typo-668894-675480.patch Patch3001: vim-manpagefixes-948566.patch Patch3002: vim-7.4-globalsyntax.patch @@ -877,6 +895,11 @@ LC_ALL=en_US.UTF-8 make test || echo "Warning: tests have failure." %changelog +* Wed Mar 11 2026 licunlongli - 9.0.2092-13 +- [Type] security +- [DESC] fix CVE-2026-25749, CVE-2026-26269, CVE-2026-28417, CVE-2026-28418 + CVE-2026-28419, CVE-2026-28420, CVE-2026-28421, CVE-2026-28422 + * Fri Jul 25 2025 licunlong - 9.0.2092-12 - [Type] security - [DESC] fix CVE-2025-53905, CVE-2025-53906 -- Gitee