bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

PATCH: make_var_array() mishandles exportstr on CYGWIN and SuSE


From: Wolfgang Oertl
Subject: PATCH: make_var_array() mishandles exportstr on CYGWIN and SuSE
Date: Sat, 18 Nov 2000 11:06:19 +0100

Configuration Information [Automatically generated, do not change]:
Machine: i386
OS: linux
Compiler: gcc
Compilation CFLAGS:  -DPROGRAM='bash' -DCONF_HOSTTYPE='i386' 
-DCONF_OSTYPE='linux' -DCONF_MACHTYPE='i386-suse-linux' -DCONF_VENDOR='suse' 
-DSHELL -DHAVE_CONFIG_H  -D_FILE_OFFSET_BITS=64  -I. -I/usr/include -I. 
-I./include -I./lib -I/usr/include -O2 -m486 -D_GNU_SOURCE -Wall -pipe
uname output: Linux laptop 2.4.0-test10 #24 Tue Nov 7 22:56:09 CET 2000 i686 
unknown
Machine Type: i386-suse-linux

Bash Version: 2.04
Patch Level: 0
Release Status: release

Description:
        In the vanilla version this problem only arises on __CYGWIN__ and
        __CYGWIN32__ platforms.  The Bash distributed with SuSE 7.0 contains a
        patch that triggers this problem by enabling the __CYGWIN__ code in
        this function.

        During generation of 'export_env', function variables that have their
        'exportstr' set, lose.  The problem is localized to the function
        variables.c:make_var_array().

        Consider this code fragment of variables.c:make_var_array():

>       if (var->exportstr)
>         {
> #if defined(__CYGWIN__) || defined (__CYGWIN32__)
>         INVALIDATE_EXPORTSTR (var);
>         value = value_cell (var);
> #else
>         /* XXX -- this test can go away in the next release, to be replaced
>            by a simple `value = var->exportstr;', when the exportstr code
>            is better-tested.  Until then, don't do it for cygwin at all,
>            since that system has some weird environment variables. */
>         if (valid_exportstr (var))
>           value = var->exportstr;
>         else
>           {
>             INVALIDATE_EXPORTSTR (var);
>             value = value_cell (var);
>           }
> #endif
>         }

        There are some ways for 'value = value_cell (var)' to happen, which is
        bad when 'var' is a function.  Actually, 'exportstr' is set only for
        functions!

Repeat-By:
        $ function foo_func () { echo here; }
        $ export -f foo_func
        $ /bin/ls
        $ export BAR=1
        $ sh
        $ set

        /bin/ls (or whatever external command) provokes a call of
        'maybe_make_export_env', which makes 'export_env', thereby setting the
        'exportstr' of 'foo_func'.

        The second time (provoked by changing an exported variable, BAR in
        this case, and then another external command), the function
        'make_var_array' behaves badly and uses 'value_cell (var)' on the
        function variable!  Only 'function_cell (var)' must be used...  This
        results in a Bad String.

        In set's output in the subshell, the 'foo_func' is listed as a string
        variable with a content of "\t" (which is, in my setup, the result of
        using a pointer to COMMAND (4 bytes) as a string).

Fix:
        A quick fix would be to replace the instances of 'value_cell (var)' in
        the code fragment above with 'named_function_string ((char *)NULL,
        function_cell (var), 0)'.

        This would break as soon as arrays can be exported, though.  This
        patch (for the unpatched version of Bash) should yield better
        results in the long run.  It still leaves the call to
        'valid_exportstr' intact, which (IMHO) could be removed.
 
--- variables.c.orig    Thu Nov 16 00:01:30 2000
+++ variables.c Thu Nov 16 16:55:45 2000
@@ -2492,30 +2492,33 @@
 
   list = alloc_array ((1 + array_len ((char **)vars)));
 
