dcb3b7
From 34716e2a6ee2af96078d62b065b7785c001194be Mon Sep 17 00:00:00 2001
dcb3b7
From: David Mitchell <davem@iabyn.com>
dcb3b7
Date: Fri, 29 Jun 2018 13:37:03 +0100
dcb3b7
Subject: [PATCH] Perl_my_setenv(); handle integer wrap
dcb3b7
dcb3b7
RT #133204
dcb3b7
dcb3b7
Wean this function off int/I32 and onto UV/Size_t.
dcb3b7
Also, replace all malloc-ish calls with a wrapper that does
dcb3b7
overflow checks,
dcb3b7
dcb3b7
In particular, it was doing (nlen + vlen + 2) which could wrap when
dcb3b7
the combined length of the environment variable name and value
dcb3b7
exceeded around 0x7fffffff.
dcb3b7
dcb3b7
The wrapper check function is probably overkill, but belt and braces...
dcb3b7
dcb3b7
NB this function has several variant parts, #ifdef'ed by platform
dcb3b7
type; I have blindly changed the parts that aren't compiled under linux.
dcb3b7
---
dcb3b7
 util.c | 76 ++++++++++++++++++++++++++++++++++++++++------------------
dcb3b7
 1 file changed, 53 insertions(+), 23 deletions(-)
dcb3b7
dcb3b7
diff --git a/util.c b/util.c
dcb3b7
index 7282dd9cfe..c5c7becc0f 100644
dcb3b7
--- a/util.c
dcb3b7
+++ b/util.c
dcb3b7
@@ -2162,8 +2162,40 @@ Perl_new_warnings_bitfield(pTHX_ STRLEN *buffer, const char *const bits,
dcb3b7
    *(s+(nlen+1+vlen)) = '\0'
dcb3b7
 
dcb3b7
 #ifdef USE_ENVIRON_ARRAY
dcb3b7
-       /* VMS' my_setenv() is in vms.c */
dcb3b7
+
dcb3b7
+/* small wrapper for use by Perl_my_setenv that mallocs, or reallocs if
dcb3b7
+ * 'current' is non-null, with up to three sizes that are added together.
dcb3b7
+ * It handles integer overflow.
dcb3b7
+ */
dcb3b7
+static char *
dcb3b7
+S_env_alloc(void *current, Size_t l1, Size_t l2, Size_t l3, Size_t size)
dcb3b7
+{
dcb3b7
+    void *p;
dcb3b7
+    Size_t sl, l = l1 + l2;
dcb3b7
+
dcb3b7
+    if (l < l2)
dcb3b7
+        goto panic;
dcb3b7
+    l += l3;
dcb3b7
+    if (l < l3)
dcb3b7
+        goto panic;
dcb3b7
+    sl = l * size;
dcb3b7
+    if (sl < l)
dcb3b7
+        goto panic;
dcb3b7
+
dcb3b7
+    p = current
dcb3b7
+            ? safesysrealloc(current, sl)
dcb3b7
+            : safesysmalloc(sl);
dcb3b7
+    if (p)
dcb3b7
+        return (char*)p;
dcb3b7
+
dcb3b7
+  panic:
dcb3b7
+    croak_memory_wrap();
dcb3b7
+}
dcb3b7
+
dcb3b7
+
dcb3b7
+/* VMS' my_setenv() is in vms.c */
dcb3b7
 #if !defined(WIN32) && !defined(NETWARE)
dcb3b7
+
dcb3b7
 void
dcb3b7
 Perl_my_setenv(pTHX_ const char *nam, const char *val)
dcb3b7
 {
dcb3b7
@@ -2179,28 +2211,27 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val)
dcb3b7
 #ifndef PERL_USE_SAFE_PUTENV
dcb3b7
     if (!PL_use_safe_putenv) {
dcb3b7
         /* most putenv()s leak, so we manipulate environ directly */
dcb3b7
-        I32 i;
dcb3b7
-        const I32 len = strlen(nam);
dcb3b7
-        int nlen, vlen;
dcb3b7
+        UV i;
dcb3b7
+        Size_t vlen, nlen = strlen(nam);
dcb3b7
 
dcb3b7
         /* where does it go? */
dcb3b7
         for (i = 0; environ[i]; i++) {
dcb3b7
-            if (strnEQ(environ[i],nam,len) && environ[i][len] == '=')
dcb3b7
+            if (strnEQ(environ[i], nam, nlen) && environ[i][nlen] == '=')
dcb3b7
                 break;
dcb3b7
         }
dcb3b7
 
dcb3b7
         if (environ == PL_origenviron) {   /* need we copy environment? */
dcb3b7
-            I32 j;
dcb3b7
-            I32 max;
dcb3b7
+            UV j, max;
dcb3b7
             char **tmpenv;
dcb3b7
 
dcb3b7
             max = i;
dcb3b7
             while (environ[max])
dcb3b7
                 max++;
dcb3b7
-            tmpenv = (char**)safesysmalloc((max+2) * sizeof(char*));
dcb3b7
+            /* XXX shouldn't that be max+1 rather than max+2 ??? - DAPM */
dcb3b7
+            tmpenv = (char**)S_env_alloc(NULL, max, 2, 0, sizeof(char*));
dcb3b7
             for (j=0; j
dcb3b7
-                const int len = strlen(environ[j]);
dcb3b7
-                tmpenv[j] = (char*)safesysmalloc((len+1)*sizeof(char));
dcb3b7
+                const Size_t len = strlen(environ[j]);
dcb3b7
+                tmpenv[j] = S_env_alloc(NULL, len, 1, 0, 1);
dcb3b7
                 Copy(environ[j], tmpenv[j], len+1, char);
dcb3b7
             }
dcb3b7
             tmpenv[max] = NULL;
dcb3b7
@@ -2219,15 +2250,15 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val)
dcb3b7
 #endif
dcb3b7
         }
dcb3b7
         if (!environ[i]) {                 /* does not exist yet */
dcb3b7
-            environ = (char**)safesysrealloc(environ, (i+2) * sizeof(char*));
dcb3b7
+            environ = (char**)S_env_alloc(environ, i, 2, 0, sizeof(char*));
dcb3b7
             environ[i+1] = NULL;    /* make sure it's null terminated */
dcb3b7
         }
dcb3b7
         else
dcb3b7
             safesysfree(environ[i]);
dcb3b7
-        nlen = strlen(nam);
dcb3b7
+
dcb3b7
         vlen = strlen(val);
dcb3b7
 
dcb3b7
-        environ[i] = (char*)safesysmalloc((nlen+vlen+2) * sizeof(char));
dcb3b7
+        environ[i] = S_env_alloc(NULL, nlen, vlen, 2, 1);
dcb3b7
         /* all that work just for this */
dcb3b7
         my_setenv_format(environ[i], nam, nlen, val, vlen);
dcb3b7
     } else {
dcb3b7
@@ -2252,22 +2283,21 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val)
dcb3b7
             if (environ) /* old glibc can crash with null environ */
