ash: expand: Fix multiple issues with EXP_DISCARD in evalvar

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>
This commit is contained in:
Denys Vlasenko 2020-02-22 19:38:40 +01:00
parent 823318822c
commit 1555895b4a

View File

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