summaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorDenys Vlasenko2017-07-26 20:06:48 +0200
committerDenys Vlasenko2017-07-26 20:33:51 +0200
commitd04fc712e30b681117afaad490fec2a747b390c6 (patch)
tree6b60ab1de6c02dcb1d85bfd49923443a0a25da7c /shell
parent85241c7b0b4558f405f7e633ad62520299b8396d (diff)
downloadbusybox-d04fc712e30b681117afaad490fec2a747b390c6.zip
busybox-d04fc712e30b681117afaad490fec2a747b390c6.tar.gz
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>
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c26
1 files changed, 16 insertions, 10 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 3604af4..5bb5935 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -2237,14 +2237,15 @@ bltinlookup(const char *name)
* will go away.
* Called with interrupts off.
*/
-static void
+static struct var *
setvareq(char *s, int flags)
{
struct var *vp, **vpp;
vpp = hashvar(s);
flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
- vp = *findvar(vpp, s);
+ vpp = findvar(vpp, s);
+ vp = *vpp;
if (vp) {
if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
const char *n;
@@ -2257,7 +2258,7 @@ setvareq(char *s, int flags)
}
if (flags & VNOSET)
- return;
+ goto out;
if (vp->var_func && !(flags & VNOFUNC))
vp->var_func(var_end(s));
@@ -2271,14 +2272,14 @@ setvareq(char *s, int flags)
out_free:
if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
free(s);
- return;
+ goto out;
}
flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
} else {
/* variable s is not found */
if (flags & VNOSET)
- return;
+ goto out;
if ((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
goto out_free;
vp = ckzalloc(sizeof(*vp));
@@ -2290,13 +2291,16 @@ setvareq(char *s, int flags)
s = ckstrdup(s);
vp->var_text = s;
vp->flags = flags;
+
+ out:
+ return vp;
}
/*
* 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.
*/
-static void
+static struct var *
setvar(const char *name, const char *val, int flags)
{
const char *q;
@@ -2304,6 +2308,7 @@ setvar(const char *name, const char *val, int flags)
char *nameeq;
size_t namelen;
size_t vallen;
+ struct var *vp;
q = endofname(name);
p = strchrnul(q, '=');
@@ -2325,8 +2330,10 @@ setvar(const char *name, const char *val, int flags)
p = mempcpy(p, val, vallen);
}
*p = '\0';
- setvareq(nameeq, flags | VNOSAVE);
+ vp = setvareq(nameeq, flags | VNOSAVE);
INT_ON;
+
+ return vp;
}
static void FAST_FUNC
@@ -9336,10 +9343,9 @@ mklocal(char *name)
if (vp == NULL) {
/* variable did not exist yet */
if (eq)
- setvareq(name, VSTRFIXED);
+ vp = setvareq(name, VSTRFIXED);
else
- setvar(name, NULL, VSTRFIXED);
- vp = *vpp; /* the new variable */
+ vp = setvar(name, NULL, VSTRFIXED);
lvp->flags = VUNSET;
} else {
lvp->text = vp->var_text;