Re: [LFS Trac] #5982: vim-9.2.0847 (Security Update) (was: vim-9.2.0843 (Security Update))

LFS Trac ([email protected] via lfs-book Mailing List) <[email protected]> Sat, 25 Jul 2026 19:31:35 -0000
Newsgroups gmane.linux.lfs.book
Message-ID <[email protected]>
This is a multi-part message in MIME format...

------------=_1785007898-1024790-5931
Content-Type: multipart/related;
 boundary="===============7463204950342342417=="

--===============7463204950342342417==
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit

#5982: vim-9.2.0847 (Security Update)
-------------------------+-----------------------
 Reporter:  Joe Locash   |       Owner:  lfs-book
     Type:  enhancement  |      Status:  new
 Priority:  normal       |   Milestone:  13.1
Component:  Book         |     Version:  git
 Severity:  normal       |  Resolution:
 Keywords:               |
-------------------------+-----------------------
Changes (by Joe Locash):

 * summary:  vim-9.2.0843 (Security Update) => vim-9.2.0847 (Security
              Update)

Comment:

 4 more:

 {{{
 Use-after-free in JSON Decoding in Vim >= 9.2.0511 && Vim < 9.2.0844
 ====================================================================

 Date: 23.07.2026
 Severity: Medium
 CVE: *requested, not yet assigned*
 CWE: Use After Free (CWE-416),
      Out-of-bounds Read (CWE-125)

 ## Summary

 When Vim decodes a JSON message received on a channel, the decoder may
 need
 more bytes than the current read buffer holds.  It then obtains the next
 buffer, joins the two into a newly allocated one and frees the previous
 buffer.  The function decoding the surrounding item keeps its own pointer
 into the old buffer and does not refresh it after such a refill.  When the
 decoding of a string fails after a refill has taken place, for example
 because of an invalid escape sequence, the shared error path passes that
 stale pointer to the routine that formats the error message, which reads
 the
 freed memory.

 ## Description

 Channel JSON decoding uses a reader that can request more input while an
 item is being parsed.  `channel_fill()` in `src/channel.c` obtains the
 next
 queued read buffer, allocates a combined buffer holding the unparsed
 remainder followed by the new data, frees the previous buffer and stores
 the
 new one in the reader.

 `json_decode_string()` in `src/json.c` handles this correctly: before
 asking
 for more input it converts its position into an index, and it recomputes
 its
 pointer from the reader afterwards.  `json_decode_item()`, which parses
 the
 enclosing value, keeps a separate pointer into the buffer.  That pointer
 is
 refreshed at several points in its loop, but not after the call that
 decodes
 a string.  If that call first triggers a refill and then fails, the buffer
 the pointer refers to has been freed, and the error path at the end of the
 function reports the position using it.  The error text is composed from
 the
 memory at that address, so the freed buffer is read.

 Any channel using one of the JSON based modes is affected, since the
 reader
 is always given the ability to refill.  A peer that writes a message in
 parts controls where the input is split, and therefore whether the refill
 happens in the middle of a string.

 The socket server backend of the client-server feature is reachable
 without
 any authentication: it decodes whatever bytes a client has sent, before
 any
 field of the message is examined, so a client can trigger the condition
 with
 two writes and without a valid message.

 ## Impact

 A read of freed heap memory.  The bytes read are used to compose an error
 message, which is suppressed while a channel message is decoded and is
 only
 recorded when channel logging has been enabled, so the contents are
 normally
 not shown anywhere.  Depending on the state of the heap the read can also
 fault and end the Vim process, which for a server means the loss of
 unsaved
 changes.

 Exploitation requires:

 - Vim decoding JSON from a channel, either a channel opened by the user in
   one of the JSON based modes, or the socket server backend of the
   client-server feature, which is used when Vim is started with
   `--servername` and that backend,
 - a peer that can send a message split into parts, which for the socket
   server means any process able to connect to it: one running under the
 same
   user account for the default unix domain socket, or any process on the
   local machine for a `channel:` address, since those are bound to the
   loopback interface.

 The severity is rated Medium because the impact is limited to reading
 freed
 memory and, where that memory is no longer mapped, to ending the process;
 the contents that are read are not returned to the peer and are normally
 not
 displayed.

 ## Acknowledgements

 The Vim project would like to thank @tdjackey for reporting the issue.

 ## References

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

 -
 [Commit](https://github.com/vim/vim/commit/f8126294a526aa80c5123eb3079e325daee9ec75)
 - [Github Security
 Advisory](https://github.com/vim/vim/security/advisories/GHSA-69ch-
 22ch-r887)
 }}}

 {{{
 Arbitrary Ex Command Execution in C Omni-Completion in Vim < 9.2.0845
 ======================================================================

 Date: 23.07.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)

 ## Summary

 The C omni-completion script in `runtime/autoload/ccomplete.vim` looks up
 struct members by building a `:vimgrep` command that contains the type
 name
 taken from the `typeref:` field of a tags file entry, and running it with
 `:execute`.  The value was escaped only for the pattern delimiter and the
 backslash.  That is not sufficient: a value containing an unterminated
 collection makes Vim's own pattern skipping fail, after which the command
 parser looks for a command separator across the whole argument.  A crafted
 tags file can therefore run arbitrary Ex commands, and through them shell
 commands, when the user invokes omni-completion on a member access.

 This is a bypass of the fix released as patch v9.2.0735 for
 GHSA-mf92-v4xw-j45x, which prevented the same injection only for values
 containing the pattern delimiter.

 ## 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
 files listed in the tags file:

     execute 'silent! keepjumps noautocmd '
       .. n .. 'vimgrep ' .. '/\t' .. escape(typename, '/\') ..
 '\(\t\|$\)/j '
       .. fnames

 `typename` comes verbatim from the `typeref:` or `typename:` extension
 field
 of a tags entry, which Vim returns unchanged.

 The `:vimgrep` command may be followed by another command after a bar, so
 before looking for that bar Vim first skips over the search pattern.  When
 skipping the pattern does not end at the closing delimiter, the routine
 returns a failure and the caller silently falls back to the beginning of
 the
 argument, after which the first unescaped bar is treated as a command
 separator.

 Skipping the pattern fails on an unterminated collection: an opening
 bracket
 makes the pattern skipping look for the matching closing bracket and run
 to
 the end of the text instead.  A `typeref:` value that opens a bracket and
 then contains a bar therefore ends the `:vimgrep` command early, and what
 follows the bar is executed as an Ex command.  No delimiter character is
 needed, so the escaping applied by the earlier fix does not prevent it.
 The
 leading `:silent!` suppresses the resulting error, so the injected command
 runs without a visible failure.

 The issue has been addressed by matching the field literally, using the
 "very nomagic" mode for the part of the pattern that holds the value, so
 that no character in it can affect how the pattern is parsed.

 ## 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, which is the default in
   `runtime/defaults.vim` and in most distribution configurations, so that
   the C omni-completion function is installed on C buffers,
 - a tags file under the attacker's control, which is ordinary data to
   receive together with source code, for example in a cloned repository or
   an unpacked archive,
 - 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 because the crafted tags file has no effect
 until the user invokes omni-completion, which is a deliberate action, and
 because the type must not be declared in the edited buffer, since the
 completion would otherwise not consult the tags file at all.

 ## Acknowledgements

 The Vim project would like to thank Threonine for reporting the issue.

 ## References

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

 -
 [Commit](https://github.com/vim/vim/commit/2f628d8104958fa7421664f792ca6d4f7a39a10f)
 - [Github Security
 Advisory](https://github.com/vim/vim/security/advisories/GHSA-cx73-phcg-
 3j5g)
 }}}

 {{{
 Heap Buffer Overflow when Loading a Spell File in Vim < 9.2.0846
 ================================================================

 Date: 24.07.2026
 Severity: Medium
 CVE: *requested, not yet assigned*
 CWE: Heap-based Buffer Overflow (CWE-122),
      Out-of-bounds Write (CWE-787)

 ## Summary

 A spell file may contain a section holding sound-folding rules and a
 section
 holding a simple character mapping used for the same purpose.  Reading the
 first of these fills an index table with the value minus one for every
 entry.
 Reading the second uses that same table to count how many mappings share a
 byte, but did not reset it beforehand, so every count came out one too
 low.
 The buffer allocated from that count is then too small for the mappings
 that
 are stored into it, and the surplus is written past its end.  A crafted
 spell
 file therefore causes an out-of-bounds write when it is loaded, with part
 of
 the written data taken from the file.

 ## Description

 Vim loads a spell file when a language is selected with the 'spelllang'
 option.  The file is a sequence of sections, and the loader in
 `src/spellfile.c` dispatches on the section identifier without imposing an
 order on them or requiring them to be unique.

 Reading the sound-folding section ends with a call to `set_sal_first()`,
 which sets all 256 entries of the language's first-index table to minus
 one.
 This happens even when that section contains no rules at all.

 Reading the character mapping section calls `set_sofo()`, which reuses the
 same table as a temporary counter.  For every mapped character above 255
 it
 increments the entry belonging to the low byte of that character,
 allocates
 one list per entry from the resulting counts, and only afterwards clears
 the
 table again.  Since the counting started from minus one rather than zero,
 each count is one too low.  For a byte value used by two mapped characters
 the list is allocated for one pair and two are written into it, so the
 storing loop writes past the end of the allocation; part of what is
 written
 is the mapped-to character taken from the file.  For a byte value used by
 exactly one mapped character the count becomes zero, no list is allocated
 at
 all, and the storing loop follows a null pointer.

 The two sections are mutually exclusive by construction and the tool that
 generates spell files never emits both, but nothing in the loader rejected
 a
 file that contains them in that order.

 The issue has been addressed by clearing the table before it is used as a
 counter.

 ## Impact

 An out-of-bounds write to the heap, or a null pointer dereference, while a
 spell file is being loaded.  Part of the written data is taken from the
 spell file, and the sizes of the other sections in the same file influence
 the surrounding heap layout, so the effect is not limited to a crash,
 although no further consequence has been demonstrated.

 Exploitation requires the victim to load a crafted spell file, which
 happens
 when 'spelllang' is set to a value naming that file, or to a language for
 which the crafted file is found in the directories searched for spell
 files.
 Vim must be built with the spell checking and multi-byte features, which
 is
 the case for the "huge" feature set used by most distributions.

 The severity is rated Medium because a spell file has to be placed where
 Vim
 will load it and the option has to be set to select it.

 ## Acknowledgements

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

 ## References

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

 -
 [Commit](https://github.com/vim/vim/commit/05c41c922309c7a11b6ec2f124be66551c90d66a)
 - [Github Security
 Advisory](https://github.com/vim/vim/security/advisories/GHSA-9jqx-hgpr-
 6v64)
 }}}

 {{{
 Arbitrary Command Execution via the Vimball Record File in Vim < 9.2.0847
 =========================================================================

 Date: 24.07.2026
 Severity: Medium
 CVE: *requested, not yet assigned*
 CWE: Improper Control of Generation of Code (CWE-94),
      Inclusion of Functionality from Untrusted Control Sphere (CWE-829)

 ## Summary

 The vimball plugin records the files it extracts in a plain text file
 named
 `.VimballRecord`, so that they can be removed again later.  Each line of
 that
 file holds the commands to undo one installation, and they were executed
 without being checked.  The plugin refused to extract members whose name
 could inject into that file, but it did not refuse a member that *is* the
 record file.  A crafted vimball can therefore write chosen commands into
 the
 record, which are then executed the next time any vimball operation
 consults
 it.

 ## Description

 `runtime/autoload/vimball.vim` writes one line per installed vimball into
 `.VimballRecord` in the directory used for vimball installations, listing
 the
 commands that delete the files that were extracted.  When a vimball is
 installed or removed, `vimball#RmVimball()` searches that file for the
 line
 belonging to the archive, strips the archive name from the front of it and
 runs the remainder:

     sil! keepalt keepjumps exe exestring

 The name of each member of an archive was already checked, and names that
 contain a bar, a quote or a closing parenthesis were rejected, precisely
 so
 that a file name could not inject commands into the record.  That check
 does
 not apply to a member whose name is `.VimballRecord` itself: extracting it
 overwrites the record with content taken straight from the archive, and
 the
 content of a member is not examined at all.

 The commands placed there are run later, when a vimball with the matching
 name is installed or removed, and not while the crafted archive is being
 extracted.

 The issue has been addressed by refusing to extract a member named
 `.VimballRecord`, and by executing only entries of the expected form,
 namely
 a single call that deletes one file or directory.  Anything else in the
 record is reported and skipped.

 ## Impact

 Execution of arbitrary Ex commands, and through commands such as `:!`
 arbitrary operating-system commands, in the context of the user running
 Vim.

 Exploitation requires the victim to install a vimball from a source
 controlled by the attacker, either by sourcing it or through the getscript
 plugin, which downloads and sources vimballs automatically.  Installing a
 vimball already runs the commands contained in it, so the attacker does
 not
 gain the ability to run commands as such.  What the issue adds is that the
 archive itself can appear harmless while leaving commands behind that run
 at
 a later time, during an unrelated vimball installation or removal, and
 that
 continue to do so after the archive that placed them has been removed.

 The severity is rated Medium because installing a vimball from an
 untrusted
 source already permits commands from that vimball to run, so the issue
 extends the reach of such an installation rather than creating it.

 ## Acknowledgements

 The Vim project would like to thank tdjackey for reporting the issue.

 ## References

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

 -
 [Commit](https://github.com/vim/vim/commit/581a2f3ac9c6f96a26324f6b2c8c11415fd0d452)
 - [Github Security
 Advisory](https://github.com/vim/vim/security/advisories/GHSA-r22p-
 fhw4-84p2)
 }}}
-- 
Ticket URL: <https://wiki.linuxfromscratch.org/lfs/ticket/5982#comment:1>
LFS Trac <https://wiki.linuxfromscratch.org/lfs/>
Linux From Scratch: Your Distro, Your Rules.

--===============7463204950342342417==--

------------=_1785007898-1024790-5931
Content-Type: text/plain; charset="UTF-8"
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0

-- 
http://lists.linuxfromscratch.org/sympa/info/lfs-book
Unsubscribe: See the above information page

------------=_1785007898-1024790-5931--