patch 9.1.1410: out-of-bounds access with 'completefunc'
Problem: out-of-bounds access with 'completefunc' (csetc)
Solution: check if it is safe to advance cpt_sources_index
(Girish Palya)
fixes: #17363
closes: #17374
Co-authored-by: @csetc
Signed-off-by: Girish Palya <girishji@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/insexpand.c b/src/insexpand.c
index 700f734..3592fdc 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -227,7 +227,7 @@
static cpt_source_T *cpt_sources_array; // Pointer to the array of completion sources
static int cpt_sources_count; // Total number of completion sources specified in the 'cpt' option
-static int cpt_sources_index; // Index of the current completion source being expanded
+static int cpt_sources_index = -1; // Index of the current completion source being expanded
// "compl_match_array" points the currently displayed list of entries in the
// popup menu. It is NULL when there is no popup menu.
@@ -3950,6 +3950,19 @@
}
/*
+ * Check if 'cpt' list index can be advanced to the next completion source.
+ */
+ static int
+may_advance_cpt_index(char_u *cpt)
+{
+ char_u *p = cpt;
+
+ while (*p == ',' || *p == ' ') // Skip delimiters
+ p++;
+ return (*p != NUL);
+}
+
+/*
* Return value of process_next_cpt_value()
*/
enum
@@ -4004,12 +4017,14 @@
ins_compl_next_state_T *st,
int *compl_type_arg,
pos_T *start_match_pos,
- int fuzzy_collect)
+ int fuzzy_collect,
+ int *advance_cpt_idx)
{
int compl_type = -1;
int status = INS_COMPL_CPT_OK;
st->found_all = FALSE;
+ *advance_cpt_idx = FALSE;
while (*st->e_cpt == ',' || *st->e_cpt == ' ')
st->e_cpt++;
@@ -4124,6 +4139,7 @@
// in any case e_cpt is advanced to the next entry
(void)copy_option_part(&st->e_cpt, IObuff, IOSIZE, ",");
+ *advance_cpt_idx = may_advance_cpt_index(st->e_cpt);
st->found_all = TRUE;
if (compl_type == -1)
@@ -4977,6 +4993,23 @@
}
/*
+ * Safely advance the cpt_sources_index by one.
+ */
+ static int
+advance_cpt_sources_index_safe(void)
+{
+ if (cpt_sources_index < cpt_sources_count - 1)
+ {
+ cpt_sources_index++;
+ return OK;
+ }
+#ifdef FEAT_EVAL
+ semsg(_(e_list_index_out_of_range_nr), cpt_sources_index + 1);
+#endif
+ return FAIL;
+}
+
+/*
* Get the next expansion(s), using "compl_pattern".
* The search starts at position "ini" in curbuf and in the direction
* compl_direction.
@@ -4993,6 +5026,7 @@
int i;
int found_new_match;
int type = ctrl_x_mode;
+ int may_advance_cpt_idx = FALSE;
if (!compl_started)
{
@@ -5027,7 +5061,8 @@
? &st.last_match_pos : &st.first_match_pos;
// For ^N/^P loop over all the flags/windows/buffers in 'complete'.
- for (cpt_sources_index = 0;;)
+ cpt_sources_index = 0;
+ for (;;)
{
found_new_match = FAIL;
st.set_match_pos = FALSE;
@@ -5038,13 +5073,15 @@
if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
&& (!compl_started || st.found_all))
{
- int status = process_next_cpt_value(&st, &type, ini, cfc_has_mode());
+ int status = process_next_cpt_value(&st, &type, ini,
+ cfc_has_mode(), &may_advance_cpt_idx);
if (status == INS_COMPL_CPT_END)
break;
if (status == INS_COMPL_CPT_CONT)
{
- cpt_sources_index++;
+ if (may_advance_cpt_idx && !advance_cpt_sources_index_safe())
+ break;
continue;
}
}
@@ -5057,8 +5094,8 @@
// get the next set of completion matches
found_new_match = get_next_completion_match(type, &st, ini);
- if (type > 0)
- cpt_sources_index++;
+ if (may_advance_cpt_idx && !advance_cpt_sources_index_safe())
+ break;
// break the loop for specialized modes (use 'complete' just for the
// generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new
@@ -6907,7 +6944,7 @@
strip_caret_numbers_in_place(cpt);
cpt_sources_index = 0;
- for (p = cpt; *p; cpt_sources_index++)
+ for (p = cpt; *p;)
{
while (*p == ',' || *p == ' ') // Skip delimiters
p++;
@@ -6926,7 +6963,9 @@
}
}
- copy_option_part(&p, IObuff, IOSIZE, ","); // Advance p
+ (void)copy_option_part(&p, IObuff, IOSIZE, ","); // Advance p
+ if (may_advance_cpt_index(p))
+ (void)advance_cpt_sources_index_safe();
}
cpt_sources_index = -1;