comparison src/insexpand.c @ 30487:79e2d9b7780c v9.0.0579

patch 9.0.0579: using freed memory when 'tagfunc' wipes out buffer Commit: https://github.com/vim/vim/commit/0ff01835a40f549c5c4a550502f62a2ac9ac447c Author: Bram Moolenaar <Bram@vim.org> Date: Sat Sep 24 19:20:30 2022 +0100 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.
author Bram Moolenaar <Bram@vim.org>
date Sat, 24 Sep 2022 20:30:03 +0200
parents faecc8c6916f
children 101f08b49ed3
comparison
equal deleted inserted replaced
30486:004697058faf 30487:79e2d9b7780c
2488 { 2488 {
2489 static win_T *wp = NULL; 2489 static win_T *wp = NULL;
2490 2490
2491 if (flag == 'w') // just windows 2491 if (flag == 'w') // just windows
2492 { 2492 {
2493 if (buf == curbuf || wp == NULL) // first call for this flag/expansion 2493 if (buf == curbuf || !win_valid(wp))
2494 // first call for this flag/expansion or window was closed
2494 wp = curwin; 2495 wp = curwin;
2495 while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin 2496 while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin
2496 && wp->w_buffer->b_scanned) 2497 && wp->w_buffer->b_scanned)
2497 ; 2498 ;
2498 buf = wp->w_buffer; 2499 buf = wp->w_buffer;
3186 * state information used for getting the next set of insert completion 3187 * state information used for getting the next set of insert completion
3187 * matches. 3188 * matches.
3188 */ 3189 */
3189 typedef struct 3190 typedef struct
3190 { 3191 {
3191 char_u *e_cpt; // current entry in 'complete' 3192 char_u *e_cpt_copy; // copy of 'complete'
3193 char_u *e_cpt; // current entry in "e_cpt_copy"
3192 buf_T *ins_buf; // buffer being scanned 3194 buf_T *ins_buf; // buffer being scanned
3193 pos_T *cur_match_pos; // current match position 3195 pos_T *cur_match_pos; // current match position
3194 pos_T prev_match_pos; // previous match position 3196 pos_T prev_match_pos; // previous match position
3195 int set_match_pos; // save first_match_pos/last_match_pos 3197 int set_match_pos; // save first_match_pos/last_match_pos
3196 pos_T first_match_pos; // first match position 3198 pos_T first_match_pos; // first match position
3197 pos_T last_match_pos; // last match position 3199 pos_T last_match_pos; // last match position
3198 int found_all; // found all matches of a certain type. 3200 int found_all; // found all matches of a certain type.
3255 // Remember the first match so that the loop stops when we 3257 // Remember the first match so that the loop stops when we
3256 // wrap and come back there a second time. 3258 // wrap and come back there a second time.
3257 st->set_match_pos = TRUE; 3259 st->set_match_pos = TRUE;
3258 } 3260 }
3259 else if (vim_strchr((char_u *)"buwU", *st->e_cpt) != NULL 3261 else if (vim_strchr((char_u *)"buwU", *st->e_cpt) != NULL
3260 && (st->ins_buf = ins_compl_next_buf(st->ins_buf, *st->e_cpt)) != curbuf) 3262 && (st->ins_buf = ins_compl_next_buf(
3263 st->ins_buf, *st->e_cpt)) != curbuf)
3261 { 3264 {
3262 // Scan a buffer, but not the current one. 3265 // Scan a buffer, but not the current one.
3263 if (st->ins_buf->b_ml.ml_mfp != NULL) // loaded buffer 3266 if (st->ins_buf->b_ml.ml_mfp != NULL) // loaded buffer
3264 { 3267 {
3265 compl_started = TRUE; 3268 compl_started = TRUE;
3754 * Return the total number of matches or -1 if still unknown -- Acevedo 3757 * Return the total number of matches or -1 if still unknown -- Acevedo
3755 */ 3758 */
3756 static int 3759 static int
3757 ins_compl_get_exp(pos_T *ini) 3760 ins_compl_get_exp(pos_T *ini)
3758 { 3761 {
3759 static ins_compl_next_state_T st; 3762 static ins_compl_next_state_T st;
3763 static int st_cleared = FALSE;
3760 int i; 3764 int i;
3761 int found_new_match; 3765 int found_new_match;
3762 int type = ctrl_x_mode; 3766 int type = ctrl_x_mode;
3763 3767
3764 if (!compl_started) 3768 if (!compl_started)
3765 { 3769 {
3766 FOR_ALL_BUFFERS(st.ins_buf) 3770 buf_T *buf;
3767 st.ins_buf->b_scanned = 0; 3771
3772 FOR_ALL_BUFFERS(buf)
3773 buf->b_scanned = 0;
3774 if (!st_cleared)
3775 {
3776 CLEAR_FIELD(st);
3777 st_cleared = TRUE;
3778 }
3768 st.found_all = FALSE; 3779 st.found_all = FALSE;
3769 st.ins_buf = curbuf; 3780 st.ins_buf = curbuf;
3770 st.e_cpt = (compl_cont_status & CONT_LOCAL) 3781 vim_free(st.e_cpt_copy);
3771 ? (char_u *)"." : curbuf->b_p_cpt; 3782 // Make a copy of 'complete', if case the buffer is wiped out.
3783 st.e_cpt_copy = vim_strsave((compl_cont_status & CONT_LOCAL)
3784 ? (char_u *)"." : curbuf->b_p_cpt);
3785 st.e_cpt = st.e_cpt_copy == NULL ? (char_u *)"" : st.e_cpt_copy;
3772 st.last_match_pos = st.first_match_pos = *ini; 3786 st.last_match_pos = st.first_match_pos = *ini;
3773 } 3787 }
3774 else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf)) 3788 else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf))
3775 st.ins_buf = curbuf; // In case the buffer was wiped out. 3789 st.ins_buf = curbuf; // In case the buffer was wiped out.
3776 3790
4110 { 4124 {
4111 int num_matches = -1; 4125 int num_matches = -1;
4112 int todo = count; 4126 int todo = count;
4113 int advance; 4127 int advance;
4114 int started = compl_started; 4128 int started = compl_started;
4129 buf_T *orig_curbuf = curbuf;
4115 4130
4116 // When user complete function return -1 for findstart which is next 4131 // When user complete function return -1 for findstart which is next
4117 // time of 'always', compl_shown_match become NULL. 4132 // time of 'always', compl_shown_match become NULL.
4118 if (compl_shown_match == NULL) 4133 if (compl_shown_match == NULL)
4119 return -1; 4134 return -1;
4141 // Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap 4156 // Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap
4142 // around. 4157 // around.
4143 if (find_next_completion_match(allow_get_expansion, todo, advance, 4158 if (find_next_completion_match(allow_get_expansion, todo, advance,
4144 &num_matches) == -1) 4159 &num_matches) == -1)
4145 return -1; 4160 return -1;
4161
4162 if (curbuf != orig_curbuf)
4163 {
4164 // In case some completion function switched buffer, don't want to
4165 // insert the completion elsewhere.
4166 return -1;
4167 }
4146 4168
4147 // Insert the text of the new completion, or the compl_leader. 4169 // Insert the text of the new completion, or the compl_leader.
4148 if (compl_no_insert && !started) 4170 if (compl_no_insert && !started)
4149 { 4171 {
4150 ins_bytes(compl_orig_text + get_compl_len()); 4172 ins_bytes(compl_orig_text + get_compl_len());