diff src/userfunc.c @ 26747:a8a4e1e7b111 v8.2.3902

patch 8.2.3902: Vim9: double free with nested :def function Commit: https://github.com/vim/vim/commit/9c23f9bb5fe435b28245ba8ac65aa0ca6b902c04 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Dec 26 14:23:22 2021 +0000 patch 8.2.3902: Vim9: double free with nested :def function Problem: Vim9: double free with nested :def function. Solution: Pass "line_to_free" from compile_def_function() and make sure cmdlinep is valid.
author Bram Moolenaar <Bram@vim.org>
date Sun, 26 Dec 2021 15:30:02 +0100
parents 9c9b8d95b05f
children 3a2b222107a6
line wrap: on
line diff
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -720,12 +720,14 @@ get_function_body(
 	}
 	else
 	{
-	    vim_free(*line_to_free);
 	    if (eap->getline == NULL)
 		theline = getcmdline(':', 0L, indent, getline_options);
 	    else
 		theline = eap->getline(':', eap->cookie, indent,
 							      getline_options);
+	    if (*eap->cmdlinep == *line_to_free)
+		*eap->cmdlinep = theline;
+	    vim_free(*line_to_free);
 	    *line_to_free = theline;
 	}
 	if (KeyTyped)
@@ -837,7 +839,8 @@ get_function_body(
 			// we can simply point into it, otherwise we need to
 			// change "eap->cmdlinep".
 			eap->nextcmd = nextcmd;
-			if (*line_to_free != NULL)
+			if (*line_to_free != NULL
+					    && *eap->cmdlinep != *line_to_free)
 			{
 			    vim_free(*eap->cmdlinep);
 			    *eap->cmdlinep = *line_to_free;
@@ -1161,7 +1164,7 @@ lambda_function_body(
 	}
 	if (ga_grow(gap, 1) == FAIL || ga_grow(freegap, 1) == FAIL)
 	    goto erret;
-	if (cmdline != NULL)
+	if (eap.nextcmd != NULL)
 	    // more is following after the "}", which was skipped
 	    last = cmdline;
 	else
@@ -1175,7 +1178,7 @@ lambda_function_body(
 	((char_u **)freegap->ga_data)[freegap->ga_len++] = pnl;
     }
 
-    if (cmdline != NULL)
+    if (eap.nextcmd != NULL)
     {
 	garray_T *tfgap = &evalarg->eval_tofree_ga;
 
@@ -1187,6 +1190,8 @@ lambda_function_body(
 	{
 	    ((char_u **)(tfgap->ga_data))[tfgap->ga_len++] = cmdline;
 	    evalarg->eval_using_cmdline = TRUE;
+	    if (cmdline == line_to_free)
+		line_to_free = NULL;
 	}
     }
     else
@@ -3988,9 +3993,8 @@ list_functions(regmatch_T *regmatch)
  * Returns a pointer to the function or NULL if no function defined.
  */
     ufunc_T *
-define_function(exarg_T *eap, char_u *name_arg)
+define_function(exarg_T *eap, char_u *name_arg, char_u **line_to_free)
 {
-    char_u	*line_to_free = NULL;
     int		j;
     int		c;
     int		saved_did_emsg;
@@ -4258,7 +4262,7 @@ define_function(exarg_T *eap, char_u *na
     if (get_function_args(&p, ')', &newargs,
 			eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
 			 NULL, &varargs, &default_args, eap->skip,
-			 eap, &line_to_free) == FAIL)
+			 eap, line_to_free) == FAIL)
 	goto errret_2;
     whitep = p;
 
@@ -4368,7 +4372,7 @@ define_function(exarg_T *eap, char_u *na
 
     // Do not define the function when getting the body fails and when
     // skipping.
-    if (get_function_body(eap, &newlines, line_arg, &line_to_free) == FAIL
+    if (get_function_body(eap, &newlines, line_arg, line_to_free) == FAIL
 	    || eap->skip)
 	goto erret;
 
@@ -4660,7 +4664,6 @@ errret_2:
     }
 ret_free:
     ga_clear_strings(&argtypes);
-    vim_free(line_to_free);
     vim_free(fudi.fd_newkey);
     if (name != name_arg)
 	vim_free(name);
@@ -4676,7 +4679,10 @@ ret_free:
     void
 ex_function(exarg_T *eap)
 {
-    (void)define_function(eap, NULL);
+    char_u *line_to_free = NULL;
+
+    (void)define_function(eap, NULL, &line_to_free);
+    vim_free(line_to_free);
 }
 
 /*