vim/vim-CVE-2023-4752.patch
2025-02-25 14:17:22 +01:00

263 lines
7.8 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

diff --git a/src/insexpand.c b/src/insexpand.c
index 8d76c68..2ce675d 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -2162,7 +2162,8 @@ ins_compl_next_buf(buf_T *buf, int flag)
if (flag == 'w') // just windows
{
- if (buf == curbuf || wp == NULL) // first call for this flag/expansion
+ if (buf == curbuf || !win_valid(wp))
+ // first call for this flag/expansion or window was closed
wp = curwin;
while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin
&& wp->w_buffer->b_scanned)
@@ -2675,10 +2676,12 @@ ins_compl_get_exp(pos_T *ini)
{
static pos_T first_match_pos;
static pos_T last_match_pos;
- static char_u *e_cpt = (char_u *)""; // curr. entry in 'complete'
+ static char_u *e_cpt; // curr. entry in 'complete'
+ static char_u *e_cpt_copy; // copy of curr. entry in 'complete'
static int found_all = FALSE; // Found all matches of a
// certain type.
static buf_T *ins_buf = NULL; // buffer being scanned
+ static int st_cleared = FALSE;
pos_T *pos;
char_u **matches;
@@ -2697,12 +2700,34 @@ ins_compl_get_exp(pos_T *ini)
if (!compl_started)
{
- FOR_ALL_BUFFERS(ins_buf)
- ins_buf->b_scanned = 0;
+ buf_T *buf;
+
+ FOR_ALL_BUFFERS(buf)
+ buf->b_scanned = 0;
+ if (!st_cleared)
+ {
+ // this code does not match upstream commit, upstream clears struct `st`
+ // which includes the members which are arguments of `CLEAR_FIELD()` macros
+ // below. The code below should do the same thing as upstream `CLEAR_FIELD(st)`
+ CLEAR_FIELD(e_cpt);
+ CLEAR_FIELD(e_cpt_copy);
+ CLEAR_FIELD(ins_buf);
+ CLEAR_FIELD(pos);
+ CLEAR_FIELD(set_match_pos);
+ CLEAR_FIELD(first_match_pos);
+ CLEAR_FIELD(last_match_pos);
+ CLEAR_FIELD(found_all);
+ CLEAR_FIELD(dict);
+ CLEAR_FIELD(dict_f);
+ st_cleared = TRUE;
+ }
found_all = FALSE;
ins_buf = curbuf;
- e_cpt = (compl_cont_status & CONT_LOCAL)
- ? (char_u *)"." : curbuf->b_p_cpt;
+ vim_free(e_cpt_copy);
+ // Make a copy of 'complete', if case the buffer is wiped out.
+ e_cpt_copy = vim_strsave((compl_cont_status & CONT_LOCAL)
+ ? (char_u *)"." : curbuf->b_p_cpt);
+ e_cpt = e_cpt_copy == NULL ? (char_u *)"" : e_cpt_copy;
last_match_pos = first_match_pos = *ini;
}
else if (ins_buf != curbuf && !buf_valid(ins_buf))
@@ -3100,7 +3125,7 @@ ins_compl_get_exp(pos_T *ini)
else
{
// Mark a buffer scanned when it has been scanned completely
- if (type == 0 || type == CTRL_X_PATH_PATTERNS)
+ if (buf_valid(ins_buf) && (type == 0 || type == CTRL_X_PATH_PATTERNS))
ins_buf->b_scanned = TRUE;
compl_started = FALSE;
@@ -3210,6 +3235,7 @@ ins_compl_next(
int found_end = FALSE;
int advance;
int started = compl_started;
+ buf_T *orig_curbuf = curbuf;
// When user complete function return -1 for findstart which is next
// time of 'always', compl_shown_match become NULL.
@@ -3342,6 +3368,13 @@ ins_compl_next(
}
}
+ if (curbuf != orig_curbuf)
+ {
+ // In case some completion function switched buffer, don't want to
+ // insert the completion elsewhere.
+ return -1;
+ }
+
// Insert the text of the new completion, or the compl_leader.
if (compl_no_insert && !started)
{
diff --git a/src/move.c b/src/move.c
index 8ac1d0e..55cbd24 100644
--- a/src/move.c
+++ b/src/move.c
@@ -649,6 +649,7 @@ cursor_valid(void)
void
validate_cursor(void)
{
+ check_cursor_lnum();
check_cursor_moved(curwin);
if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
curs_columns(TRUE);
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index 108c528..e800864 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -92,6 +92,7 @@ NEW_TESTS = \
test_conceal \
test_const \
test_cpoptions \
+ test_crash \
test_crypt \
test_cscope \
test_cursor_func \
@@ -348,6 +349,7 @@ NEW_TESTS_RES = \
test_conceal.res \
test_const.res \
test_cpoptions.res \
+ test_crash.res \
test_crypt.res \
test_cscope.res \
test_cursor_func.res \
diff --git a/src/testdir/crash/poc_tagfunc.vim b/src/testdir/crash/poc_tagfunc.vim
new file mode 100644
index 0000000..49d9b6f
--- /dev/null
+++ b/src/testdir/crash/poc_tagfunc.vim
@@ -0,0 +1,6 @@
+fu Tagfunc(t,f,o)
+ bw
+endf
+set tagfunc=Tagfunc
+n0
+sil0norm0i
diff --git a/src/testdir/term_util.vim b/src/testdir/term_util.vim
index 8541ef3..3a06019 100644
--- a/src/testdir/term_util.vim
+++ b/src/testdir/term_util.vim
@@ -54,6 +54,7 @@ endfunc
" "cols" - width of the terminal window (max. 78)
" "statusoff" - number of lines the status is offset from default
" "wait_for_ruler" - if zero then don't wait for ruler to show
+" "cmd" - run any other command, e.g. "xxd" (used in xxd test)
func RunVimInTerminal(arguments, options)
" If Vim doesn't exit a swap file remains, causing other tests to fail.
" Remove it here.
@@ -88,7 +89,11 @@ func RunVimInTerminal(arguments, options)
let reset_u7 = ' --cmd "set t_u7=" '
endif
- let cmd = GetVimCommandCleanTerm() .. reset_u7 .. a:arguments
+ if empty(get(a:options, 'cmd', ''))
+ let cmd = GetVimCommandCleanTerm() .. reset_u7 .. a:arguments
+ else
+ let cmd = get(a:options, 'cmd')
+ endif
let options = #{curwin: 1}
if &termwinsize == ''
@@ -114,7 +119,7 @@ func RunVimInTerminal(arguments, options)
call TermWait(buf)
- if get(a:options, 'wait_for_ruler', 1)
+ if get(a:options, 'wait_for_ruler', 1) && empty(get(a:options, 'cmd', ''))
" Wait for "All" or "Top" of the ruler to be shown in the last line or in
" the status line of the last window. This can be quite slow (e.g. when
" using valgrind).
diff --git a/src/testdir/test_crash.vim b/src/testdir/test_crash.vim
new file mode 100644
index 0000000..4be1b49
--- /dev/null
+++ b/src/testdir/test_crash.vim
@@ -0,0 +1,40 @@
+" Some tests, that used to crash Vim
+source check.vim
+source screendump.vim
+
+CheckScreendump
+
+func Test_crash1()
+ if !executable('sh')
+ throw 'Skipped: sh not executable!'
+ endif
+ " The following used to crash Vim
+ let opts = #{cmd: 'sh'}
+ let vim = GetVimProg()
+
+ let buf = RunVimInTerminal('sh', opts)
+
+ let file = 'crash/poc_tagfunc.vim'
+ let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'"
+ let args = printf(cmn_args, vim, file)
+ call term_sendkeys(buf, args ..
+ \ ' || echo "crash 5: [OK]" >> X_crash1_result.txt' .. "\<cr>")
+
+ call TermWait(buf, 100)
+
+ " clean up
+ exe buf .. "bw!"
+
+ sp X_crash1_result.txt
+
+ let expected = [
+ \ 'crash 5: [OK]',
+ \ ]
+
+ call assert_equal(expected, getline(1, '$'))
+ bw!
+
+ call delete('X_crash1_result.txt')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim
index 613e952..24232d8 100644
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -496,9 +496,8 @@ func Test_pum_with_preview_win()
call writefile(lines, 'Xpreviewscript')
let buf = RunVimInTerminal('-S Xpreviewscript', #{rows: 12})
- call TermWait(buf, 50)
call term_sendkeys(buf, "Gi\<C-X>\<C-O>")
- call TermWait(buf, 100)
+ call TermWait(buf, 200)
call term_sendkeys(buf, "\<C-N>")
call VerifyScreenDump(buf, 'Test_pum_with_preview_win', {})
@@ -724,7 +723,23 @@ func Test_z1_complete_no_history()
exe "normal owh\<C-X>\<C-K>"
exe "normal owh\<C-N>"
call assert_equal(currmess, execute('messages'))
- close!
+ bwipe!
+endfunc
+
+func s:Tagfunc(t,f,o)
+ bwipe!
+ return []
+endfunc
+
+" This was using freed memory, since 'complete' was in a wiped out buffer.
+" Also using a window that was closed.
+func Test_tagfunc_wipes_out_buffer()
+ new
+ set complete=.,t,w,b,u,i
+ se tagfunc=s:Tagfunc
+ sil norm i␎
+ bwipe!
endfunc
+
" vim: shiftwidth=2 sts=2 expandtab