perl: securely handle duplicate environment variables [CVE-2016-2381]
authorJo-Philipp Wich <redacted>
Tue, 1 Mar 2016 16:23:45 +0000 (17:23 +0100)
committerJo-Philipp Wich <redacted>
Tue, 1 Mar 2016 16:26:51 +0000 (17:26 +0100)
Stephane Chazelas discovered a bug in the environment handling in Perl.

Perl provides a Perl-space hash variable, %ENV, in which environment
variables can be looked up.  If a variable appears twice in envp, only
the last value would appear in %ENV, but getenv would return the first.
Perl's taint security mechanism would be applied to the value in %ENV,
but not to the other rest of the environment.  This could result in an
ambiguous environment causing environment variables to be propagated to
subprocesses, despite the protections supposedly offered by taint
checking.

With this update Perl changes the behavior to match the following:

 a) %ENV is populated with the first environment variable, as getenv
    would return.
 b) Duplicate environment entries are removed.

References:

 * http://perl5.git.perl.org/perl.git/commit/ae37b791a73a9e78dedb89fb2429d2628cf58076
 * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-2381

Signed-off-by: Jo-Philipp Wich <redacted>
lang/perl/Makefile
lang/perl/patches/901-CVE-2016-2381.patch [new file with mode: 0644]

index 5e6da214114674e92ee40830826a9e2a0258c69c..7c7f10d4e0878e539a6310eaa66ebcb59444466d 100644 (file)
@@ -1,5 +1,5 @@
 #
-# Copyright (C) 2006-2015 OpenWrt.org
+# Copyright (C) 2006-2016 OpenWrt.org
 #
 # This is free software, licensed under the GNU General Public License v2.
 # See /LICENSE for more information.
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=perl
 PKG_VERSION:=5.22.1
-PKG_RELEASE:=2
+PKG_RELEASE:=3
 
 PKG_SOURCE_URL:=ftp://ftp.cpan.org/pub/CPAN/src/5.0 \
                http://www.cpan.org/src/5.0 \
diff --git a/lang/perl/patches/901-CVE-2016-2381.patch b/lang/perl/patches/901-CVE-2016-2381.patch
new file mode 100644 (file)
index 0000000..ee5db6c
--- /dev/null
@@ -0,0 +1,102 @@
+From: Tony Cook <tony@develop-help.com>
+Date: Wed, 27 Jan 2016 00:52:15 +0000 (+1100)
+Subject: remove duplicate environment variables from environ
+X-Git-Url: http://perl5.git.perl.org/perl.git/commitdiff_plain/ae37b791a73a9e78dedb89fb2429d2628cf58076
+
+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.
+
+CVE-2016-2381
+---
+
+--- a/perl.c
++++ b/perl.c
+@@ -4298,23 +4298,70 @@ S_init_postdump_symbols(pTHX_ int argc,
+       }
+       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 */
git clone https://git.99rst.org/PROJECT