-#define USE_EXPORTSTR (value == var->exportstr)
-
   for (i = 0, list_index = 0; (var = vars[i]); i++)
     {
+      /* When var->exportstr is set, assume that it is up to date.  All
+       * changes to variables must therefore call INVALIDATE_EXPORTSTR.
+       */
       if (var->exportstr)
-        {
+       {
 #if defined(__CYGWIN__) || defined (__CYGWIN32__)
          INVALIDATE_EXPORTSTR (var);
-         value = value_cell (var);
 #else
-         /* XXX -- this test can go away in the next release, to be replaced
-            by a simple `value = var->exportstr;', when the exportstr code
+         /* XXX -- this test can go away in the next release
+            when the exportstr code
             is better-tested.  Until then, don't do it for cygwin at all,
             since that system has some weird environment variables. */
           if (valid_exportstr (var))
-           value = var->exportstr;
-         else
            {
-             INVALIDATE_EXPORTSTR (var);
-             value = value_cell (var);
+             /* Gee, I'd like to get away with not using savestring() if we're
+                using the cached exportstr... */
+             list[list_index++] = savestring (var->exportstr);
+             continue;
            }
+         INVALIDATE_EXPORTSTR (var);
 #endif
-        }
-      else if (function_p (var))
+       }
+
+      /* otherwise, get the current value */
+      if (function_p (var))
        value = named_function_string ((char *)NULL, function_cell (var), 0);
 #if defined (ARRAY_VARS)
       else if (array_p (var))
@@ -2530,17 +2533,18 @@
 
       if (value)
        {
-         /* Gee, I'd like to get away with not using savestring() if we're
-            using the cached exportstr... */
-         list[list_index] = USE_EXPORTSTR ? savestring (value)
-                                          : mk_env_string (var->name, value);
+         list[list_index] = mk_env_string (var->name, value);
 
-         if (USE_EXPORTSTR == 0 && function_p (var))
+         /* This place is reached only if var->exportstr is NULL */
+         if (function_p (var)
+#if defined (ARRAY_VARS) && 0
+                 || array_p (var)
+#endif
+                 )
            {
              SAVE_EXPORTSTR (var, list[list_index]);
            }
          list_index++;
-#undef USE_EXPORTSTR
 
 #if 0  /* not yet */
 #if defined (ARRAY_VARS)
        


Details
=======

Of course the same happens when a shell script defines and exports functions,
and then calls other scripts (that was how I noticed it).


The first time 'maybe_make_export_env' is called (with array_needs_making
set), the shell variable for 'func' looks like this (in gdb):

$41 = {name = 0x80d53ac "func", value = 0x80d684c "\t", exportstr = 0x0, 
dynamic_value = 0,
  assign_func = 0, attributes = 33, context = 0, prev_context = 0x0}

The second time:

$43 = {name = 0x80d53ac "func", value = 0x80d684c "\t",
  exportstr = 0x80d694c "func=() {  echo \"here is the function\"\n}", 
dynamic_value = 0,
  assign_func = 0, attributes = 33, context = 0, prev_context = 0x0}


As you can see, the only difference is that 'exportstr' is already computed.

The function make_var_array() looks like this in my installed Bash.
(It includes a patch at this point, enabling the #if defined __CYGWIN__ part,
by andreas.schwab@suse.de, dated 2000/03/27.  Without the patch the problem
wouldn't arise, and INVALIDATE_EXPORTSTR wouldn't be called.)

> #define USE_EXPORTSTR (value == var->exportstr)
>  
>   for (i = 0, list_index = 0; (var = vars[i]); i++)
>     {
>       if (var->exportstr)
>         {
>           INVALIDATE_EXPORTSTR (var);
>           value = value_cell (var);
>         }
>       else if (function_p (var))
>         value = named_function_string ((char *)NULL, function_cell (var), 0);
> #if defined (ARRAY_VARS)
>       else if (array_p (var))
> #  if 0
>         value = array_to_assignment_string (array_cell (var));
> #  else
>         continue;       /* XXX array vars cannot yet be exported */
> #  endif
> #endif
>       else
>         value = value_cell (var);

exportstr is used only for functions: the only time SAVE_EXPORTSTR is called
is in this very function, enclosed by an if(function_p()) predicate.

What follows is that when var->exportstr is set (only for functions), you end
up with 'value = value_cell (var)', which is *not* what you want...  This in
fact uses the pointer to a COMMAND structure as a string.

end




reply via email to

[Prev in Thread] Current Thread [Next in Thread]