Skip to content

[Security] MacVim affected by GHSA-q4jv-r9gj-6cwv — heap buffer overflow in spellfile.c read_compound() (vim < 9.2.0450) #1660

Description

@vulgraph

Summary

MacVim's src/spellfile.c contains a heap buffer overflow in read_compound() when loading specially crafted spell files. An integer overflow in the allocation size computation allows a malicious .spl file to cause out-of-bounds writes. The fix from vim 9.2.0450 (92993329) has not been applied to macvim r183.

Vulnerability Details

  • GHSA: GHSA-q4jv-r9gj-6cwv
  • CVE: CVE-2026-45130
  • Upstream fix (vim): 9.2.0450 (commit 929933294a5c56ef8e9dab03e0b8c61bbb1dc3cd, 2026-05-07)
  • Affected code: src/spellfile.cread_compound() function
  • Vulnerability type: CWE-122 — Heap-based Buffer Overflow

Root Cause

In read_compound(), the todo variable (derived from the SN_COMPOUND section length in the spell file) is used directly in buffer size calculations without an upper bound check:

/* src/spellfile.c line 1278 (macvim r183) */
c = todo * 2 + 7;

A malicious spell file can set todo to a large value (e.g., 0x40000000), causing todo * 2 to overflow a 32-bit integer to 7, resulting in an undersized allocation. Subsequent writes to the buffer cause a heap overflow.

Attack Scenario

  1. Attacker provides a malicious .spl spell file (e.g., in a project's spell directory)
  2. Victim loads the spell file in MacVim (:setlocal spelllang=... or via modeline)
  3. read_compound() allocates an undersized buffer and writes beyond it, potentially enabling arbitrary code execution

Verification

$ grep -n 'todo.*2.*7\|read_compound\|COMPOUND_MAX_LEN' src/spellfile.c
1278:    c = todo * 2 + 7;

Missing the COMPOUND_MAX_LEN guard and safe size computation. Patch 9.2.0450 not present:

$ git log --all --oneline | grep -i '9.2.0450\|spellfile\|q4jv'
(no output)

Suggested Fix

Merge vim patches up to at least 9.2.0450. The fix adds an upper bound check and uses size_t arithmetic:

/* Fixed (vim 9.2.0450): */
#define COMPOUND_MAX_LEN 100000

if ((size_t)todo > COMPOUND_MAX_LEN)
    return SP_FORMERROR;
size_t patsize = (size_t)todo * 2 + 7;
size_t flagsize = (size_t)todo + 1;
pat = alloc(patsize);
cp = alloc(flagsize);
ap = alloc(flagsize);
crp = alloc(flagsize);

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions