convert_var_to_{array,assoc} can get called with a non-scalar VAR
argument, in which case the old variable's hash pointer is assigned to
element 0 of the new array, and its storage never gets freed properly.
---
 arrayfunc.c | 8 ++++----
 variables.c | 3 +--
 variables.h | 1 +
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/arrayfunc.c b/arrayfunc.c
index db70eb0c..aac4dfec 100644
--- a/arrayfunc.c
+++ b/arrayfunc.c
@@ -78,10 +78,10 @@ convert_var_to_array (SHELL_VAR *var)
 
   oldval = value_cell (var);
   array = array_create ();
-  if (oldval)
+  if (oldval && array_p (var) == 0 && assoc_p (var) == 0)
     array_insert (array, 0, oldval);
 
-  FREE (value_cell (var));
+  dispose_variable_value (var);
   var_setarray (var, array);
 
   /* these aren't valid anymore */
@@ -120,10 +120,10 @@ convert_var_to_assoc (SHELL_VAR *var)
 
   oldval = value_cell (var);
   hash = assoc_create (0);
-  if (oldval)
+  if (oldval && array_p (var) == 0 && assoc_p (var) == 0)
     assoc_insert (hash, savestring ("0"), oldval);
 
-  FREE (value_cell (var));
+  dispose_variable_value (var);
   var_setassoc (var, hash);
 
   /* these aren't valid anymore */
diff --git a/variables.c b/variables.c
index 1d316a1e..6b91a997 100644
--- a/variables.c
+++ b/variables.c
@@ -287,7 +287,6 @@ static SHELL_VAR *bind_variable_internal (const char *, 
const char *, HASH_TABLE
 static void init_variable (SHELL_VAR *);
 static void init_shell_variable (SHELL_VAR *);
 
-static void dispose_variable_value (SHELL_VAR *);
 static void free_variable_hash_data (PTR_T);
 
 static VARLIST *vlist_alloc (size_t);
@@ -3744,7 +3743,7 @@ copy_variable (SHELL_VAR *var)
 /* **************************************************************** */
 
 /* Dispose of the information attached to VAR. */
-static void
+void
 dispose_variable_value (SHELL_VAR *var)
 {
   if (nofree_p (var))
diff --git a/variables.h b/variables.h
index 5e870986..f3c0b971 100644
--- a/variables.h
+++ b/variables.h
@@ -388,6 +388,7 @@ extern void push_source (ARRAY *, char *);
 
 extern void adjust_shell_level (int);
 extern void non_unsettable (char *);
+extern void dispose_variable_value (SHELL_VAR *);
 extern void dispose_variable (SHELL_VAR *);
 extern void dispose_used_env_vars (void);
 extern void dispose_function_env (void);
-- 
2.54.0


Reply via email to