mirror of
https://github.com/neovim/neovim.git
synced 2026-08-02 04:51:38 +08:00
vim-patch:9.2.0869: buf_copy_options() can lose the P_INSECURE flag (#41040)
Problem: An insecurely-set 'indentexpr', 'formatexpr', 'includeexpr'
or 'complete' value can end up evaluated outside the
sandbox after buf_copy_options() and clears the flag.
Solution: Copy the insecure flag alongside the value in
buf_copy_options(), and make 'complete' a per-buffer
insecure-flags field
Supported by AI.
closes: vim/vim#20861
35f7fdfdfb
I'm a bit hesitant to port this, but it's a follow-up to #39452.
Co-authored-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
parent
1eba3a75da
commit
7b7a21bd2a
@ -557,6 +557,7 @@ struct file_buffer {
|
||||
#ifdef BACKSLASH_IN_FILENAME
|
||||
char *b_p_csl; ///< 'completeslash'
|
||||
#endif
|
||||
uint32_t b_p_cpt_flags; ///< flags for 'complete'
|
||||
Callback *b_p_cpt_cb; ///< F{func} in 'complete' callback
|
||||
int b_p_cpt_count; ///< Count of values in 'complete'
|
||||
char *b_p_cfu; ///< 'completefunc'
|
||||
|
||||
@ -1926,6 +1926,8 @@ uint32_t *insecure_flag(win_T *const wp, OptIndex opt_idx, int opt_flags)
|
||||
return &wp->w_buffer->b_p_fex_flags;
|
||||
case kOptIncludeexpr:
|
||||
return &wp->w_buffer->b_p_inex_flags;
|
||||
case kOptComplete:
|
||||
return &wp->w_buffer->b_p_cpt_flags;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -5597,6 +5599,8 @@ void didset_window_options(win_T *wp, bool valid_cursor)
|
||||
}
|
||||
|
||||
#define COPY_OPT_SCTX(buf, bv) buf->b_p_script_ctx[bv] = options[buf_opt_idx[bv]].script_ctx
|
||||
#define COPY_OPT_INSECURE(flagsfield, bv) \
|
||||
(flagsfield) = (options[buf_opt_idx[bv]].flags & kOptFlagInsecure)
|
||||
|
||||
/// Copy global option values to local options for one buffer.
|
||||
/// Used when creating a new buffer and sometimes when entering a buffer.
|
||||
@ -5707,6 +5711,7 @@ void buf_copy_options(buf_T *buf, int flags)
|
||||
}
|
||||
buf->b_p_cpt = xstrdup(p_cpt);
|
||||
COPY_OPT_SCTX(buf, kBufOptComplete);
|
||||
COPY_OPT_INSECURE(buf->b_p_cpt_flags, kBufOptComplete);
|
||||
set_buflocal_cpt_callbacks(buf);
|
||||
#ifdef BACKSLASH_IN_FILENAME
|
||||
buf->b_p_csl = xstrdup(p_csl);
|
||||
@ -5786,11 +5791,13 @@ void buf_copy_options(buf_T *buf, int flags)
|
||||
buf->b_s.b_p_spo_flags = spo_flags;
|
||||
buf->b_p_inde = xstrdup(p_inde);
|
||||
COPY_OPT_SCTX(buf, kBufOptIndentexpr);
|
||||
COPY_OPT_INSECURE(buf->b_p_inde_flags, kBufOptIndentexpr);
|
||||
buf->b_p_indk = xstrdup(p_indk);
|
||||
COPY_OPT_SCTX(buf, kBufOptIndentkeys);
|
||||
buf->b_p_fp = empty_string_option;
|
||||
buf->b_p_fex = xstrdup(p_fex);
|
||||
COPY_OPT_SCTX(buf, kBufOptFormatexpr);
|
||||
COPY_OPT_INSECURE(buf->b_p_fex_flags, kBufOptFormatexpr);
|
||||
buf->b_p_sua = xstrdup(p_sua);
|
||||
COPY_OPT_SCTX(buf, kBufOptSuffixesadd);
|
||||
buf->b_p_keymap = xstrdup(p_keymap);
|
||||
@ -5826,6 +5833,7 @@ void buf_copy_options(buf_T *buf, int flags)
|
||||
buf->b_p_inc = empty_string_option;
|
||||
buf->b_p_inex = xstrdup(p_inex);
|
||||
COPY_OPT_SCTX(buf, kBufOptIncludeexpr);
|
||||
COPY_OPT_INSECURE(buf->b_p_inex_flags, kBufOptIncludeexpr);
|
||||
buf->b_p_cot = empty_string_option;
|
||||
buf->b_cot_flags = 0;
|
||||
buf->b_p_dict = empty_string_option;
|
||||
|
||||
@ -3117,4 +3117,122 @@ func Test_comma_option_key_value()
|
||||
set lcs&
|
||||
endfunc
|
||||
|
||||
func Test_insecure_flag_copied_to_new_buffer_indentexpr()
|
||||
let modeline = &modeline
|
||||
let modelineexpr = &modelineexpr
|
||||
"let modelinestrict = &modelinestrict
|
||||
|
||||
func! Xindentexprpwn(findstart, base)
|
||||
if a:findstart
|
||||
sandbox setglobal indentexpr=writefile(['leak'],\ 'Xindentexpr_proof')
|
||||
return 0
|
||||
endif
|
||||
return []
|
||||
endfunc
|
||||
|
||||
try
|
||||
set modeline modelineexpr "nomodelinestrict
|
||||
|
||||
call writefile([
|
||||
\ 'vim: set complete=FXindentexprpwn :',
|
||||
\ 'body',
|
||||
\ ], 'Xindentexpr_attack', 'D')
|
||||
call delete('Xindentexpr_proof')
|
||||
edit Xindentexpr_attack
|
||||
call cursor(2, 1)
|
||||
call feedkeys("i\<C-N>\<Esc>", 'xt')
|
||||
bwipe!
|
||||
|
||||
" A brand new buffer now inherits the poisoned 'indentexpr' via
|
||||
" buf_copy_options(). It must still be evaluated in the sandbox.
|
||||
enew!
|
||||
call setline(1, ['{', 'x', '}'])
|
||||
normal! 2G==
|
||||
call assert_false(filereadable('Xindentexpr_proof'))
|
||||
bwipe!
|
||||
finally
|
||||
let &modeline = modeline
|
||||
let &modelineexpr = modelineexpr
|
||||
"let &modelinestrict = modelinestrict
|
||||
set indentexpr&
|
||||
call delete('Xindentexpr_proof')
|
||||
delfunc Xindentexprpwn
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
func Test_insecure_flag_not_cleared_by_other_buffer_complete()
|
||||
let modeline = &modeline
|
||||
let modelineexpr = &modelineexpr
|
||||
"let modelinestrict = &modelinestrict
|
||||
|
||||
func! Xcompletepwn(findstart, base)
|
||||
if a:findstart
|
||||
call writefile(['leak'], 'Xcomplete_cross_proof')
|
||||
return 0
|
||||
endif
|
||||
return ['match']
|
||||
endfunc
|
||||
|
||||
try
|
||||
set modeline modelineexpr "nomodelinestrict
|
||||
|
||||
call writefile([
|
||||
\ 'vim: set complete=FXcompletepwn :',
|
||||
\ 'body',
|
||||
\ ], 'Xcomplete_cross_attack', 'D')
|
||||
call delete('Xcomplete_cross_proof')
|
||||
edit Xcomplete_cross_attack
|
||||
let bufA = bufnr('%')
|
||||
|
||||
" An unrelated buffer does a completely ordinary, trusted reset.
|
||||
new
|
||||
setlocal complete=.,w,b,u,t
|
||||
bwipe!
|
||||
|
||||
" Back in the modeline-tainted buffer: must still be sandboxed.
|
||||
exe 'buffer ' .. bufA
|
||||
call cursor(2, 1)
|
||||
call assert_fails('call feedkeys("i\<C-N>\<Esc>", "xt")', 'E48:')
|
||||
call assert_false(filereadable('Xcomplete_cross_proof'))
|
||||
bwipe!
|
||||
finally
|
||||
let &modeline = modeline
|
||||
let &modelineexpr = modelineexpr
|
||||
"let &modelinestrict = modelinestrict
|
||||
call delete('Xcomplete_cross_proof')
|
||||
delfunc Xcompletepwn
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
func Test_formatexpr_insecure_copied_to_new_buffer()
|
||||
new
|
||||
sandbox setglobal formatexpr=writefile(['leak'],\ 'Xleak_fex')
|
||||
enew!
|
||||
call setline(1, ['some text to format'])
|
||||
set textwidth=10
|
||||
silent! normal! gqq
|
||||
call assert_false(filereadable('Xleak_fex'))
|
||||
call delete('Xleak_fex')
|
||||
set formatexpr& textwidth&
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
" includeexpr: triggered via gf / find_pattern_in_path (used by [i, gf, etc.)
|
||||
func Test_includeexpr_insecure_copied_to_new_buffer()
|
||||
func Xleakinclude(fname)
|
||||
call writefile(['leak'], 'Xleak_inex')
|
||||
return a:fname
|
||||
endfunc
|
||||
new
|
||||
sandbox setglobal includeexpr=Xleakinclude(v:fname)
|
||||
enew!
|
||||
call setline(1, ['#include "foo.h"'])
|
||||
silent! normal! [i
|
||||
call assert_false(filereadable('Xleak_inex'))
|
||||
call delete('Xleak_inex')
|
||||
set includeexpr&
|
||||
delfunc Xleakinclude
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user