References: vim/vim#19888 · GHSA-p9wv-8m72-gc7v · CVE-2026-28421 · Medium Article
Vim's Partial Patch Problem: 14+ Heap Overflows Left Behind After CVE-2026-28421
Vim recently patched a critical heap overflow — but only in a single location. Fourteen identical vulnerabilities remain, and the maintainer just closed the security advisory.
Feng Ning · Innora Security Research · April 2026
TL;DR
- What: CVE-2026-28421 fixed ONE
(int)cast truncation inviminfo.c. At least 14 identical truncations remain acrossex_getln.c,memline.c,terminal.c,session.c, and others. - Impact: CWE-190 (Integer Overflow) → CWE-122 (Heap Buffer Overflow). Attackable via crafted swap files, undo files, session files, and terminal output — all reachable through git repositories and shared filesystems.
- Response: Vim's lead maintainer closed the GitHub Security Advisory and threatened to ban the reporter.
- Fix: Trivial — remove the redundant
(int)casts.alloc()already acceptssize_t.
The Vulnerability Pattern
Vim's alloc() accepts size_t. But many call sites cast to (int) first:
alloc_cmdbuff((int)len); // src/ex_getln.c:1540
Exceeding INT_MAX (2³¹ − 1, roughly 2.1 billion) triggers silent truncation — the upper bits are discarded:
size_t len = 0x100000010; // 4,294,967,312 (4 GB + 16)
int ilen = (int)len; // 16 — upper 33 bits discarded
alloc(ilen); // allocates 16 bytes
memcpy(buf, data, len); // copies 4 GB into a 16-byte buffer
// → heap buffer overflow
Patch 9.2.0278 removed the (int) cast in ONE location in viminfo.c. The remaining 14+ are untouched.
Affected Locations
| File | Line | Allocation Call | Trigger Complexity |
|------|------|-----------------|--------------------|
| ex_getln.c | 1540 | alloc_cmdbuff((int)len) | Medium — command buffer |
| ex_getln.c | 1553 | alloc_cmdbuff((int)plen) | Medium — command buffer |
| memline.c | 3677 | alloc(len + (int)textproplen) then mch_memmove(..., textproplen) | Low — file open (text props) |
| terminal.c | 5700 | alloc(width + (int)STRLEN(fname) + 1) | Low — terminal output |
| session.c | 1102 | alloc(10 + (int)STRLEN(escaped_filename) + 1) | Medium — session file |
| popupwin.c | 5899 | alloc((int)len) | High — UI interaction |
| vim9expr.c | 3358 | alloc((int)(len1 + STRLEN(s2) + 1)) | High — Vim9 script |
| list.c | 1256 | list_alloc_with_items((int)totallen) | High — list ops |
The memline.c case proves these aren't theoretical bugs. The allocation uses (int)textproplen, but the subsequent mch_memmove copies the full, original textproplen:
// src/memline.c:3677-3684 — allocation vs. copy mismatch
size_t textproplen = curbuf->b_ml.ml_line_len - oldtextlen;
newline = alloc(len + (int)textproplen); // truncated size
mch_memmove(newline + len, ptr, textproplen); // original size → overflow
A natural question is whether the write operations also use the truncated value. In ex_getln.c, the situation is similar — the buffer is allocated with (int)len, but the fill loop writes using the original size_t len:
alloc_cmdbuff((int)len); // allocates based on truncated value
ccline.cmdbuff[len] = NUL; // writes at original offset → overflow
The alloc_cmdbuff function signature reveals the design flaw:
static void alloc_cmdbuff(int len) // ← int, not size_t
{
if (len < 80) len = 100;
else len += 20;
ccline.cmdbuff = alloc(len); // alloc() takes size_t,
ccline.cmdbufflen = len; // but receives truncated int
}
"You Need 4 GB of Input" — Why That's a Misconception
Vim co-maintainer mattn argued:
"To trigger the truncation, you would need to feed Vim a value exceeding INT_MAX, meaning roughly 4 GB+ of data."
This confuses payload size with length-field value. Computed values — not actual data volume — trigger the truncation. A crafted file can declare large dimensions in its metadata while remaining kilobytes on disk. The overflow occurs during allocation based on the declared size, not the file size.
The following five attack paths all exploit this distinction: none requires 4 GB of actual data.
This distinction is well-established. Vim's own CVE history demonstrates it:
| CVE | Year | File | Attack Surface | Required Payload |
|-----|------|------|----------------|------------------|
| CVE-2026-28421 | 2026 | viminfo.c / swap | Swap file metadata | Small file, large declared length |
| CVE-2026-28420 | 2026 | terminal.c | Terminal output | Controlled process output |
| CVE-2017-6350 | 2017 | undo.c | Crafted undo file | Small file, overflow in u_read_undo |
| CVE-2017-6349 | 2017 | undo.c | Crafted undo file | Integer overflow at alloc site |
| GHSA-h4mf-vg97-hj8j | 2025 | tags parsing | Emacs tags file | Heap overflow via crafted tags |
In every case, the attacker-controlled input was a metadata field in a small file, not gigabytes of raw data.
Concrete Attack Vectors
1. Malicious Swap Files (.file.swp) — Supply Chain
Vim automatically detects and offers recovery from swap files. A crafted .swp file placed in a git repository or shared directory triggers the memline.c allocation path when any user opens the corresponding file. The swap file declares large textproplen values in its block headers; the file itself can be kilobytes.
CVE-2026-28421 itself was described as arising from "improper bounds checking when reading swap file metadata" — confirming this path is attacker-reachable.
2. Crafted Undo Files (.file.un~) — Supply Chain
Vim auto-loads persistent undo files stored alongside edited files. CVE-2017-6350 proved that u_read_undo in undo.c is vulnerable to integer overflow via crafted undo files. The same class of overflow applies to the (int) truncation sites.
3. Terminal Buffer Injection — Remote
CVE-2026-28420 (patched in 9.2.0076) proved that terminal.c's handle_pushline() is exploitable by any process whose output renders in a :terminal buffer. The advisory states:
"An attacker who can control the output of a program running inside a Vim :terminal buffer can trigger a heap buffer overflow."
The (int) truncation at terminal.c:5700 is a different location in the same file, reachable through the same surface: SSH sessions, Docker exec, CI/CD log viewers — anywhere :terminal displays remote output.
4. Session Files — Social Engineering
// src/session.c:1102
alloc(10 + (int)STRLEN(escaped_filename) + 1)
Session files (.session.vim) are shared in project repositories and IDE configurations. A session file referencing a pathname whose escaped length overflows int reaches this allocation.
5. Git Editor Integration — Workflow
With EDITOR=vim, git operations (commit, rebase -i, merge) invoke Vim with content from .git/ internals. A malicious git hook or crafted rebase plan can inject data reaching the ex_getln.c command buffer paths.
Current Fix Status
| File | Location | Patched? |
|------|----------|----------|
| viminfo.c | (int) cast in alloc | Yes (9.2.0278) |
| ex_getln.c:1540 | alloc_cmdbuff((int)len) | No |
| ex_getln.c:1553 | alloc_cmdbuff((int)plen) | No |
| memline.c:3677 | alloc(len + (int)textproplen) | No |
| terminal.c:5700 | alloc(width + (int)STRLEN(...)) | No |
| session.c:1102 | alloc(10 + (int)STRLEN(...)) | No |
| popupwin.c:5899 | alloc((int)len) | No |
| vim9expr.c:3358 | alloc((int)(...)) | No |
| list.c:1256 | list_alloc_with_items((int)totallen) | No |
As of April 2, 2026 — Vim master branch.
Disclosure Timeline
| Date (UTC) | Event | |------------|-------| | 2026-04-02 05:20 | Issue vim/vim#19888 filed | | 2026-04-02 05:20 | GHSA-p9wv-8m72-gc7v submitted | | 2026-04-02 06:04 | mattn comments: pattern is "theoretically correct," practical exploitability is low | | 2026-04-02 06:44 | chrisbra closes #19888, states: "You clearly did not follow SECURITY.md. Last warning, next time you will be blocked." | | 2026-04-02 07:04 | chrisbra reopens #19888, clarifies his previous comment was intended for the GHSA draft | | 2026-04-02 07:06 | chrisbra closes GHSA-p9wv-8m72-gc7v: "Those are bugs. The mentioned CVE is completely irrelevant. Last warning." |
Three observations:
First, mattn acknowledged the vulnerability class is real. His objection was scope, not validity.
Second, SECURITY.md requires reporters to "clearly explain why the behaviour is a security issue" — the report included CWE classifications, affected code paths with line numbers, and a truncation proof. It also warns against "AI-generated reports without careful review" — the analysis was based on manual source code audit, with LLM-assisted cross-checking of conclusions.
Third, threatening researchers for submitting valid, technically acknowledged reports stifles responsible disclosure. The vulnerability class is proven. The pattern is identical. The only question is whether 14 more instances warrant security treatment. Closing the advisory without addressing the code is a choice — and a public one.
Prior Art: Integer Truncation in Production
This isn't a niche bug class. Integer truncation drives some of the most critical exploits in recent history:
- CVE-2023-4863 (libwebp) — Integer overflow in Huffman coding → heap overflow, exploited in the wild by NSO Group's BLASTPASS chain. CVSS 8.8.
- CVE-2023-38408 (OpenSSH) — Integer overflow in PKCS#11 → remote code execution.
- CVE-2017-6350 (Vim) — Integer overflow at
u_read_undoallocation site — in Vim's own codebase.
The pattern is always the same: a computation wraps, a small buffer is allocated, writes overflow it.
Mitigation
Until the remaining (int) casts are removed, users can reduce exposure:
" Disable swap file auto-recovery (mitigates .swp vector)
set noswapfile
" Disable persistent undo (mitigates .un~ vector)
set noundofile
" Disable modeline processing
set nomodeline
" Avoid :terminal with untrusted process output
For git workflows, setting GIT_EDITOR to a sandboxed editor (e.g., nano) eliminates the editor-injection vector.
The proper fix is what mattn described: remove the (int) casts and change alloc_cmdbuff(int len) to alloc_cmdbuff(size_t len). Since alloc() already accepts size_t, these casts are purely redundant narrowing conversions.
Conclusion
CVE-2026-28421 established that (int) truncation before alloc() in Vim is a heap buffer overflow vulnerability. Patch 9.2.0278 fixed one instance. Fourteen remain, reachable via swap files, undo files, terminal buffers, session files, and git editor integration.
The fix is trivial: remove the redundant casts. The risk is real: proven by Vim's own CVE history. The precedent is set — and 14 instances remain.
Feng Ning is a CISSP/CISA security researcher and founder of Innora.ai. This research was conducted as part of an ongoing secure code audit initiative.
References: vim/vim#19888 · GHSA-p9wv-8m72-gc7v · CVE-2026-28421 · CVE-2026-28420 · CVE-2017-6350

Related Chronicles
31 Vulns in 48 Hours: An AI-Assisted Methodology for Auditing Automotive Code
31 CVEs in 48 hours across 12 automotive projects. Our AI-augmented audit methodology with ASAN verification and 3-LLM validation.
Broken By Design: Why One of the World's Largest Payment Apps Still Runs on Crypto from 2004
Systematic analysis of cryptographic failures in Alipay APK signing — MD5, RSA-1024, hardcoded DES keys still active in 2026.
In-Depth Analysis of Mainstream APT Team Tactics in 2025
Based on OmniSec framework's APT simulation capabilities and global threat intelligence from 2020-2025
Subscribe for AI Security Insights
Join 5,000+ engineers and security researchers. Get our latest deep dives into Sovereign AI, Red Teaming, and System Architecture.
No spam. Unsubscribe at any time.
Comments are currently disabled.