oss-sec mailing list archives

[vim-security] Arbitrary Ex Command Execution via File Names in C Omni-Completion in Vim < 9.2.1011


From: Christian Brabandt <cb () 256bit org>
Date: Tue, 25 Aug 2026 23:28:09 +0200

Arbitrary Ex Command Execution via File Names in C Omni-Completion in Vim < 9.2.1011
====================================================================================

Date: 25.08.2026
Severity: Medium
CVE: *requested, not yet assigned*
CWE: Improper Control of Generation of Code ('Code Injection') (CWE-94),
     Inclusion of Functionality from Untrusted Control Sphere (CWE-829),
     Improper Neutralization of Argument Delimiters in a Command ('Argument Injection') (CWE-88)

## Summary

The C omni-completion script in `runtime/autoload/ccomplete.vim` looks up
struct members by building a `:vimgrep` command and running it with
`:execute`.  Besides the type name, that command line also holds the list of
tags file names returned by `tagfiles()`.  Those names were escaped for the
space, the backslash, `#` and `%`, but not for the bar.  Since `:vimgrep`
accepts another command after a bar, a file name whose name contains one
ends the `:vimgrep` command early and what follows is executed as an Ex
command when the user invokes omni-completion on a member access.

This is the same defect in the same command line as GHSA-cx73-phcg-3j5g,
fixed in patch [v9.2.0845](https://github.com/vim/vim/releases/tag/v9.2.0845),
which addressed only the type name.  Unlike that issue, no crafted tags file
content is needed here: the file name itself carries the payload.

## Description

`runtime/ftplugin/c.vim` sets `omnifunc=ccomplete#Complete` on C buffers
when filetype plugins are enabled.  When completing a member access, and the
declaration is not found in the buffer itself, `StructMembers()` searches the
tags files:

    var fnames: string = tagfiles()
      ->map((_, v: string) => escape(v, ' \#%'))
      ->join()
    ...
    execute 'silent! keepjumps noautocmd '
      .. n .. 'vimgrep ' .. '/\t\V' .. escape(typename, '/\') .. '\m\(\t\|$\)/j '
      .. fnames

`tagfiles()` returns the names of the tags files in effect for the buffer,
derived from the `'tags'` option.  The names are appended unquoted to the
`:vimgrep` argument list.

The `:vimgrep` command is defined with the `EX_TRLBAR` flag, so a bar ends
the command and starts a new one.  A bar in a file name is therefore not
part of the name but a command separator, and the remainder of the line is
parsed and executed as an independent Ex command by the same `:execute`.
Escaping the space limits what such a command can contain, but does not
prevent it, and `ccomplete.vim` is a `vim9script` file, so the injected text
is parsed with Vim9 syntax.  The leading `:silent!` suppresses the resulting
error, so the injected command runs without a visible failure.

The issue has been addressed by escaping the bar in the tags file names as
well.

## Impact

Arbitrary Ex command execution, and through commands such as `:!` arbitrary
operating-system command execution, in the context of the user running Vim.
Exploitation requires:

- Vim with filetype plugins enabled
- a tags file whose path contains a bar, for example because it is stored in
  a directory whose name contains one, and a `'tags'` value under which that
  file is found,
- the victim opening a C file from that tree and invoking omni-completion
  with `CTRL-X CTRL-O` on a member access whose type is only known from the
  tags file.

The severity is rated Medium.  The payload travels in a path rather than in
file content, so it is not visible to review that inspects files only.  On
the other hand a bar in a directory name is unusual and conspicuous, it is
not a valid character in file names on MS-Windows, and the crafted name has
no effect until the user deliberately invokes omni-completion on a type that
is not declared in the edited buffer, since the completion would otherwise
not consult the tags files at all.

## Acknowledgements

The Vim project would like to thank Yazan Balawneh, Cystack.ps for reporting
the issue.

## References

The issue has been fixed as of Vim patch [v9.2.1011](https://github.com/vim/vim/releases/tag/v9.2.1011).

- [Commit](https://github.com/vim/vim/commit/331d5d67028505c5b6043603a57d1e8497b922b5)
- [Github Security Advisory](https://github.com/vim/vim/security/advisories/GHSA-r77m-8m55-rpr6)

Best,
Christian
-- 
Auge um Auge - und die ganze Welt wird blind sein.
                -- Mahatma Gandhi


Current thread: