vim/backport-CVE-2022-3297.patch
2022-11-03 15:22:25 +08:00

163 lines
5.1 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.

From 0ff01835a40f549c5c4a550502f62a2ac9ac447c Mon Sep 17 00:00:00 2001
From: Bram Moolenaar <Bram@vim.org>
Date: Sat, 24 Sep 2022 19:20:30 +0100
Subject: [PATCH] patch 9.0.0579: using freed memory when 'tagfunc' wipes out
buffer
Problem: Using freed memory when 'tagfunc' wipes out buffer that holds
'complete'.
Solution: Make a copy of the option. Make sure cursor position is valid.
---
src/insexpand.c | 40 ++++++++++++++++++++++++-------
src/move.c | 1 +
src/testdir/test_ins_complete.vim | 20 ++++++++++++++--
3 files changed, 50 insertions(+), 11 deletions(-)
diff --git a/src/insexpand.c b/src/insexpand.c
index 24308e6..3585ef2 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -2485,7 +2485,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)
@@ -3188,9 +3189,10 @@ enum
*/
typedef struct
{
- char_u *e_cpt; // current entry in 'complete'
+ char_u *e_cpt_copy; // copy of 'complete'
+ char_u *e_cpt; // current entry in "e_cpt_copy"
buf_T *ins_buf; // buffer being scanned
- pos_T *cur_match_pos; // current match position
+ pos_T *cur_match_pos; // current match position
pos_T prev_match_pos; // previous match position
int set_match_pos; // save first_match_pos/last_match_pos
pos_T first_match_pos; // first match position
@@ -3257,7 +3259,8 @@ process_next_cpt_value(
st->set_match_pos = TRUE;
}
else if (vim_strchr((char_u *)"buwU", *st->e_cpt) != NULL
- && (st->ins_buf = ins_compl_next_buf(st->ins_buf, *st->e_cpt)) != curbuf)
+ && (st->ins_buf = ins_compl_next_buf(
+ st->ins_buf, *st->e_cpt)) != curbuf)
{
// Scan a buffer, but not the current one.
if (st->ins_buf->b_ml.ml_mfp != NULL) // loaded buffer
@@ -3756,19 +3759,30 @@ get_next_completion_match(int type, ins_compl_next_state_T *st, pos_T *ini)
static int
ins_compl_get_exp(pos_T *ini)
{
- static ins_compl_next_state_T st;
+ static ins_compl_next_state_T st;
+ static int st_cleared = FALSE;
int i;
int found_new_match;
int type = ctrl_x_mode;
if (!compl_started)
{
- FOR_ALL_BUFFERS(st.ins_buf)
- st.ins_buf->b_scanned = 0;
+ buf_T *buf;
+
+ FOR_ALL_BUFFERS(buf)
+ buf->b_scanned = 0;
+ if (!st_cleared)
+ {
+ CLEAR_FIELD(st);
+ st_cleared = TRUE;
+ }
st.found_all = FALSE;
st.ins_buf = curbuf;
- st.e_cpt = (compl_cont_status & CONT_LOCAL)
- ? (char_u *)"." : curbuf->b_p_cpt;
+ vim_free(st.e_cpt_copy);
+ // Make a copy of 'complete', if case the buffer is wiped out.
+ st.e_cpt_copy = vim_strsave((compl_cont_status & CONT_LOCAL)
+ ? (char_u *)"." : curbuf->b_p_cpt);
+ st.e_cpt = st.e_cpt_copy == NULL ? (char_u *)"" : st.e_cpt_copy;
st.last_match_pos = st.first_match_pos = *ini;
}
else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf))
@@ -4112,6 +4126,7 @@ ins_compl_next(
int todo = count;
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.
@@ -4144,6 +4159,13 @@ ins_compl_next(
&num_matches) == -1)
return -1;
+ 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 b061a75..6c654ac 100644
--- a/src/move.c
+++ b/src/move.c
@@ -652,6 +652,7 @@ cursor_valid(void)
void
validate_cursor(void)
{
+ check_cursor();
check_cursor_moved(curwin);
if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
curs_columns(TRUE);
diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim
index b303993..702a87c 100644
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -628,9 +628,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', {})
@@ -2233,4 +2232,21 @@ func Test_ins_complete_end_of_line()
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
--
2.27.0