dcb3b7
                 (void)unsetenv(nam);
dcb3b7
         } else {
dcb3b7
-	    const int nlen = strlen(nam);
dcb3b7
-	    const int vlen = strlen(val);
dcb3b7
-	    char * const new_env =
dcb3b7
-                (char*)safesysmalloc((nlen + vlen + 2) * sizeof(char));
dcb3b7
+	    const Size_t nlen = strlen(nam);
dcb3b7
+	    const Size_t vlen = strlen(val);
dcb3b7
+	    char * const new_env = S_env_alloc(NULL, nlen, vlen, 2, 1);
dcb3b7
             my_setenv_format(new_env, nam, nlen, val, vlen);
dcb3b7
             (void)putenv(new_env);
dcb3b7
         }
dcb3b7
 #       else /* ! HAS_UNSETENV */
dcb3b7
         char *new_env;
dcb3b7
-	const int nlen = strlen(nam);
dcb3b7
-	int vlen;
dcb3b7
+	const Size_t nlen = strlen(nam);
dcb3b7
+	Size_t vlen;
dcb3b7
         if (!val) {
dcb3b7
 	   val = "";
dcb3b7
         }
dcb3b7
         vlen = strlen(val);
dcb3b7
-        new_env = (char*)safesysmalloc((nlen + vlen + 2) * sizeof(char));
dcb3b7
+        new_env = S_env_alloc(NULL, nlen, vlen, 2, 1);
dcb3b7
         /* all that work just for this */
dcb3b7
         my_setenv_format(new_env, nam, nlen, val, vlen);
dcb3b7
         (void)putenv(new_env);
dcb3b7
@@ -2290,14 +2320,14 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val)
dcb3b7
 {
dcb3b7
     dVAR;
dcb3b7
     char *envstr;
dcb3b7
-    const int nlen = strlen(nam);
dcb3b7
-    int vlen;
dcb3b7
+    const Size_t nlen = strlen(nam);
dcb3b7
+    Size_t vlen;
dcb3b7
 
dcb3b7
     if (!val) {
dcb3b7
        val = "";
dcb3b7
     }
dcb3b7
     vlen = strlen(val);
dcb3b7
-    Newx(envstr, nlen+vlen+2, char);
dcb3b7
+    envstr = S_env_alloc(NULL, nlen, vlen, 2, 1);
dcb3b7
     my_setenv_format(envstr, nam, nlen, val, vlen);
dcb3b7
     (void)PerlEnv_putenv(envstr);
dcb3b7
     Safefree(envstr);
dcb3b7
-- 
dcb3b7
2.17.1
dcb3b7