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