mirror of
https://github.com/neovim/neovim.git
synced 2026-07-09 19:09:39 +00:00
vim-patch:9.2.0735: [security]: arbitrary Ex command execution during C omni-completion (#40441)
Problem: [security]: With C omni-completion, a crafted tags file can execute
arbitrary Ex commands when completing a struct/union member
(cipher-creator)
Solution: Escape the type field before inserting it into the :vimgrep
pattern so it cannot close the pattern and start a new command
(Hirohito Higashi).
Github Security Advisory:
https://github.com/vim/vim/security/advisories/GHSA-mf92-v4xw-j45x
6b611b0d15
Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
This commit is contained in:
@@ -555,7 +555,9 @@ func s:StructMembers(typename, items, all)
|
||||
if complete_check()
|
||||
return []
|
||||
endif
|
||||
exe 'silent! keepj noautocmd ' . n . 'vimgrep /\t' . typename . '\(\t\|$\)/j ' . fnames
|
||||
exe 'silent! keepj noautocmd '
|
||||
\ .. n .. 'vimgrep /\t' .. escape(typename, '/\') .. '\(\t\|$\)/j '
|
||||
\ .. fnames
|
||||
|
||||
let qflist = getqflist()
|
||||
if len(qflist) > 0 || match(typename, "::") < 0
|
||||
|
||||
62
test/old/testdir/test_plugin_ccomplete.vim
Normal file
62
test/old/testdir/test_plugin_ccomplete.vim
Normal file
@@ -0,0 +1,62 @@
|
||||
" Tests for the C omni-completion plugin (runtime/autoload/ccomplete.vim).
|
||||
|
||||
func s:WriteTags(lines)
|
||||
" Mark unsorted so lookup is a linear scan regardless of entry order.
|
||||
let tagsfile = tempname()
|
||||
call writefile(["!_TAG_FILE_SORTED\t0\t/0/"] + a:lines, tagsfile)
|
||||
return tagsfile
|
||||
endfunc
|
||||
|
||||
" A crafted typeref field is interpolated into the :vimgrep pattern in
|
||||
" StructMembers(). Without escaping, "/" closes the pattern and "|" starts a
|
||||
" new Ex command, so the field runs as an Ex command during completion.
|
||||
func Test_ccomplete_no_exec_via_typeref()
|
||||
unlet! g:ccomplete_injected
|
||||
let tagsfile = s:WriteTags([
|
||||
\ "myvar\tmain.c\t/^x$/;\"\tv\ttyperef:x/|let g:ccomplete_injected = 1|\"",
|
||||
\ ])
|
||||
|
||||
let save_tags = &tags
|
||||
let &tags = tagsfile
|
||||
|
||||
new
|
||||
call ccomplete#Complete(1, '')
|
||||
call ccomplete#Complete(0, 'myvar.x')
|
||||
|
||||
call assert_false(exists('g:ccomplete_injected'),
|
||||
\ 'typeref field was executed as an Ex command during omni-completion')
|
||||
|
||||
bwipe!
|
||||
let &tags = save_tags
|
||||
unlet! g:ccomplete_injected
|
||||
endfunc
|
||||
|
||||
" A legitimate typeref must still drive struct-member completion: escaping the
|
||||
" field value must not break the normal path.
|
||||
func Test_ccomplete_typeref_completion_still_works()
|
||||
let tagsfile = s:WriteTags([
|
||||
\ "myvar\tmain.c\t/^x$/;\"\tv\ttyperef:struct:mystruct",
|
||||
\ "alpha\tmain.c\t/^x$/;\"\tm\tstruct:mystruct",
|
||||
\ "beta\tmain.c\t/^x$/;\"\tm\tstruct:mystruct",
|
||||
\ ])
|
||||
|
||||
let save_tags = &tags
|
||||
let &tags = tagsfile
|
||||
|
||||
new
|
||||
call ccomplete#Complete(1, '')
|
||||
let items = ccomplete#Complete(0, 'myvar.')
|
||||
|
||||
call assert_equal(type([]), type(items),
|
||||
\ 'ccomplete#Complete did not return a list')
|
||||
let names = map(copy(items), 'v:val.word')
|
||||
call assert_true(index(names, 'alpha') >= 0,
|
||||
\ 'struct member "alpha" missing from completion: ' . string(names))
|
||||
call assert_true(index(names, 'beta') >= 0,
|
||||
\ 'struct member "beta" missing from completion: ' . string(names))
|
||||
|
||||
bwipe!
|
||||
let &tags = save_tags
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
Reference in New Issue
Block a user