changeset 19866:1136ec381dd2 v8.2.0489

patch 8.2.0489: Vim9: memory leaks Commit: https://github.com/vim/vim/commit/25b70c780a7e6063544e7f93c368fe403076f34e Author: Bram Moolenaar <Bram@vim.org> Date: Wed Apr 1 16:34:17 2020 +0200 patch 8.2.0489: Vim9: memory leaks Problem: Vim9: memory leaks. Solution: Free memory in the right place. Add hints for using asan.
author Bram Moolenaar <Bram@vim.org>
date Wed, 01 Apr 2020 16:45:05 +0200
parents cbe06eef8c71
children 10a4e7a33b76
files src/Makefile src/testdir/lsan-suppress.txt src/version.c src/vim9compile.c
diffstat 4 files changed, 39 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/src/Makefile
+++ b/src/Makefile
@@ -691,9 +691,12 @@ LINT_OPTIONS = -beprxzF
 
 
 # Uncomment one of the next two lines to compile Vim with the
-# address sanitizer or with the undefined sanitizer.  Works with gcc and
+# address sanitizer (asan) or with the undefined sanitizer.  Works with gcc and
 # clang.  May make Vim twice as slow.  Errors reported on stderr.
 # More at: https://code.google.com/p/address-sanitizer/
+# Useful environment variables:
+# $ export ASAN_OPTIONS="print_stacktrace=1 log_path=asan"
+# $ export LSAN_OPTIONS="suppressions=$cwd/testdir/lsan-suppress.txt"
 #SANITIZER_CFLAGS = -g -O0 -fsanitize=address -fno-omit-frame-pointer
 #SANITIZER_CFLAGS = -g -O0 -fsanitize=undefined -fno-omit-frame-pointer
 SANITIZER_LIBS = $(SANITIZER_CFLAGS)
--- a/src/testdir/lsan-suppress.txt
+++ b/src/testdir/lsan-suppress.txt
@@ -9,3 +9,4 @@ leak:libtinfo.so.5
 leak:libperl.so.*
 leak:libpython*.so.*
 leak:libruby*.so.*
+leak:libxcb*.so.*
--- a/src/version.c
+++ b/src/version.c
@@ -739,6 +739,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    489,
+/**/
     488,
 /**/
     487,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -3500,7 +3500,7 @@ compile_assignment(char_u *arg, exarg_T 
 	    if (cmdidx == CMD_const)
 	    {
 		emsg(_(e_const_option));
-		return NULL;
+		goto theend;
 	    }
 	    if (is_decl)
 	    {
@@ -3513,7 +3513,7 @@ compile_assignment(char_u *arg, exarg_T 
 	    {
 		// cannot happen?
 		emsg(_(e_letunexp));
-		return NULL;
+		goto theend;
 	    }
 	    cc = *p;
 	    *p = NUL;
@@ -3522,7 +3522,7 @@ compile_assignment(char_u *arg, exarg_T 
 	    if (opt_type == -3)
 	    {
 		semsg(_(e_unknown_option), arg);
-		return NULL;
+		goto theend;
 	    }
 	    if (opt_type == -2 || opt_type == 0)
 		type = &t_string;
@@ -3543,7 +3543,7 @@ compile_assignment(char_u *arg, exarg_T 
 	    if (!valid_yank_reg(arg[1], TRUE))
 	    {
 		emsg_invreg(arg[1]);
-		return FAIL;
+		goto theend;
 	    }
 	    dest = dest_reg;
 	    if (is_decl)
@@ -3994,6 +3994,23 @@ new_scope(cctx_T *cctx, scopetype_T type
 }
 
 /*
+ * Free the current scope and go back to the outer scope.
+ */
+    static void
+drop_scope(cctx_T *cctx)
+{
+    scope_T *scope = cctx->ctx_scope;
+
+    if (scope == NULL)
+    {
+	iemsg("calling drop_scope() without a scope");
+	return;
+    }
+    cctx->ctx_scope = scope->se_outer;
+    vim_free(scope);
+}
+
+/*
  * Evaluate an expression that is a constant:
  *  has(arg)
  *
@@ -4412,7 +4429,6 @@ compile_endif(char_u *arg, cctx_T *cctx)
 	return NULL;
     }
     ifscope = &scope->se_u.se_if;
-    cctx->ctx_scope = scope->se_outer;
     unwind_locals(cctx, scope->se_local_count);
 
     if (scope->se_u.se_if.is_if_label >= 0)
@@ -4425,7 +4441,7 @@ compile_endif(char_u *arg, cctx_T *cctx)
     compile_fill_jump_to_end(&ifscope->is_end_label, cctx);
     cctx->ctx_skip = FALSE;
 
-    vim_free(scope);
+    drop_scope(cctx);
     return arg;
 }
 
@@ -4486,25 +4502,35 @@ compile_for(char_u *arg, cctx_T *cctx)
     // Reserve a variable to store the loop iteration counter.
     loop_idx = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
     if (loop_idx < 0)
+    {
+	drop_scope(cctx);
 	return NULL;
+    }
 
     // Reserve a variable to store "var"
     var_idx = reserve_local(cctx, arg, varlen, FALSE, &t_any);
     if (var_idx < 0)
+    {
+	drop_scope(cctx);
 	return NULL;
+    }
 
     generate_STORENR(cctx, loop_idx, -1);
 
     // compile "expr", it remains on the stack until "endfor"
     arg = p;
     if (compile_expr1(&arg, cctx) == FAIL)
+    {
+	drop_scope(cctx);
 	return NULL;
+    }
 
     // now we know the type of "var"
     vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
     if (vartype->tt_type != VAR_LIST)
     {
 	emsg(_("E1024: need a List to iterate over"));
+	drop_scope(cctx);
 	return NULL;
     }
     if (vartype->tt_member->tt_type != VAR_UNKNOWN)