Skip to content

GHSA-q8mh-6qm3-25g4 on CTRL-OS 26.05

Aliases: GHSA-q8mh-6qm3-25g4, CVE-2026-57455

Packages: vim

Status: Plausible

Advisory Information

Out-of-bounds Write in SOFO Soundfolding in Vim < 9.2.0698

Date: 21.06.2026 Severity: Medium CVE: CVE-2026-57455 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:

    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. - Commit - Github Security Advisory

Updates

2026-07-10 22:53 CEST

Metadata changes:

  • Status for package vim: “Plausible

2026-07-06 22:42 CEST

Metadata changes:

  • Status for package vim: “New