summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTony Cook <tony@develop-help.com>2016-01-27 11:52:15 +1100
committerRicardo Signes <rjbs@cpan.org>2016-02-13 09:23:13 -0500
commit7098efff946437a2db6013d12c4fc3193fc328ce (patch)
tree0a0215db090e0657240871ddc67e0559f30e2e39
parent3c312e43c08e5ccfdc2dce4a2576e629e7a9934c (diff)
downloadperl-7098efff946437a2db6013d12c4fc3193fc328ce.tar.gz
remove duplicate environment variables from environ
If we see duplicate environment variables while iterating over environ[]: a) make sure we use the same value in %ENV that getenv() returns. Previously on a duplicate, %ENV would have the last entry for the name from environ[], but a typical getenv() would return the first entry. Rather than assuming all getenv() implementations return the first entry explicitly call getenv() to ensure they agree. b) remove duplicate entries from environ Previously if there was a duplicate definition for a name in environ[] setting that name in %ENV could result in an unsafe value being passed to a child process, so ensure environ[] has no duplicates.
-rw-r--r--perl.c51
1 files changed, 49 insertions, 2 deletions
diff --git a/perl.c b/perl.c
index d3e378f1de..25e2075ce6 100644
--- a/perl.c
+++ b/perl.c
@@ -4281,23 +4281,70 @@ S_init_postdump_symbols(pTHX_ int argc, char **argv, char **env)
}
if (env) {
char *s, *old_var;
+ STRLEN nlen;
SV *sv;
+ HV *dups = newHV();
+
for (; *env; env++) {
old_var = *env;
if (!(s = strchr(old_var,'=')) || s == old_var)
continue;
+ nlen = s - old_var;
#if defined(MSDOS) && !defined(DJGPP)
*s = '\0';
(void)strupr(old_var);
*s = '=';
#endif
- sv = newSVpv(s+1, 0);
- (void)hv_store(hv, old_var, s - old_var, sv, 0);
+ if (hv_exists(hv, old_var, nlen)) {
+ const char *name = savepvn(old_var, nlen);
+
+ /* make sure we use the same value as getenv(), otherwise code that
+ uses getenv() (like setlocale()) might see a different value to %ENV
+ */
+ sv = newSVpv(PerlEnv_getenv(name), 0);
+
+ /* keep a count of the dups of this name so we can de-dup environ later */
+ if (hv_exists(dups, name, nlen))
+ ++SvIVX(*hv_fetch(dups, name, nlen, 0));
+ else
+ (void)hv_store(dups, name, nlen, newSViv(1), 0);
+
+ Safefree(name);
+ }
+ else {
+ sv = newSVpv(s+1, 0);
+ }
+ (void)hv_store(hv, old_var, nlen, sv, 0);
if (env_is_not_environ)
mg_set(sv);
}
+ if (HvKEYS(dups)) {
+ /* environ has some duplicate definitions, remove them */
+ HE *entry;
+ hv_iterinit(dups);
+ while ((entry = hv_iternext_flags(dups, 0))) {
+ STRLEN nlen;
+ const char *name = HePV(entry, nlen);
+ IV count = SvIV(HeVAL(entry));
+ IV i;
+ SV **valp = hv_fetch(hv, name, nlen, 0);
+
+ assert(valp);
+
+ /* try to remove any duplicate names, depending on the
+ * implementation used in my_setenv() the iteration might
+ * not be necessary, but let's be safe.
+ */
+ for (i = 0; i < count; ++i)
+ my_setenv(name, 0);
+
+ /* and set it back to the value we set $ENV{name} to */
+ my_setenv(name, SvPV_nolen(*valp));
+ }
+ }
+ SvREFCNT_dec_NN(dups);
}
#endif /* USE_ENVIRON_ARRAY */
#endif /* !PERL_MICRO */