ash: [VAR] Fix loss of variables when hash collides

Upstream commit:

    Date: Tue, 6 Jul 2010 17:40:53 +0800
    [VAR] Fix loss of variables when hash collides

    Brian Koropoff reported that the new var patches broke the following
    script:

    #!/bin/dash
    GDM_LANG="bar"
    OPTION="foo"
    unset GDM_LANG
    # OPTION has mysteriously become unset
    echo "$OPTION"

    He correctly diagnosed this as a result of removing all variables
    in the hash chain preceding the one that should be removed in
    setvareq.

    He also provided a patch to fix this.

    This patch is based on his but without keeping the original vpp.
    As a result, we now store new variables at the end of the hash
    chain instead of the beginning.

    To make this work, setvareq/setvar now returns the vp pointer
    modified.  In case they're used to unset a variable the pointer
    returned is undefined.  This is because mklocal needs it and
    used to get it by assuming that the new variable always appear
    at the beginning of the chain.

    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 2017-07-26 20:06:48 +02:00
parent 85241c7b0b
commit d04fc712e3

View File

@ -2237,14 +2237,15 @@ bltinlookup(const char *name)
* will go away. * will go away.
* Called with interrupts off. * Called with interrupts off.
*/ */
static void static struct var *
setvareq(char *s, int flags) setvareq(char *s, int flags)
{ {
struct var *vp, **vpp; struct var *vp, **vpp;
vpp = hashvar(s); vpp = hashvar(s);
flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
vp = *findvar(vpp, s); vpp = findvar(vpp, s);
vp = *vpp;
if (vp) { if (vp) {
if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) { if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
const char *n; const char *n;
@ -2257,7 +2258,7 @@ setvareq(char *s, int flags)
} }
if (flags & VNOSET) if (flags & VNOSET)
return; goto out;
if (vp->var_func && !(flags & VNOFUNC)) if (vp->var_func && !(flags & VNOFUNC))
vp->var_func(var_end(s)); vp->var_func(var_end(s));
@ -2271,14 +2272,14 @@ setvareq(char *s, int flags)
out_free: out_free:
if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE) if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
free(s); free(s);
return; goto out;
} }
flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
} else { } else {
/* variable s is not found */ /* variable s is not found */
if (flags & VNOSET) if (flags & VNOSET)
return; goto out;
if ((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET) if ((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
goto out_free; goto out_free;
vp = ckzalloc(sizeof(*vp)); vp = ckzalloc(sizeof(*vp));
@ -2290,13 +2291,16 @@ setvareq(char *s, int flags)
s = ckstrdup(s); s = ckstrdup(s);
vp->var_text = s; vp->var_text = s;
vp->flags = flags; vp->flags = flags;
out:
return vp;
} }
/* /*
* Set the value of a variable. The flags argument is ored with the * Set the value of a variable. The flags argument is ored with the
* flags of the variable. If val is NULL, the variable is unset. * flags of the variable. If val is NULL, the variable is unset.
*/ */
static void static struct var *
setvar(const char *name, const char *val, int flags) setvar(const char *name, const char *val, int flags)
{ {
const char *q; const char *q;
@ -2304,6 +2308,7 @@ setvar(const char *name, const char *val, int flags)
char *nameeq; char *nameeq;
size_t namelen; size_t namelen;
size_t vallen; size_t vallen;
struct var *vp;
q = endofname(name); q = endofname(name);
p = strchrnul(q, '='); p = strchrnul(q, '=');
@ -2325,8 +2330,10 @@ setvar(const char *name, const char *val, int flags)
p = mempcpy(p, val, vallen); p = mempcpy(p, val, vallen);
} }
*p = '\0'; *p = '\0';
setvareq(nameeq, flags | VNOSAVE); vp = setvareq(nameeq, flags | VNOSAVE);
INT_ON; INT_ON;
return vp;
} }
static void FAST_FUNC static void FAST_FUNC
@ -9336,10 +9343,9 @@ mklocal(char *name)
if (vp == NULL) { if (vp == NULL) {
/* variable did not exist yet */ /* variable did not exist yet */
if (eq) if (eq)
setvareq(name, VSTRFIXED); vp = setvareq(name, VSTRFIXED);
else else
setvar(name, NULL, VSTRFIXED); vp = setvar(name, NULL, VSTRFIXED);
vp = *vpp; /* the new variable */
lvp->flags = VUNSET; lvp->flags = VUNSET;
} else { } else {
lvp->text = vp->var_text; lvp->text = vp->var_text;