CVE-2022-2982: Use After Free (CWE-416)
value: Use After Free in GitHub repository vim/vim prior to 9.0.0260.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 7.8
Published: 2022-08-25T20:15Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/quickfix.c, src/testdir/test_quickfix.vim, src/version.c
Commit date: 2022-08-24 20:07:22 +0100
Commit message: patch 9.0.0260: using freed memory when using 'quickfixtextfunc' recursively

Problem:    Using freed memory when using 'quickfixtextfunc' recursively.
Solution:   Do not allow for recursion.

Diff: diff --git a/src/quickfix.c b/src/quickfix.c
index 54ae07df53d4..6af62e8dfe56 100644
--- a/src/quickfix.c
+++ b/src/quickfix.c
@@ -4672,10 +4672,15 @@ qf_buf_add_line(
     static list_T *
 call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long end_idx)
 {
     callback_T	*cb = &qftf_cb;
     list_T	*qftf_list = NULL;
+    static int	recursive = FALSE;
+
+    if (recursive)
+	return NULL;  // this doesn't work properly recursively
+    recursive = TRUE;
 
     // If 'quickfixtextfunc' is set, then use the user-supplied function to get
     // the text to display. Use the local value of 'quickfixtextfunc' if it is
     // set.
     if (qfl->qf_qftf_cb.cb_name != NULL)
@@ -4686,11 +4691,14 @@ call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long end_idx)
 	dict_T		*d;
 	typval_T	rettv;
 
 	// create the dict argument
 	if ((d = dict_alloc_lock(VAR_FIXED)) == NULL)
+	{
+	    recursive = FALSE;
 	    return NULL;
+	}
 	dict_add_number(d, "quickfix", (long)IS_QF_LIST(qfl));
 	dict_add_number(d, "winid", (long)qf_winid);
 	dict_add_number(d, "id", (long)qfl->qf_id);
 	dict_add_number(d, "start_idx", start_idx);
 	dict_add_number(d, "end_idx", end_idx);
@@ -4709,10 +4717,11 @@ call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long end_idx)
 	    clear_tv(&rettv);
 	}
 	dict_unref(d);
     }
 
+    recursive = FALSE;
     return qftf_list;
 }
 
 /*
  * Fill current buffer with quickfix errors, replacing any previous contents.
diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim
index 94651af81942..762fa8d8d0e3 100644
--- a/src/testdir/test_quickfix.vim
+++ b/src/testdir/test_quickfix.vim
@@ -6349,6 +6349,19 @@ func Test_qflist_statusmsg()
     au!
   augroup END
   %bw!
 endfunc
 
+func Test_quickfixtextfunc_recursive()
+  func s:QFTfunc(o)
+    cgete '0'
+  endfunc
+  copen
+  let &quickfixtextfunc = 's:QFTfunc'
+  cex ""
+
+  let &quickfixtextfunc = ''
+  cclose
+endfunc
+
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index b1943bb16157..02c20f03f602 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,10 +729,12 @@ static char *(features[]) =
 	NULL
 };
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    260,
 /**/
     259,
 /**/
     258,
 /**/

Fixed code:     static int	recursive = FALSE;

    if (recursive)
	return NULL;  // this doesn't work properly recursively
    recursive = TRUE;
	{
	    recursive = FALSE;
	}
    recursive = FALSE;
func Test_quickfixtextfunc_recursive()
  func s:QFTfunc(o)
    cgete '0'
  endfunc
  copen
  let &quickfixtextfunc = 's:QFTfunc'
  cex ""

  let &quickfixtextfunc = ''
  cclose
endfunc


/**/
    260,