CVE-2022-2862: Use After Free (CWE-416)
value: Use After Free in GitHub repository vim/vim prior to 9.0.0221.
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-17T20:15Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/testdir/test_vim9_func.vim, src/version.c, src/vim9compile.c
Commit date: 2022-08-16 19:34:44 +0100
Commit message: patch 9.0.0221: accessing freed memory if compiling nested function fails

Problem:    Accessing freed memory if compiling nested function fails.
Solution:   Mess up the variable name so that it won't be found.

Diff: diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index 5c7545784f3d..fd7182d50e08 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -909,10 +909,22 @@ def Test_nested_function()
       defcompile
   END
   v9.CheckScriptFailure(lines, 'E1173: Text found after enddef: burp', 3)
 enddef
 
+def Test_nested_function_fails()
+  var lines =<< trim END
+      def T()
+        def Func(g: string):string
+        enddef
+        Func()
+      enddef
+      silent! defcompile
+  END
+  v9.CheckScriptFailure(lines, 'E1069:')
+enddef
+
 def Test_not_nested_function()
   echo printf('%d',
       function('len')('xxx'))
 enddef
 
diff --git a/src/version.c b/src/version.c
index a6293e0cfa16..03d641fd77f7 100644
--- a/src/version.c
+++ b/src/version.c
@@ -733,10 +733,12 @@ static char *(features[]) =
 	NULL
 };
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    221,
 /**/
     220,
 /**/
     219,
 /**/
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 98fc84c58a4c..d1e2c87fd82c 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -828,10 +828,11 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
     char_u	*lambda_name;
     ufunc_T	*ufunc;
     int		r = FAIL;
     compiletype_T   compile_type;
     isn_T	*funcref_isn = NULL;
+    lvar_T	*lvar = NULL;
 
     if (eap->forceit)
     {
 	emsg(_(e_cannot_use_bang_with_nested_def));
 	return NULL;
@@ -934,13 +935,12 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
 	lambda_name = NULL;
     }
     else
     {
 	// Define a local variable for the function reference.
-	lvar_T	*lvar = reserve_local(cctx, func_name, name_end - name_start,
+	lvar = reserve_local(cctx, func_name, name_end - name_start,
 						    TRUE, ufunc->uf_func_type);
-
 	if (lvar == NULL)
 	    goto theend;
 	if (generate_FUNCREF(cctx, ufunc, &funcref_isn) == FAIL)
 	    goto theend;
 	r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
@@ -955,10 +955,13 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
 #endif
     if (func_needs_compiling(ufunc, compile_type)
 	    && compile_def_function(ufunc, TRUE, compile_type, cctx) == FAIL)
     {
 	func_ptr_unref(ufunc);
+	if (lvar != NULL)
+	    // Now the local variable can't be used.
+	    *lvar->lv_name = '/';  // impossible value
 	goto theend;
     }
 
 #ifdef FEAT_PROFILE
     // When the outer function is compiled for profiling, the nested function

Vulnerable code: 	lvar_T	*lvar = reserve_local(cctx, func_name, name_end - name_start,

Fixed code: def Test_nested_function_fails()
  var lines =<< trim END
      def T()
        def Func(g: string):string
        enddef
        Func()
      enddef
      silent! defcompile
  END
  v9.CheckScriptFailure(lines, 'E1069:')
enddef

/**/
    221,
    lvar_T	*lvar = NULL;
	lvar = reserve_local(cctx, func_name, name_end - name_start,
	if (lvar != NULL)
	    // Now the local variable can't be used.
	    *lvar->lv_name = '/';  // impossible value