Fix CVE-2026-52858: possible code execution with python3complete
Backport two upstream commits to fix CVE-2026-52858, which allowed arbitrary code execution via python3complete and pythoncomplete omni-completion plugins. Patch 9.2.0561 disables execution of import/from statements in the Python completion plugins by default, adding a g:pythoncomplete_allow_import opt-in variable for users who trust their buffer contents. Patch 9.2.0568 is a follow-up fix that re-imports the vim module inside evalsource() so the opt-in variable check works correctly. CVE: CVE-2026-52858 Upstream patches: -4b850457e1.patch -868ad62cb8.patch Resolves: RHEL-186649 This commit was backported by Ymir, a Red Hat Enterprise Linux software maintenance AI agent. Assisted-by: Ymir
This commit is contained in:
parent
12deb611d3
commit
a2455d25a1
155
0001-patch-9.2.0561-security-possible-code-execution-with.patch
Normal file
155
0001-patch-9.2.0561-security-possible-code-execution-with.patch
Normal file
@ -0,0 +1,155 @@
|
||||
From c5bdbe42dccebaf741c9ed6ee32a7c2c75e84dd8 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Brabandt <cb@256bit.org>
|
||||
Date: Fri, 29 May 2026 19:05:53 +0000
|
||||
Subject: [PATCH] patch 9.2.0561: [security]: possible code execution with
|
||||
python3complete
|
||||
|
||||
Problem: [security]: possible code execution with python3complete
|
||||
Solution: Disable execution of import/from statements
|
||||
|
||||
Github Security Advisory:
|
||||
https://github.com/vim/vim/security/advisories/GHSA-52mc-rq6p-rc7c
|
||||
|
||||
Signed-off-by: Christian Brabandt <cb@256bit.org>
|
||||
---
|
||||
runtime/autoload/README.txt | 1 +
|
||||
runtime/autoload/python3complete.vim | 17 ++++++++++++++---
|
||||
runtime/autoload/pythoncomplete.vim | 17 ++++++++++++++---
|
||||
runtime/doc/filetype.txt | 15 ++++++++++++++-
|
||||
4 files changed, 43 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/runtime/autoload/README.txt b/runtime/autoload/README.txt
|
||||
--- a/runtime/autoload/README.txt
|
||||
+++ b/runtime/autoload/README.txt
|
||||
@@ -17,6 +17,7 @@
|
||||
javascriptcomplete.vim Javascript
|
||||
phpcomplete.vim PHP
|
||||
pythoncomplete.vim Python
|
||||
+python3complete.vim Python
|
||||
rubycomplete.vim Ruby
|
||||
syntaxcomplete.vim from syntax highlighting
|
||||
xmlcomplete.vim XML (uses files in the xml directory)
|
||||
diff --git a/runtime/autoload/python3complete.vim b/runtime/autoload/python3complete.vim
|
||||
--- a/runtime/autoload/python3complete.vim
|
||||
+++ b/runtime/autoload/python3complete.vim
|
||||
@@ -14,6 +14,10 @@
|
||||
" i.e. "import url<c-x,c-o>"
|
||||
" Continue parsing on invalid line??
|
||||
"
|
||||
+" v 0.10 by Vim project
|
||||
+" * disables importing local modules, unless the global Vim variable
|
||||
+" g:pythoncomplete_allow_import is set to non-zero
|
||||
+"
|
||||
|
||||
" v 0.9
|
||||
" * Fixed docstring parsing for classes and functions
|
||||
@@ -130,11 +134,20 @@
|
||||
|
||||
def evalsource(self,text,line=0):
|
||||
sc = self.parser.parse(text,line)
|
||||
+ try: allow_imports = int(
|
||||
+ vim.eval("get(g:, 'pythoncomplete_allow_import', 0)"))
|
||||
+ except Exception:
|
||||
+ allow_imports = 0
|
||||
src = sc.get_code()
|
||||
dbg("source: %s" % src)
|
||||
try: exec(src,self.compldict)
|
||||
except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1]))
|
||||
for l in sc.locals:
|
||||
+ # Executing import/from statements harvested from the buffer runs
|
||||
+ # arbitrary package code; only do so when the user opted in.
|
||||
+ if not allow_imports and (l.startswith('import')
|
||||
+ or l.startswith('from ')):
|
||||
+ continue
|
||||
try: exec(l,self.compldict)
|
||||
except: dbg("locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l))
|
||||
|
||||
@@ -298,13 +311,11 @@
|
||||
def get_code(self):
|
||||
str = ""
|
||||
if len(self.docstr) > 0: str += repr(self.docstr)+'\n'
|
||||
- for l in self.locals:
|
||||
- if l.startswith('import'): str += l+'\n'
|
||||
str += 'class _PyCmplNoType:\n def __getattr__(self,name):\n return None\n'
|
||||
for sub in self.subscopes:
|
||||
str += sub.get_code()
|
||||
for l in self.locals:
|
||||
- if not l.startswith('import'): str += l+'\n'
|
||||
+ if not l.startswith('import') and not l.startswith('from '): str += l+'\n'
|
||||
|
||||
return str
|
||||
|
||||
diff --git a/runtime/autoload/pythoncomplete.vim b/runtime/autoload/pythoncomplete.vim
|
||||
--- a/runtime/autoload/pythoncomplete.vim
|
||||
+++ b/runtime/autoload/pythoncomplete.vim
|
||||
@@ -12,6 +12,10 @@
|
||||
" i.e. "import url<c-x,c-o>"
|
||||
" Continue parsing on invalid line??
|
||||
"
|
||||
+" v 0.10 by Vim project
|
||||
+" * disables importing local modules, unless the global Vim variable
|
||||
+" g:pythoncomplete_allow_import is set to non-zero
|
||||
+"
|
||||
|
||||
" v 0.9
|
||||
" * Fixed docstring parsing for classes and functions
|
||||
@@ -147,11 +151,20 @@
|
||||
|
||||
def evalsource(self,text,line=0):
|
||||
sc = self.parser.parse(text,line)
|
||||
+ try: allow_imports = int(
|
||||
+ vim.eval("get(g:, 'pythoncomplete_allow_import', 0)"))
|
||||
+ except Exception:
|
||||
+ allow_imports = 0
|
||||
src = sc.get_code()
|
||||
dbg("source: %s" % src)
|
||||
try: exec(src) in self.compldict
|
||||
except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1]))
|
||||
for l in sc.locals:
|
||||
+ # Executing import/from statements harvested from the buffer runs
|
||||
+ # arbitrary package code; only do so when the user opted in.
|
||||
+ if not allow_imports and (l.startswith('import')
|
||||
+ or l.startswith('from ')):
|
||||
+ continue
|
||||
try: exec(l) in self.compldict
|
||||
except: dbg("locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l))
|
||||
|
||||
@@ -316,13 +329,11 @@
|
||||
def get_code(self):
|
||||
str = ""
|
||||
if len(self.docstr) > 0: str += repr(self.docstr)+'\n'
|
||||
- for l in self.locals:
|
||||
- if l.startswith('import'): str += l+'\n'
|
||||
str += 'class _PyCmplNoType:\n def __getattr__(self,name):\n return None\n'
|
||||
for sub in self.subscopes:
|
||||
str += sub.get_code()
|
||||
for l in self.locals:
|
||||
- if not l.startswith('import'): str += l+'\n'
|
||||
+ if not l.startswith('import') and not l.startswith('from '): str += l+'\n'
|
||||
|
||||
return str
|
||||
|
||||
diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt
|
||||
--- a/runtime/doc/filetype.txt
|
||||
+++ b/runtime/doc/filetype.txt
|
||||
@@ -658,7 +658,20 @@
|
||||
To disable this behavior, set the following variable in your vimrc: >
|
||||
|
||||
let g:python_recommended_style = 0
|
||||
+<
|
||||
+Python omni-completion |compl-omni| is provided by python3complete.vim (or
|
||||
+pythoncomplete.vim) for Vim builds with the |+python|/|+python3| interpreter.
|
||||
+By default it does not inspect the import / from statements found in the
|
||||
+buffer. This means completion of names defined in the buffer itself (classes,
|
||||
+functions, variables) works, but completion of members of imported modules is
|
||||
+not offered.
|
||||
|
||||
+To enable completion of imported module members, set: >
|
||||
+ let g:pythoncomplete_allow_import = 1
|
||||
+<
|
||||
+WARNING: enabling this causes omni-completion to execute the import statements
|
||||
+found in the buffer through Python's import machinery, which runs the imported
|
||||
+modules' top-level code. Only enable this for code you trust.
|
||||
|
||||
QF QUICKFIX *qf.vim* *ft-qf-plugin*
|
||||
|
||||
@ -0,0 +1,84 @@
|
||||
From 43f21e84c7483963fe030a9899532d129a20b05e Mon Sep 17 00:00:00 2001
|
||||
From: thinca <thinca@gmail.com>
|
||||
Date: Sun, 31 May 2026 12:33:07 +0000
|
||||
Subject: [PATCH] patch 9.2.0568: pythoncomplete: g:pythoncomplete_allow_import
|
||||
had no effect
|
||||
|
||||
Problem: The security patch 9.2.0561 added a vim.eval() call inside
|
||||
Completer.evalsource() to honor g:pythoncomplete_allow_import.
|
||||
But the 'vim' module is only imported inside the outer
|
||||
vimcomplete() / vimpy3complete() function, not at the script's
|
||||
top level, so referring to it from a Completer method raises
|
||||
NameError. The surrounding bare 'except' silently swallows
|
||||
the error and leaves allow_imports at 0, meaning the opt-in
|
||||
never takes effect -- 'import os' (and any other
|
||||
buffer-level import) is always skipped, no candidates are
|
||||
produced for 'os.<...>' and
|
||||
Test_popup_and_preview_autocommand() fails on the Windows
|
||||
CI matrix (Linux skips the test because Python 2 is absent).
|
||||
Solution: Re-import 'vim' at the top of evalsource() in both
|
||||
pythoncomplete.vim and python3complete.vim so the eval reads
|
||||
the global, and set g:pythoncomplete_allow_import = 1 in the
|
||||
test (it is the opt-in intended for callers that trust the
|
||||
buffer contents) (thinca).
|
||||
|
||||
closes: #20386
|
||||
|
||||
Signed-off-by: thinca <thinca@gmail.com>
|
||||
Signed-off-by: Christian Brabandt <cb@256bit.org>
|
||||
---
|
||||
runtime/autoload/python3complete.vim | 3 +++
|
||||
runtime/autoload/pythoncomplete.vim | 3 +++
|
||||
src/testdir/test_popup.vim | 4 ++++
|
||||
3 files changed, 10 insertions(+)
|
||||
|
||||
diff --git a/runtime/autoload/python3complete.vim b/runtime/autoload/python3complete.vim
|
||||
index bf79a609d..04344cc0d 100644
|
||||
--- a/runtime/autoload/python3complete.vim
|
||||
+++ b/runtime/autoload/python3complete.vim
|
||||
@@ -132,6 +132,9 @@ class Completer(object):
|
||||
self.parser = PyParser()
|
||||
|
||||
def evalsource(self,text,line=0):
|
||||
+ # vim is imported locally in vimpy3complete(); re-import here so the
|
||||
+ # vim.eval() below works (otherwise NameError, silently caught).
|
||||
+ import vim
|
||||
sc = self.parser.parse(text,line)
|
||||
try: allow_imports = int(
|
||||
vim.eval("get(g:, 'pythoncomplete_allow_import', 0)"))
|
||||
diff --git a/runtime/autoload/pythoncomplete.vim b/runtime/autoload/pythoncomplete.vim
|
||||
index 73e739ded..af9ebb8f6 100644
|
||||
--- a/runtime/autoload/pythoncomplete.vim
|
||||
+++ b/runtime/autoload/pythoncomplete.vim
|
||||
@@ -149,6 +149,9 @@ class Completer(object):
|
||||
self.parser = PyParser()
|
||||
|
||||
def evalsource(self,text,line=0):
|
||||
+ # vim is imported locally in vimcomplete(); re-import here so the
|
||||
+ # vim.eval() below works (otherwise NameError, silently caught).
|
||||
+ import vim
|
||||
sc = self.parser.parse(text,line)
|
||||
try: allow_imports = int(
|
||||
vim.eval("get(g:, 'pythoncomplete_allow_import', 0)"))
|
||||
diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim
|
||||
index c8f8820af..894e2ff1e 100644
|
||||
--- a/src/testdir/test_popup.vim
|
||||
+++ b/src/testdir/test_popup.vim
|
||||
@@ -708,6 +708,9 @@ func Test_popup_and_preview_autocommand()
|
||||
au!
|
||||
au BufAdd * nested tab sball
|
||||
augroup END
|
||||
+ " Let pythoncomplete follow the buffer's 'import os' (off by default
|
||||
+ " since v9.2.0561) so 'os.' can be completed.
|
||||
+ let g:pythoncomplete_allow_import = 1
|
||||
set omnifunc=pythoncomplete#Complete
|
||||
call setline(1, 'import os')
|
||||
" make the line long
|
||||
@@ -730,6 +733,7 @@ func Test_popup_and_preview_autocommand()
|
||||
augroup END
|
||||
augroup! MyBufAdd
|
||||
bw!
|
||||
+ unlet g:pythoncomplete_allow_import
|
||||
endfunc
|
||||
|
||||
func Test_popup_and_previewwindow_dump()
|
||||
14
vim.spec
14
vim.spec
@ -27,7 +27,7 @@ Summary: The VIM editor
|
||||
URL: http://www.vim.org/
|
||||
Name: vim
|
||||
Version: %{baseversion}.%{patchlevel}
|
||||
Release: 39%{?dist}
|
||||
Release: 40%{?dist}
|
||||
License: Vim and MIT
|
||||
Source0: ftp://ftp.vim.org/pub/vim/unix/vim-%{baseversion}-%{patchlevel}.tar.bz2
|
||||
Source1: virc
|
||||
@ -238,6 +238,12 @@ Patch3078: 0001-patch-9.2.0699-security-possible-code-execution-with.patch
|
||||
# https://github.com/vim/vim/commit/f08ab2f4d7d2947c8dd6c179ae08ee6146a2694b
|
||||
# adjusted: netrw path changed from runtime/pack/dist/opt/netrw/autoload/ to runtime/autoload/ (Vim 8.2)
|
||||
Patch3079: 0001-patch-9.2.0495-security-runtime-netrw-code-injectio.patch
|
||||
# RHEL-186649 CVE-2026-52858 possible code execution with python3complete
|
||||
# https://redhat.atlassian.net/browse/RHEL-186649
|
||||
# https://github.com/vim/vim/commit/4b850457e12e1a678dd209f2868154f7553cbf8d
|
||||
# https://github.com/vim/vim/commit/868ad62cb8bf8038322eab2badd31bd98b02b9df
|
||||
Patch3080: 0001-patch-9.2.0561-security-possible-code-execution-with.patch
|
||||
Patch3081: 0001-patch-9.2.0568-pythoncomplete-g-pythoncomplete_allow.patch
|
||||
|
||||
|
||||
# gcc is no longer in buildroot by default
|
||||
@ -505,6 +511,8 @@ perl -pi -e "s,bin/nawk,bin/awk,g" runtime/tools/mve.awk
|
||||
%patch -P 3077 -p1 -b .tree-count-words-oob
|
||||
%patch -P 3078 -p1 -b .python-complete-repr
|
||||
%patch -P 3079 -p1 -b .netrw-book-hist-inject
|
||||
%patch -P 3080 -p1 -b .python3complete-code-exec
|
||||
%patch -P 3081 -p1 -b .pythoncomplete-allow-import
|
||||
|
||||
%build
|
||||
cd src
|
||||
@ -1057,6 +1065,10 @@ touch %{buildroot}/%{_datadir}/%{name}/vimfiles/doc/tags
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Thu Jul 30 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2:8.2.2637-40
|
||||
- RHEL-186649 CVE-2026-52858 vim: possible code execution with
|
||||
python3complete
|
||||
|
||||
* Thu Jul 30 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2:8.2.2637-39
|
||||
- RHEL-186655 CVE-2026-47162 vim: netrw code injection via
|
||||
NetrwBookHistSave()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user