From 3cc246980b800454dda0603af410c77a8c1926e0 Mon Sep 17 00:00:00 2001
From: Christian Brabandt <cb@256bit.org>
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

CVE: CVE-2026-26269
Upstream-Status: Backport [https://github.com/vim/vim/commit/c5f312aad8e4179e437f81ad39a860cd0ef11970]

Backport Changes:
- Excluded changes to src/version.c from this backport. This file only tracks
  upstream version increments. We are applying a security fix, not a version
  upgrade. These changes were skipped to maintain current package versioning
  and avoid merge conflicts.

Signed-off-by: Christian Brabandt <cb@256bit.org>
(cherry picked from commit c5f312aad8e4179e437f81ad39a860cd0ef11970)
Signed-off-by: Anil Dongare <adongare@cisco.com>
---
 runtime/doc/version9.txt      |  5 +++
 src/netbeans.c                |  2 +-
 src/testdir/test_netbeans.py  |  4 ++-
 src/testdir/test_netbeans.vim | 57 +++++++++++++++++++++++++++++++++++
 4 files changed, 66 insertions(+), 2 deletions(-)

diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index b82071757..b32400f17 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -41899,4 +41899,9 @@ features, but does not include runtime file changes (syntax, indent, ftplugin,
 documentation, etc.)
 
 
+Patch 9.1.2148
+Problem:  [security]: Buffer overflow in netbeans special_keys() handling
+Solution: Limit writing to max KEYBUFLEN bytes to prevent writing out of
+          bounds.
+
  vim:tw=78:ts=8:noet:ft=help:norl:fdm=manual:nofoldenable
diff --git a/src/netbeans.c b/src/netbeans.c
index 4f5378512..8a341a20b 100644
--- a/src/netbeans.c
+++ b/src/netbeans.c
@@ -2302,7 +2302,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 0d6b09680..585886fb4 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 d3d5e8baf..d1be5066e 100644
--- a/src/testdir/test_netbeans.vim
+++ b/src/testdir/test_netbeans.vim
@@ -958,6 +958,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()
@@ -967,4 +1019,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.43.7

