ash: [VAR] Fix poplocalvar on abnormal exit from function
authorDenys Vlasenko <vda.linux@googlemail.com>
Wed, 26 Jul 2017 17:55:31 +0000 (19:55 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Wed, 26 Jul 2017 18:33:51 +0000 (20:33 +0200)
Upstream commit:

    Date: Thu, 27 May 2010 11:32:55 +0800
    [VAR] Fix poplocalvar on abnormal exit from function

    The new localvar code broke the abnormal exit from functions
    and built-ins by not restoring the original localvar state.

    This patch fixes this by storing the previous localvar state so
    that we always unwind correctly in case of an abnormal exit.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash.c

index e900a425fa8301b46e5c2b3453bfb807c15aa374..f7fc18f0d85e31bb6400fe86e32ef01ff5603b74 100644 (file)
@@ -9228,7 +9228,7 @@ poplocalvars(int keep)
 /*
  * Create a new localvar environment.
  */
-static void
+static struct localvar_list *
 pushlocalvars(void)
 {
        struct localvar_list *ll;
@@ -9239,6 +9239,15 @@ pushlocalvars(void)
        ll->next = localvar_stack;
        localvar_stack = ll;
        INT_ON;
+
+       return ll->next;
+}
+
+static void
+unwindlocalvars(struct localvar_list *stop)
+{
+       while (localvar_stack != stop)
+               poplocalvars(0);
 }
 
 static int
@@ -9619,6 +9628,7 @@ evalcommand(union node *cmd, int flags)
        static const struct builtincmd null_bltin = {
                "\0\0", bltincmd /* why three NULs? */
        };
+       struct localvar_list *localvar_stop;
        struct stackmark smark;
        union node *argp;
        struct arglist arglist;
@@ -9640,7 +9650,7 @@ evalcommand(union node *cmd, int flags)
        /* First expand the arguments. */
        TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
        setstackmark(&smark);
-       pushlocalvars();
+       localvar_stop = pushlocalvars();
        back_exitstatus = 0;
 
        cmdentry.cmdtype = CMDBUILTIN;
@@ -9827,7 +9837,6 @@ evalcommand(union node *cmd, int flags)
                                /* parent */
                                status = waitforjob(jp);
                                INT_ON;
-                               poplocalvars(0);
                                TRACE(("forked child exited with %d\n", status));
                                break;
                        }
@@ -9874,6 +9883,7 @@ evalcommand(union node *cmd, int flags)
  out:
        if (cmd->ncmd.redirect)
                popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
+       unwindlocalvars(localvar_stop);
        if (lastarg) {
                /* dsl: I think this is intended to be used to support
                 * '_' in 'vi' command mode during line editing...
@@ -13607,8 +13617,7 @@ reset(void)
                popredir(/*drop:*/ 0, /*restore:*/ 0);
 
        /* from var.c: */
-       while (localvar_stack)
-               poplocalvars(0);
+       unwindlocalvars(NULL);
 }
 
 #if PROFILE