vim-patch:9.0.0579: using freed memory when 'tagfunc' wipes out buffer (#24838)

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.

0ff01835a4

Cherry-pick a cmdwin change from patch 9.0.0500.

Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
zeertzjq 2023-08-22 22:48:55 +08:00 committed by GitHub
parent e34eb4ccf9
commit b84a67f50e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 48 additions and 12 deletions

View File

@ -4572,7 +4572,8 @@ static int open_cmdwin(void)
ccline.cmdlen = (int)strlen(ccline.cmdbuff); ccline.cmdlen = (int)strlen(ccline.cmdbuff);
ccline.cmdbufflen = ccline.cmdlen + 1; ccline.cmdbufflen = ccline.cmdlen + 1;
ccline.cmdpos = curwin->w_cursor.col; ccline.cmdpos = curwin->w_cursor.col;
if (ccline.cmdpos > ccline.cmdlen) { // If the cursor is on the last character, it probably should be after it.
if (ccline.cmdpos == ccline.cmdlen - 1 || ccline.cmdpos > ccline.cmdlen) {
ccline.cmdpos = ccline.cmdlen; ccline.cmdpos = ccline.cmdlen;
} }
if (cmdwin_result == K_IGNORE) { if (cmdwin_result == K_IGNORE) {

View File

@ -64,6 +64,7 @@
#include "nvim/ui.h" #include "nvim/ui.h"
#include "nvim/undo.h" #include "nvim/undo.h"
#include "nvim/vim.h" #include "nvim/vim.h"
#include "nvim/window.h"
// Definitions used for CTRL-X submode. // Definitions used for CTRL-X submode.
// Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[] // Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[]
@ -161,7 +162,8 @@ struct compl_S {
/// state information used for getting the next set of insert completion /// state information used for getting the next set of insert completion
/// matches. /// matches.
typedef struct { typedef struct {
char *e_cpt; ///< current entry in 'complete' char *e_cpt_copy; ///< copy of 'complete'
char *e_cpt; ///< current entry in "e_cpt_copy"
buf_T *ins_buf; ///< buffer being scanned 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 pos_T prev_match_pos; ///< previous match position
@ -2219,7 +2221,8 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag)
static win_T *wp = NULL; static win_T *wp = NULL;
if (flag == 'w') { // just windows 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; wp = curwin;
} }
assert(wp); assert(wp);
@ -3287,6 +3290,7 @@ static bool get_next_completion_match(int type, ins_compl_next_state_T *st, pos_
static int ins_compl_get_exp(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 bool st_cleared = false;
int i; int i;
int found_new_match; int found_new_match;
int type = ctrl_x_mode; int type = ctrl_x_mode;
@ -3297,9 +3301,16 @@ static int ins_compl_get_exp(pos_T *ini)
FOR_ALL_BUFFERS(buf) { FOR_ALL_BUFFERS(buf) {
buf->b_scanned = false; buf->b_scanned = false;
} }
if (!st_cleared) {
CLEAR_FIELD(st);
st_cleared = true;
}
st.found_all = false; st.found_all = false;
st.ins_buf = curbuf; st.ins_buf = curbuf;
st.e_cpt = (compl_cont_status & CONT_LOCAL) ? "." : curbuf->b_p_cpt; xfree(st.e_cpt_copy);
// Make a copy of 'complete', in case the buffer is wiped out.
st.e_cpt_copy = xstrdup((compl_cont_status & CONT_LOCAL) ? "." : curbuf->b_p_cpt);
st.e_cpt = st.e_cpt_copy == NULL ? "" : st.e_cpt_copy;
st.last_match_pos = st.first_match_pos = *ini; st.last_match_pos = st.first_match_pos = *ini;
} else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf)) { } else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf)) {
st.ins_buf = curbuf; // In case the buffer was wiped out. st.ins_buf = curbuf; // In case the buffer was wiped out.
@ -3599,6 +3610,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
int num_matches = -1; int num_matches = -1;
int todo = count; int todo = count;
const bool started = compl_started; const bool started = compl_started;
buf_T *const orig_curbuf = curbuf;
// When user complete function return -1 for findstart which is next // When user complete function return -1 for findstart which is next
// time of 'always', compl_shown_match become NULL. // time of 'always', compl_shown_match become NULL.
@ -3634,6 +3646,12 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
return -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. // Insert the text of the new completion, or the compl_leader.
if (compl_no_insert && !started) { if (compl_no_insert && !started) {
ins_bytes(compl_orig_text + get_compl_len()); ins_bytes(compl_orig_text + get_compl_len());

View File

@ -622,6 +622,7 @@ int cursor_valid(void)
// w_topline must be valid, you may need to call update_topline() first! // w_topline must be valid, you may need to call update_topline() first!
void validate_cursor(void) void validate_cursor(void)
{ {
check_cursor();
check_cursor_moved(curwin); check_cursor_moved(curwin);
if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW)) { if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW)) {
curs_columns(curwin, true); curs_columns(curwin, true);

View File

@ -316,7 +316,7 @@ local function test_cmdline(linegrid)
screen:expect{grid=[[ screen:expect{grid=[[
| |
{2:[No Name] }| {2:[No Name] }|
{1::}make^ | {1::}mak^e |
{3:[Command Line] }| {3:[Command Line] }|
| |
]]} ]]}
@ -326,7 +326,7 @@ local function test_cmdline(linegrid)
screen:expect{grid=[[ screen:expect{grid=[[
| |
{2:[No Name] }| {2:[No Name] }|
{1::}make^ | {1::}mak^e |
{3:[Command Line] }| {3:[Command Line] }|
| |
]], cmdline={nil, { ]], cmdline={nil, {
@ -339,7 +339,7 @@ local function test_cmdline(linegrid)
screen:expect{grid=[[ screen:expect{grid=[[
| |
{2:[No Name] }| {2:[No Name] }|
{1::}make^ | {1::}mak^e |
{3:[Command Line] }| {3:[Command Line] }|
| |
]], cmdline={nil, { ]], cmdline={nil, {
@ -352,7 +352,7 @@ local function test_cmdline(linegrid)
screen:expect{grid=[[ screen:expect{grid=[[
| |
{2:[No Name] }| {2:[No Name] }|
{1::}make^ | {1::}mak^e |
{3:[Command Line] }| {3:[Command Line] }|
| |
]]} ]]}

View File

@ -3239,7 +3239,7 @@ describe('builtin popupmenu', function()
| |
{3:[No Name] }| {3:[No Name] }|
{1::}sign define | {1::}sign define |
{1::}sign define^ | {1::}sign defin^e |
{1:~ }| {1:~ }|
{1:~ }| {1:~ }|
{1:~ }| {1:~ }|

View File

@ -52,7 +52,7 @@ describe('search highlighting', function()
{1:~ }| {1:~ }|
/text^ | /text^ |
]], win_viewport={ ]], win_viewport={
[2] = {win = {id = 1000}, topline = 0, botline = 3, curline = 0, curcol = 9, linecount = 2, sum_scroll_delta = 0}; [2] = {win = {id = 1000}, topline = 0, botline = 3, curline = 0, curcol = 8, linecount = 2, sum_scroll_delta = 0};
}} }}
end) end)

View File

@ -645,9 +645,8 @@ func Test_pum_with_preview_win()
call writefile(lines, 'Xpreviewscript') call writefile(lines, 'Xpreviewscript')
let buf = RunVimInTerminal('-S Xpreviewscript', #{rows: 12}) let buf = RunVimInTerminal('-S Xpreviewscript', #{rows: 12})
call TermWait(buf, 50)
call term_sendkeys(buf, "Gi\<C-X>\<C-O>") call term_sendkeys(buf, "Gi\<C-X>\<C-O>")
call TermWait(buf, 100) call TermWait(buf, 200)
call term_sendkeys(buf, "\<C-N>") call term_sendkeys(buf, "\<C-N>")
call VerifyScreenDump(buf, 'Test_pum_with_preview_win', {}) call VerifyScreenDump(buf, 'Test_pum_with_preview_win', {})
@ -2237,4 +2236,21 @@ func Test_ins_complete_end_of_line()
bwipe! bwipe!
endfunc 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 " vim: shiftwidth=2 sts=2 expandtab