Opened 3 weeks ago
Closed 4 days ago
#5956 closed enhancement (fixed)
vim-9.2.0736 (Security Update)
| Reported by: | Joe Locash | Owned by: | SecurityAdvisory |
|---|---|---|---|
| Priority: | normal | Milestone: | 13.1 |
| Component: | Book | Version: | git |
| Severity: | normal | Keywords: | |
| Cc: |
Description
There have been a few security updates since the last book update:
Out-of-bounds Write in Spell File Word Count in Vim < 9.2.0653
==============================================================
Date: 15.06.2026
Severity: Medium
CVE: *requested, not yet assigned*
CWE: Out-of-bounds Write (CWE-787)
## Summary
The `tree_count_words()` function in `src/spellfile.c` fills in the word-count
fields of a spell-file word trie by walking it iteratively with a depth
counter. The counter is bounded only by the trie structure itself; it is
never checked against the size of the fixed `MAXWLEN`-element stack arrays it
indexes (`arridx[]`, `curi[]`, `wordcount[]`). A crafted `.spl`/`.sug` file
pair, loaded when the user invokes spell suggestion, can drive the descent
arbitrarily deep, so the function writes past the end of those arrays. This
is a stack out-of-bounds write that corrupts the call frame and crashes the
editor.
## Description
`tree_count_words()` is called from `suggest_load_files()` when spell
suggestion loads a language's `.sug` file (for example on `z=`,
`:spellsuggest`, or suggestion completion). For each node it descends one
level with:
```C
else
{
++depth;
arridx[depth] = idxs[n];
curi[depth] = 1;
wordcount[depth] = 0;
}
```
The three arrays have `MAXWLEN` (254) elements, so any `depth` of 254 or more
writes out of bounds. In a well-formed file each trie level corresponds to
one byte of a word, so depth is naturally limited, but the reader does not
enforce this for shared subtrees: `read_tree_node()` caps inline recursion at
`depth > MAXWLEN`, yet a `BY_INDEX` shared reference is accepted (its target
index is range-checked) and is *not* recursed into. A trie that uses shared
references to form a cycle, or a deep forward-shared chain, therefore parses
cleanly while driving the iterative walker past `MAXWLEN`.
## Impact
A spell file is normally inert data, but Vim resolves `spelllang`,
`spellfile`, and `runtimepath` to load it, so a repository or archive that
ships a malicious `spell/` sidecar can deliver the crafted pair. The trigger
is user-interaction-gated: spell checking must be enabled and the user must
invoke spell suggestion on a misspelled word. When that happens, the
out-of-bounds write corrupts the `tree_count_words()` stack frame. On builds
compiled with stack protection the overwrite is caught at the function epilogue
and the process aborts; cyclic geometries instead walk into the stack guard
page and causes a crash.
## Acknowledgements
The Vim project would like to thank Cipher / Causal Security
(https://causalsecurity.com/) for reporting and analyzing the issue and
suggesting a fix.
## References
The issue has been fixed as of Vim patch [v9.2.0653](https://github.com/vim/vim/releases/tag/v9.2.0653).
- [Commit](https://github.com/vim/vim/commit/a80874d9b84a01040e3d1aef2d4a59e1934dafb7)
- [Github Security Advisory](https://github.com/vim/vim/security/advisories/GHSA-wgh4-64f7-q3jq)
Out-of-bounds Write in Spell File Prefix Dump in Vim < 9.2.0662
===============================================================
Date: 16.06.2026
Severity: Medium
CVE: *requested, not yet assigned*
CWE: Out-of-bounds Write (CWE-787)
## Summary
The `dump_prefixes()` function in `src/spell.c` walks a spell-file prefix trie
iteratively with a depth counter while dumping the prefixes that apply to a
word. The counter is bounded only by the trie structure itself; it is never
checked against the size of the fixed `MAXWLEN`-element stack arrays it indexes
(`prefix[]`, `arridx[]`, `curi[]`). A crafted `.spl` file, loaded when the
user dumps the word list, can drive the descent arbitrarily deep, so the
function writes past the end of those arrays. This is a stack out-of-bounds
write that corrupts the call frame and crashes the editor.
## Description
`dump_prefixes()` is called from `spell_dump_compl()` when the word list is
dumped, for example on `:spelldump` or via spelling completion. For each node
it descends one level with:
```C
else
{
prefix[depth++] = c;
arridx[depth] = idxs[n];
curi[depth] = 1;
}
```
The arrays have `MAXWLEN` (254) elements, so any `depth` of 254 or more writes
out of bounds; a second sink underflows a `size_t` length passed to
`vim_strncpy()` once `depth` reaches `MAXWLEN`. In a well-formed file each
trie level corresponds to one byte of a prefix, so depth is naturally limited,
but the reader does not enforce this for shared subtrees: a `BY_INDEX` shared
reference is accepted (its target index is range-checked) and is *not* recursed
into. A prefix trie that uses a shared reference to point back to an ancestor
or itself therefore parses cleanly while driving the iterative walker past
`MAXWLEN`. This is the same class of issue as
[GHSA-wgh4-64f7-q3jq](https://github.com/vim/vim/security/advisories/GHSA-wgh4-64f7-q3jq)
(`tree_count_words()`, fixed in 9.2.0653) in a sibling trie walker that was
left unguarded.
## Impact
A spell file is normally inert data, but Vim resolves `spelllang`, `spellfile`,
and `runtimepath` to load it, so a repository or archive that ships a malicious
`spell/` sidecar can deliver the crafted file. The trigger is
user-interaction-gated: spell checking must be enabled and the user must dump
the word list. When that happens, the out-of-bounds write corrupts the
`dump_prefixes()` stack frame.
## Acknowledgements
The Vim project would like to thank Cipher / Causal Security
(https://causalsecurity.com/) for reporting and analyzing the issue and
suggesting a fix.
## References
The issue has been fixed as of Vim patch [v9.2.0662](https://github.com/vim/vim/releases/tag/v9.2.0662).
- [Commit](https://github.com/vim/vim/commit/8325b193bba5f01e7a7d8241f)
- [Github Security Advisory](https://github.com/vim/vim/security/advisories/GHSA-qm9w-fmpj-879h)
Vimscript Code Injection in netrw NetrwLocalRmFile() via crafted filename affects Vim < 9.2.0663
================================================================================================
Date: 16.06.2026
Severity: Medium
CVE: *requested, not yet assigned*
CWE: Improper Control of Generation of Code (CWE-94) /
Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') (CWE-78)
## Summary
A Vimscript code injection vulnerability exists in `s:NetrwLocalRmFile()` in
the netrw plugin (`runtime/pack/dist/opt/netrw/autoload/netrw.vim`) when
deleting a local file from the browser. A filename derived from the buffer's
directory listing is interpolated into an Ex command line passed to
`:execute` with only the backslash character escaped, allowing a crafted
filename containing a bar (`|`) to terminate the intended command and execute
arbitrary Vimscript, including shell commands via `:call system()` and `:!`.
## Description
`s:NetrwLocalRmFile()` removes a file and wipes its buffer. The sibling sinks
in the same file guard the Ex context with `fnameescape()`, for example:
exe "sil! keepj keepalt file ".fnameescape(a:newname)
The delete handler, however, escapes only the backslash character and passes
the value straight to `:execute`:
let rmfile = s:NetrwFile(netrw#fs#ComposePath(a:path,
escape(a:fname, '\\')))->fnamemodify(':.')
...
execute printf('silent! bwipeout %s', rmfile)
`escape(a:fname, '\\')` neutralizes only `\`; the subsequent `ComposePath`,
`NetrwFile`, and `fnamemodify` calls join and normalize the path but do not
remove `|` or a newline. When the value reaches `:execute`, a `|` terminates
the `bwipeout` command and begins the next, so the remainder of the filename
is run as Ex commands. A file named `x|call system('cmd')|y` turns the
deletion into `silent! bwipeout x`, `call system('cmd')`, `y`, and the
injected `:call system(...)` runs an arbitrary shell command.
The filename reaches `s:NetrwLocalRmFile()` through the `D` mapping, which
reads the filename from the current line of the netrw directory listing and,
after confirmation, deletes the entry.
## Impact
The vulnerability allows arbitrary Vimscript execution, and by extension
arbitrary shell command execution, with the privileges of the user running
Vim. Exploitation requires:
- a Unix-like system on which a filename may contain a bar (`|`),
- a crafted file present in a directory the victim browses with netrw, and
- the victim to delete that specific entry (press `D`, then confirm with `y`).
The severity is rated Medium because exploitation requires a planted file with
an unusual name and a deliberate delete action by the victim on that specific
entry, although the resulting primitive is full command execution as the
victim user.
Note: due to the nature of the issue, it seems unlikely that a user would
delete such a suspicious filename.
## Acknowledgements
The Vim project would like to thank Cipher / Causal Security
(https://causalsecurity.com/) for reporting and analyzing the issue.
## References
The issue has been fixed as of Vim patch [v9.2.0663](https://github.com/vim/vim/releases/tag/v9.2.0663).
- [Commit](https://github.com/vim/vim/commit/55bc757a5d436e59d50fe43f7cda94b118f86cb2)
- [Github Security Advisory](https://github.com/vim/vim/security/advisories/GHSA-vhh8-v6wx-hjjh)
Change History (9)
comment:1 by , 3 weeks ago
| Summary: | vim-9.2.0663 (Security Update) → vim-9.2.0671 (Security Update) |
|---|
comment:2 by , 3 weeks ago
| Summary: | vim-9.2.0671 (Security Update) → vim-9.2.0679 (Security Update) |
|---|
Out-of-bounds Read with Text Properties in Vim >= 9.2.0320 && Vim < 9.2.0679 ============================================================================ Date: 20.06.2026 Severity: Medium CVE: *requested, not yet assigned* CWE: Out-of-bounds Read (CWE-125) ## Summary A crafted undo or swap file can store a virtual-text property whose offset and length point outside the line's property data. When Vim restores or displays such a line it converts the offset into a pointer and reads the virtual text without bounds checking, causing an out-of-bounds read that can crash Vim or disclose adjacent heap memory. This completes GHSA-f36c-2qcp-7gpw (patch 9.2.0670): that patch validated only the property *count*; the per-property virtual-text offset and length were still taken from the untrusted file unchecked. ## Details Since patch 9.2.0320, text properties are stored inline after a line: ``` [text][NUL][prop_count (uint16)][textprop_T ...][virtual text ...] ``` For a virtual-text property (`tp_id < 0`), `tp_text_offset` is the byte offset of its string relative to the `prop_count` position. On load the offset is turned into a pointer (`count_ptr + tp_text_offset`) and the string is later read as `tp_len + 1` bytes. `text_prop_count_valid()` (9.2.0670) only ensures the `textprop_T` array fits within the property data; it does not constrain `tp_text_offset` or `tp_len`. A file with `tp_id < 0` and an out-of-range offset therefore yields a wild pointer, and reading the virtual text reads out of bounds. The unchecked conversion is reachable when: - restoring a line from an undo/swap file, and - displaying or querying a line's properties (`get_text_props()`, used by screen drawing, `charset.c`, and `prop_list()`/`prop_find()`). ## Impact Out-of-bounds read leading to a crash (denial of service) and potentially disclosure of adjacent heap memory. Exploitation requires the victim to open a maliciously crafted undo or swap file, so attack complexity is high and user interaction is required. Severity: Medium. ## Patch Fixed by validating, for every virtual-text property, that `tp_text_offset` and `tp_len` lie within the available property data before the offset is converted to a pointer (`text_prop_vtext_valid()`), applied on both the undo/swap restore and the display path. Update to patch 9.2.0679. ## Workarounds Do not load undo files (`:rundo`) or recover swap files from untrusted sources. ## References The issue has been fixed as of Vim patch [v9.2.0679](https://github.com/vim/vim/releases/tag/v9.2.0679). - [Commit](https://github.com/vim/vim/commit/b3faeecc976d3031d7c0675623516ec60c30f949) - [Github Security Advisory](https://github.com/vim/vim/security/advisories/GHSA-ww8h-47xp-hp4w) - [GHSA-f36c-2qcp-7gpw](https://github.com/vim/vim/security/advisories/GHSA-f36c-2qcp-7gpw) - the count check this completes - Introduced in patch [v9.2.0320](https://github.com/vim/vim/releases/tag/v9.2.0320) (inline text-property storage format)
comment:3 by , 2 weeks ago
| Summary: | vim-9.2.0679 (Security Update) → vim-9.2.0699 (Security Update) |
|---|
Out-of-bounds Write in SOFO Soundfolding in Vim < 9.2.0698
==========================================================
Date: 21.06.2026
Severity: Medium
CVE: *requested, not yet assigned*
CWE: Out-of-bounds Write (CWE-787)
## Summary
The single-byte branch of `spell_soundfold_sofo()` in `src/spell.c` translates
a word through a spell file's SOFO (sound-folding) byte map into a caller-owned
result buffer. Its copy loop advances the output index `ri` with no upper
bound and terminates only on the input NUL, writing one byte per input byte
into the `MAXWLEN`-element stack buffer the caller provides. A word longer
than `MAXWLEN`, passed to `soundfold()` (or reached via sound-based spell
suggestion) while a SOFO-based spell language is active, therefore writes past
the end of that buffer. This is a stack out-of-bounds write that corrupts the
call frame and crashes the editor.
## Description
`spell_soundfold_sofo()` has two branches. The multibyte branch (taken under
multibyte encodings) bounds its output with `if (ri + MB_MAXBYTES > MAXWLEN)
break;`. The single-byte branch, taken for 8-bit encodings such as `latin1`,
has no equivalent guard:
```C
else
{
// The sl_sal_first[] table contains the translation.
for (s = inword; (c = *s) != NUL; ++s) // bound: input NUL only
{
if (VIM_ISWHITE(c))
c = ' ';
else
c = slang->sl_sal_first[c];
if (c != NUL && (ri == 0 || res[ri - 1] != c))
res[ri++] = c; // no ri < MAXWLEN guard
}
}
res[ri] = NUL; // trailing OOB write too
```
The destination `res` is an array of size `[MAXWLEN]`. `f_soundfold()` passes
the user-supplied string straight to `eval_soundfold()` without length-bounding
it, so once the active language carries a SOFO map (`sl_sal_first`), any input
longer than 253 bytes runs `ri` past the end of the buffer. The two sibling
walkers do bound their output (`spell_soundfold_sal()` truncates its input with
`vim_strncpy(.., MAXWLEN - 1)` and guards `reslen < MAXWLEN`;
`spell_soundfold_wsal()` guards `reslen < MAXWLEN`); the single-byte SOFO
branch was the remaining unguarded walker of this class.
## Impact
This issue is driven by the length of the word handed to the
spell_soundfold_sofo() function, not by the contents of the spell file. any
loaded spell language with a SOFO sound-folding table is enough. Exploitation
is constrained, however. The vulnerable single-byte branch is only reached
under a non-multibyte 8-bit encoding (e.g. `set encoding=latin1`); under the
default UTF-8 encoding the multibyte branch, which is already bounded, is taken
instead. Spell checking must be enabled with such a SOFO-based language, and
`soundfold()` must be invoked on an over-long word - for instance a script or
plugin that calls `soundfold()` on untrusted input. When those conditions hold
the out-of-bounds write corrupts the `eval_soundfold()` stack frame and the
process aborts.
## Acknowledgements
The Vim project would like to thank Cipher / Causal Security
(https://causalsecurity.com/) for reporting and analyzing the issue and
suggesting a fix.
## References
The issue has been fixed as of Vim patch [v9.2.0698](https://github.com/vim/vim/releases/tag/v9.2.0698).
- [Commit](https://github.com/vim/vim/commit/497f931f85339d175d7f69588dd249e8ccfed41b)
- [Github Security Advisory](https://github.com/vim/vim/security/advisories/GHSA-q8mh-6qm3-25g4)
Arbitrary Code Execution via Python Omni-Completion Docstrings in Vim < 9.2.0699 ================================================================================ Date: 2026-06-21 Severity: Medium CVE: *requested, not yet assigned* CWE: Improper Control of Generation of Code (CWE-94) ## Summary Vim's Python omni-completion executes reconstructed function and class definitions from the current buffer with `exec()` as part of populating the completion dictionary. When reconstructing that source, each scope's docstring is inserted verbatim between triple quotes with no escaping, so a hostile buffer can break out of the triple-quoted literal and execute attacker- controlled Python during omni-completion. This is the same class of issue as GHSA-65p9-mwwx-7468 (patch 9.2.0597), whose fix sanitised parameter defaults/annotations and class base lists but left the docstring path untouched. ## Description In `runtime/autoload/python3complete.vim` (and the legacy `pythoncomplete.vim`), the `get_code()` methods build the source later passed to `exec()` and emit each docstring as `'"""' + self.docstr + '"""'`. `self.docstr` comes straight from buffer content, and the `doc()` helper only strips leading and trailing quote and whitespace characters, so a `"""` embedded in the middle of a docstring survives. A class-body docstring written as a single-quoted source string keeps the embedded `"""` as one string token through `doc()`, then breaks out of the generated triple-quoted literal: the reconstructed `class` body becomes string concatenation around an attacker expression, which Python evaluates at class-definition time when `exec()` runs. ## Impact An attacker who can convince a user to open or edit a hostile Python buffer and trigger Python omni-completion (CTRL-X CTRL-O, or a plugin that invokes the completion function) can execute Python code in the user's Vim process. The code runs with the user's privileges. Vim built without `+python3` and `+python` is not affected. Triggering omni-completion in the hostile buffer is required; opening the file alone is not sufficient. ## Acknowledgements The Vim project would like to thank Chenyuan Mi for reporting and analyzing the issue and suggesting a fix. ## References The issue has been fixed as of Vim patch [v9.2.0699](https://github.com/vim/vim/releases/tag/v9.2.0699). - [Commit](https://github.com/vim/vim/commit/cce141c42740f122dd8486ae04e21c2a81016ba8) - [Github Security Advisory](https://github.com/vim/vim/security/advisories/GHSA-ppj8-wqjf-6fp3) - [Github Security Advisory GHSA-65p9-mwwx-7468](https://github.com/vim/vim/security/advisories/GHSA-65p9-mwwx-7468) (prior fix for the same surface)
comment:4 by , 2 weeks ago
I'm going to wait until the end of the month to update vim. The number of security issues is disturbing though. Judging from the rate, there will probably be more.
comment:5 by , 2 weeks ago
| Summary: | vim-9.2.0699 (Security Update) → vim-9.2.0725 (Security Update) |
|---|
Out-of-bounds Write in SAL Soundfolding in Vim < 9.2.0725
=========================================================
Date: 24.06.2026
Severity: Medium
CVE: *requested, not yet assigned*
CWE: Out-of-bounds Write (CWE-787)
## Summary
The single-byte branch of `spell_soundfold_sal()` in `src/spell.c` translates
a word through a spell file's SAL (sound-folding) rules into a caller-owned
result buffer. Its result writes are guarded with `reslen < MAXWLEN`, which
allows `reslen` to reach `MAXWLEN`. The function then terminates the result
with `res[reslen] = NUL`, writing one byte past the end of the `MAXWLEN`-element
stack buffer when `reslen == MAXWLEN`. A word that reaches the buffer boundary,
passed to `soundfold()` (or reached via sound-based spell suggestion) while a
SAL-based spell language is active, therefore writes past the end of that
buffer. This is a stack out-of-bounds write that corrupts the call frame and
crashes the editor.
## Description
`spell_soundfold_sal()` writes its translated output into the stack buffer
`res`, an array of size `[MAXWLEN]` (valid indices `0..MAXWLEN - 1`). The
single-byte branch, taken for 8-bit encodings such as `latin1`, guards each
result write with `reslen < MAXWLEN`, so `reslen` can be incremented up to and
including `MAXWLEN`. After the translation loop the result is NUL-terminated:
```C
res[reslen] = NUL; // reslen may equal MAXWLEN -> OOB write
```
With `reslen == MAXWLEN` this stores at index `MAXWLEN` of a `MAXWLEN`-element
buffer, one byte past the end (CWE-787). The preceding write sites in this
branch — the no-`<`-rule copy loop, the `"^^"` rule append, and the
double-letter condense — are bounded with `< MAXWLEN` rather than `< MAXWLEN - 1`,
leaving no room for the terminating NUL at the boundary.
`f_soundfold()` passes the user-supplied string to `eval_soundfold()` without
length-bounding it; the input is case-folded to roughly `MAXWLEN - 1` bytes, so
an argument at that boundary length drives `reslen` to `MAXWLEN`. This is the
SAL counterpart to the SOFO issue fixed in 9.2.0698; the single-byte SAL branch
was the remaining walker of this class with an off-by-one at the terminator.
## Impact
This issue is driven by the length of the word handed to the
`spell_soundfold_sal()` function, not by the contents of the spell file; any
loaded spell language with SAL sound-folding rules is enough. Exploitation is
constrained, however. The vulnerable single-byte branch is only reached under
a non-multibyte 8-bit encoding (e.g. `set encoding=latin1`); under the default
UTF-8 encoding the multibyte branch `spell_soundfold_wsal()`, which writes one
full character at a time and breaks on `l + MB_MAXBYTES > MAXWLEN`, is taken
instead and is unaffected. Spell checking must be enabled with such a
SAL-based language, and `soundfold()` must be invoked on a boundary-length word
— for instance a script or plugin that calls `soundfold()` on untrusted input.
When those conditions hold the out-of-bounds write corrupts the
`eval_soundfold()` stack frame and the process aborts.
## Acknowledgements
The Vim project would like to thank Hirohito Higashi (@h-east) for reporting and
analyzing the issue and suggesting a fix.
## References
The issue has been fixed as of Vim patch [v9.2.0725](https://github.com/vim/vim/releases/tag/v9.2.0725).
- [Commit](https://github.com/vim/vim/commit/d22ff1c955ff87e8273210eae125aab0e85b6c30)
- [Github Security Advisory](https://github.com/vim/vim/security/advisories/GHSA-m3hf-xcm3-xhm2)
comment:6 by , 12 days ago
| Summary: | vim-9.2.0725 (Security Update) → vim-9.2.0735 (Security Update) |
|---|
Arbitrary Code Execution via C Omni-Completion in Vim < 9.2.0735
================================================================
Date: 26.06.2026
Severity: Medium
CVE: *requested, not yet assigned*
CWE: Improper Neutralization of Special Elements (CWE-94),
Inclusion of Functionality from Untrusted Control Sphere (CWE-829)
## Summary
The C omni-completion script in `runtime/autoload/ccomplete.vim` interpolates
the `typeref:` / `typename:` extension field of a `tags` entry, without
escaping, into a `:vimgrep` pattern that is run through `:execute`. Because
`:vimgrep` honors the bar (`|`) as a command separator, a crafted tag field
can close the search pattern and append an arbitrary Ex command. Opening a
hostile `.c` file whose project `tags` file contains such an entry and
invoking C omni-completion runs that command as the editing user.
## Description
`runtime/ftplugin/c.vim` installs `omnifunc=ccomplete#Complete` on every C
buffer when Vim has filetype plugins enabled. When the user invokes omni-
completion with `CTRL-X CTRL-O` on a structure-member access (for example
`myvar.field`), the completer looks up the variable in the `tags` files,
reads the type from the entry's `typeref:` (or `typename:`) field, and
searches the `tags` files for members of that type.
The member search builds a `:vimgrep` command and runs it with `:execute`.
The type name taken from the tag field is concatenated directly into the
search pattern with no escaping, while the file-name argument is passed
through `escape()`. The `typeref:` field is parsed verbatim by `taglist()`;
its only constraint is that it contains no internal whitespace, so the
characters `/ | ' " ( )` all survive.
`:vimgrep` carries the `EX_TRLBAR` attribute, which means an unescaped bar in
the constructed command line terminates the `:vimgrep` and begins a new Ex
command. A tag field of the form `x/|<command>|"` therefore closes the
regular expression with `/`, starts a fresh Ex command after `|`, and comments
out the remainder of the generated line.
## Impact
Arbitrary local code execution as the user running Vim, with that user's full
credential set, file-system access, and network egress. Realistic delivery
vectors include:
- cloning or checking out a third-party repository that ships a `tags` file
alongside its C sources,
- extracting a source tarball or archive whose layout places a hostile `tags`
file next to the `.c` file being inspected,
- auditing a malware sample or untrusted source tree in its own directory.
Exploitation requires:
- Vim with filetype plugins enabled (`filetype plugin on`, the default in
`runtime/defaults.vim` and most distribution `vimrc` files),
- a `tags` file reachable through the `'tags'` option (the default `./tags`
resolves to the attacker's file in the conventional ctags workflow, where
Vim's working directory is the project root),
- the victim opening the hostile `.c` file and invoking C omni-completion on a
member access.
The severity is rated Medium because the user must manually invoke omni-
completion on a member access after opening the file; the bug does not fire on
file-open alone and the `ccomplete.vim` produces an error message, which makes
the whole attack quite noticeable.
## Acknowledgements
The Vim project would like to thank Cipher / Causal Security
(https://causalsecurity.com/) for reporting and analyzing the issue and
providing a proof of concept.
## References
The issue has been fixed as of Vim patch [v9.2.0735](https://github.com/vim/vim/releases/tag/v9.2.0735).
- [Commit](https://github.com/vim/vim/commit/6b611b0d15603c52ebdad17172b0232b4f65704e)
- [Github Security Advisory](https://github.com/vim/vim/security/advisories/GHSA-mf92-v4xw-j45x)
comment:7 by , 11 days ago
| Summary: | vim-9.2.0735 (Security Update) → vim-9.2.0736 (Security Update) |
|---|
Arbitrary Code Execution via PHP Omni-Completion in Vim < 9.2.0736
==================================================================
Date: 28.06.2026
Severity: Medium
CVE: *requested, not yet assigned*
CWE: Improper Neutralization of Special Elements (CWE-94),
Inclusion of Functionality from Untrusted Control Sphere (CWE-829)
## Summary
The PHP omni-completion script in `runtime/autoload/phpcomplete.vim`
interpolates a class or trait name, taken from the contents of the edited
buffer, into a `search()` pattern that is run via `win_execute()` without
escaping. A name containing a single quote can terminate the `search()`
string argument early; because the bar (`|`) is honored as an Ex command
separator, the remainder of the name is then run as Ex commands. Via the
`:!` command this allows arbitrary command execution.
## Description
`runtime/ftplugin/php.vim` installs `omnifunc=phpcomplete#Complete` on every
PHP buffer when Vim has filetype plugins enabled. When the user invokes
omni-completion with `CTRL-X CTRL-O`, `phpcomplete#GetClassContentsStructure()`
locates a class declaration by building a `search()` command and running it
through `win_execute()`:
call win_execute(popup_id,
\ 'call search(''\c\(class\|interface\|trait\)\_s\+'
\ .. a:class_name .. '\(\>\|$\)'')')
The class name is concatenated into the single-quoted `search()` argument
without neutralizing the single quote. A `'` in the name ends that string
early, and the `:call` command honors the `EX_TRLBAR` attribute, so a bar
following the quote begins a new Ex command that `win_execute()` then runs.
The name passed to this function can originate from the buffer's own contents
(for example a class or trait name parsed from the file), so a crafted PHP
file can place an injecting value where a class name is expected.
## Impact
Arbitrary Ex command execution, and via the `:!` command arbitrary operating-
system command execution, in the context of the user running Vim.
Exploitation requires:
- Vim with filetype plugins enabled (`filetype plugin on`, the default in
`runtime/defaults.vim` and most distribution `vimrc` files),
- the PHP omni-completion function in use
(`omnifunc=phpcomplete#Complete`, set by the bundled PHP ftplugin),
- the victim opening a crafted PHP file and invoking omni-completion.
The severity is rated Medium because the user must open the crafted file and
manually invoke omni-completion; the bug does not fire on file-open alone.
## Acknowledgements
The Vim project would like to thank Hirohito Higashi for reporting, analyzing
and fixing the issue.
## References
The issue has been fixed as of Vim patch [v9.2.0736](https://github.com/vim/vim/releases/tag/v9.2.0736).
- [Commit](https://github.com/vim/vim/commit/43afc581a37a35762dd0ef292f038b9dc5680a24)
- [Github Security Advisory](https://github.com/vim/vim/security/advisories/GHSA-fh26-8f79-wj97)
comment:8 by , 9 days ago
| Owner: | changed from to |
|---|
Fixed at commit 4a56fb5d78.
Leaving open for security advisories.
comment:9 by , 4 days ago
| Resolution: | → fixed |
|---|---|
| Status: | new → closed |
Advisory sa-13.0-136 has been issued.
Note:
See TracTickets
for help on using tickets.

A couple more...
Out-of-bounds Read in Text Property Count in Vim < 9.2.0670 =========================================================== Date: 17.06.2026 Severity: Medium CVE: *requested, not yet assigned* CWE: Out-of-bounds Read (CWE-125) ## Summary `get_text_props()` in `src/textprop.c` reads a `uint16` property count stored inline after a line's text and returns it as the number of 32-byte `textprop_T` entries that follow. The only check is a floor that guarantees room for a single entry; the count is never checked against the amount of data actually present. A line that declares a large count while carrying little data causes consumers to read far past the end of the line buffer. Such a line can be delivered through a crafted undo file, leading to a crash. ## Description When text properties are present, a line is stored as `[text][NUL][prop_count (uint16)][textprop_T ...][vtext ...]`. `get_text_props()` validates only that the property data is large enough for one entry and then returns `prop_count` unchanged: ```C if (propdata_len < PROP_COUNT_SIZE + sizeof(textprop_T)) { iemsg(e_text_property_info_corrupted); return 0; } mch_memmove(&prop_count, text + textlen, PROP_COUNT_SIZE); *props = text + textlen + PROP_COUNT_SIZE; return (int)prop_count; ``` Consumers use the returned count as a loop bound, reading `sizeof(textprop_T)` (32) bytes per iteration with no re-check against the real line length, so a count of 65535 against a single stored entry reads about two megabytes past the line. An attacker authors both a source file and its sibling undo (`.un~`) file; the saved line text in the undo file is patched so that, on undo, the restored line declares a property count far larger than its data. The next consumer, such as `prop_list()` or a screen redraw, then reads out of bounds. The same unbounded count is used by the unpacked-memline path and by the line-deletion code, which are affected in the same way. ## Impact Undo files are normally trusted data written by Vim itself, but Vim reads a sibling `.un~` file when `'undofile'` is enabled and the file's content hash matches, so a repository or archive that ships a malicious source/undo pair can deliver the crafted line. The trigger is user-interaction-gated: `'undofile'` must be enabled, the user must open the file and undo, and a consumer must then inspect the line (for example by displaying it). When that happens, the out-of-bounds read can leak adjacent memory through property metadata returned by `prop_list()` and crash the editor. ## Acknowledgements The Vim project would like to thank Cipher / Causal Security (https://causalsecurity.com/) for reporting and analyzing the issue and suggesting a fix. ## References The issue has been fixed as of Vim patch [v9.2.0670](https://github.com/vim/vim/releases/tag/v9.2.0670). - [Commit](https://github.com/vim/vim/commit/b2338ca90643e2f01ecb6547c1172716aaec4f79) - [Github Security Advisory](https://github.com/vim/vim/security/advisories/GHSA-f36c-2qcp-7gpw)Out-of-bounds Read with libsodium-encrypted Files in Vim < 9.2.0671 =================================================================== Date: 17.06.2026 Severity: Medium CVE: *requested, not yet assigned* CWE: Integer Underflow (CWE-191), Out-of-bounds Read (CWE-125) ## Summary When Vim opens a file encrypted with the `VimCrypt~04!` or `VimCrypt~05!` method (xchacha20poly1305, requires the `+sodium` feature) whose body is shorter than a single libsodium secretstream header, an unsigned length calculation underflows and a subsequent decryption call reads far past the end of the input buffer, crashing Vim. ## Description `crypt_get_header_len()` validates only the size of the file header (magic, salt and seed), not the length of the encrypted body that follows. A file with a complete header but a body of 1 to 23 bytes therefore passes the size check. In `crypt_sodium_buffer_decode()` the secretstream header length is then subtracted from the body length: ```C from += crypto_secretstream_xchacha20poly1305_HEADERBYTES; len -= crypto_secretstream_xchacha20poly1305_HEADERBYTES; ``` `len` is a `size_t`, so when the body is shorter than `HEADERBYTES` the subtraction wraps around to a value close to the maximum. The wrapped length is then passed to `crypto_secretstream_xchacha20poly1305_pull()`, which reads that many bytes from the input buffer. Because the output buffer was already allocated using the original (small) body length, the corrupted length widens the read rather than a write, resulting in an out-of-bounds read and a crash. ## Impact An attacker who can get a victim to open a crafted, ostensibly encrypted file and enter any key at the prompt can reliably crash Vim (denial of service). No information is disclosed: the decryption call faults on the over-read and never returns plaintext, and the message authentication would fail in any case. Exploitation requires the `+sodium` feature, user interaction to open the file and supply a key, and is limited to a crash. ## Acknowledgements The Vim project would like to thank Cipher / Causal Security (https://causalsecurity.com/) for reporting and analyzing the issue and suggesting a fix. ## References The issue has been fixed as of Vim patch [v9.2.0671](https://github.com/vim/vim/releases/tag/v9.2.0671). - [Commit](https://github.com/vim/vim/commit/c8777cec25dcfae89c42e9aff51af61f71c5745f) - [Github Security Advisory](https://github.com/vim/vim/security/advisories/GHSA-c4j9-wr9j-4486)