ash: expand: Fix multiple issues with EXP_DISCARD in evalvar
authorDenys Vlasenko <vda.linux@googlemail.com>
Sat, 22 Feb 2020 18:38:40 +0000 (19:38 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 24 Feb 2020 18:20:51 +0000 (19:20 +0100)
Upstream commit:

    Date: Wed, 12 Sep 2018 14:27:16 +0800
    expand: Fix multiple issues with EXP_DISCARD in evalvar

    The commit 3cd538634f71538370f5af239f342aec48b7470b broke parameter
    expansion in multiple ways because the EXP_DISCARD flag wasn't set
    or tested for various cases:

            $ src/dash -c 'var=; echo ${var:+nonempty}'
            nonempty
            $ src/dash -u -c 'unset foo bar; echo ${foo+${bar}}'
            dash: 1: bar: parameter not set
            $ src/dash -c 'foo=bar; echo ${foo=BUG}; echo $foo'
            barBUG
            bar
            $

    This patch fixes them by introducing a new discard variable that
    tracks whether the extra word should be discarded or not when it
    is parsed.

Reported-by: Martijn Dekker <martijn@inlv.org>
    Fixes: 3cd538634f71 ("expand: Do not reprocess data when...")
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Reported-by: Martijn Dekker <martijn@inlv.org>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash.c

index 425da6bb6b5d97df1b742ffba0f08936c8539b0e..d0d99f60e905345afa693ee1e531013ddfa96ceb 100644 (file)
@@ -7454,6 +7454,7 @@ evalvar(char *p, int flag)
        int patloc;
        int startloc;
        ssize_t varlen;
+       int discard;
        int quoted;
 
        varflags = (unsigned char) *p++;
@@ -7469,33 +7470,31 @@ evalvar(char *p, int flag)
        if (varflags & VSNUL)
                varlen--;
 
+       discard = varlen < 0 ? EXP_DISCARD : 0;
+
        switch (subtype) {
        case VSPLUS:
-               varlen = -1 - varlen;
+               discard ^= EXP_DISCARD;
                /* fall through */
        case 0:
        case VSMINUS:
-               p = argstr(p, flag | EXP_TILDE | EXP_WORD);
-               if (varlen < 0)
-                       return p;
+               p = argstr(p, flag | EXP_TILDE | EXP_WORD | (discard ^ EXP_DISCARD));
                goto record;
 
        case VSASSIGN:
        case VSQUESTION:
-               if (varlen >= 0)
-                       goto record;
-
                p = subevalvar(p, var, 0, startloc, varflags,
-                          flag & ~QUOTES_ESC);
+                       (flag & ~QUOTES_ESC) | (discard ^ EXP_DISCARD));
 
-               if (flag & EXP_DISCARD)
-                       return p;
+               if ((flag | ~discard) & EXP_DISCARD)
+                       goto record;
 
                varflags &= ~VSNUL;
+               subtype = VSNORMAL;
                goto again;
        }
 
-       if (varlen < 0 && uflag)
+       if ((discard & ~flag) && uflag)
                varunset(p, var, 0, 0);
 
        if (subtype == VSLENGTH) {
@@ -7503,7 +7502,7 @@ evalvar(char *p, int flag)
                if (flag & EXP_DISCARD)
                        return p;
                cvtnum(varlen > 0 ? varlen : 0, flag);
-               goto record;
+               goto really_record;
        }
 
        if (subtype == VSNORMAL)
@@ -7528,7 +7527,7 @@ evalvar(char *p, int flag)
        }
 #endif
 
-       flag |= varlen < 0 ? EXP_DISCARD : 0;
+       flag |= discard;
        if (!(flag & EXP_DISCARD)) {
                /*
                 * Terminate the string and start recording the pattern
@@ -7541,9 +7540,10 @@ evalvar(char *p, int flag)
        p = subevalvar(p, NULL, patloc, startloc, varflags, flag);
 
  record:
-       if (flag & EXP_DISCARD)
+       if ((flag | discard) & EXP_DISCARD)
                return p;
 
+ really_record:
        if (quoted) {
                quoted = *var == '@' && shellparam.nparam;
                if (!quoted)