b1bb9c
From 17858779e42103ae815c8cf7c4d390207e485856 Mon Sep 17 00:00:00 2001
b1bb9c
From: Jitka Plesnikova <jplesnik@redhat.com>
b1bb9c
Date: Mon, 7 Jan 2019 13:38:36 +0100
b1bb9c
Subject: [PATCH] Perl_my_setenv(); handle integer wrap
b1bb9c
b1bb9c
RT #133204
b1bb9c
b1bb9c
Wean this function off int/I32 and onto UV/Size_t.
b1bb9c
Also, replace all malloc-ish calls with a wrapper that does
b1bb9c
overflow checks,
b1bb9c
b1bb9c
In particular, it was doing (nlen + vlen + 2) which could wrap when
b1bb9c
the combined length of the environment variable name and value
b1bb9c
exceeded around 0x7fffffff.
b1bb9c
b1bb9c
The wrapper check function is probably overkill, but belt and braces...
b1bb9c
b1bb9c
NB this function has several variant parts, #ifdef'ed by platform
b1bb9c
type; I have blindly changed the parts that aren't compiled under linux.
b1bb9c
b1bb9c
Backported David Mitchell's patch to 5.16.3
b1bb9c
---
b1bb9c
 util.c | 75 ++++++++++++++++++++++++++++++++++++++++------------------
b1bb9c
 1 file changed, 52 insertions(+), 23 deletions(-)
b1bb9c
b1bb9c
diff --git a/util.c b/util.c
b1bb9c
index c1dca62..1357ad5 100644
b1bb9c
--- a/util.c
b1bb9c
+++ b/util.c
b1bb9c
@@ -2029,7 +2029,38 @@ Perl_new_warnings_bitfield(pTHX_ STRLEN *buffer, const char *const bits,
b1bb9c
    *(s+(nlen+1+vlen)) = '\0'
b1bb9c
 
b1bb9c
 #ifdef USE_ENVIRON_ARRAY
b1bb9c
-       /* VMS' my_setenv() is in vms.c */
b1bb9c
+
b1bb9c
+/* small wrapper for use by Perl_my_setenv that mallocs, or reallocs if
b1bb9c
+ * 'current' is non-null, with up to three sizes that are added together.
b1bb9c
+ * It handles integer overflow.
b1bb9c
+ */
b1bb9c
+static char *
b1bb9c
+S_env_alloc(void *current, Size_t l1, Size_t l2, Size_t l3, Size_t size)
b1bb9c
+{
b1bb9c
+    void *p;
b1bb9c
+    Size_t sl, l = l1 + l2;
b1bb9c
+
b1bb9c
+    if (l < l2)
b1bb9c
+        goto panic;
b1bb9c
+    l += l3;
b1bb9c
+    if (l < l3)
b1bb9c
+        goto panic;
b1bb9c
+    sl = l * size;
b1bb9c
+    if (sl < l)
b1bb9c
+        goto panic;
b1bb9c
+
b1bb9c
+    p = current
b1bb9c
+            ? safesysrealloc(current, sl)
b1bb9c
+            : safesysmalloc(sl);
b1bb9c
+    if (p)
b1bb9c
+        return (char*)p;
b1bb9c
+
b1bb9c
+  panic:
b1bb9c
+    Perl_croak_nocontext("%s", PL_memory_wrap);
b1bb9c
+}
b1bb9c
+
b1bb9c
+
b1bb9c
+/* VMS' my_setenv() is in vms.c */
b1bb9c
 #if !defined(WIN32) && !defined(NETWARE)
b1bb9c
 void
b1bb9c
 Perl_my_setenv(pTHX_ const char *nam, const char *val)
b1bb9c
@@ -2043,28 +2074,27 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val)
b1bb9c
 #ifndef PERL_USE_SAFE_PUTENV
b1bb9c
     if (!PL_use_safe_putenv) {
b1bb9c
     /* most putenv()s leak, so we manipulate environ directly */
b1bb9c
-    register I32 i;
b1bb9c
-    register const I32 len = strlen(nam);
b1bb9c
-    int nlen, vlen;
b1bb9c
+    UV i;
b1bb9c
+    Size_t vlen, nlen = strlen(nam);
b1bb9c
 
b1bb9c
     /* where does it go? */
b1bb9c
     for (i = 0; environ[i]; i++) {
b1bb9c
-        if (strnEQ(environ[i],nam,len) && environ[i][len] == '=')
b1bb9c
+        if (strnEQ(environ[i], nam, nlen) && environ[i][nlen] == '=')
b1bb9c
             break;
b1bb9c
     }
b1bb9c
 
b1bb9c
     if (environ == PL_origenviron) {   /* need we copy environment? */
b1bb9c
-       I32 j;
b1bb9c
-       I32 max;
b1bb9c
+       UV j, max;
b1bb9c
        char **tmpenv;
b1bb9c
 
b1bb9c
        max = i;
b1bb9c
        while (environ[max])
b1bb9c
            max++;
b1bb9c
-       tmpenv = (char**)safesysmalloc((max+2) * sizeof(char*));
b1bb9c
+       /* XXX shouldn't that be max+1 rather than max+2 ??? - DAPM */
b1bb9c
+       tmpenv = (char**)S_env_alloc(NULL, max, 2, 0, sizeof(char*));
b1bb9c
        for (j=0; j
b1bb9c
-           const int len = strlen(environ[j]);
b1bb9c
-           tmpenv[j] = (char*)safesysmalloc((len+1)*sizeof(char));
b1bb9c
+           const Size_t len = strlen(environ[j]);
b1bb9c
+           tmpenv[j] = S_env_alloc(NULL, len, 1, 0, 1);
b1bb9c
            Copy(environ[j], tmpenv[j], len+1, char);
b1bb9c
        }
b1bb9c
        tmpenv[max] = NULL;
b1bb9c
@@ -2079,15 +2109,15 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val)
b1bb9c
        return;
b1bb9c
     }
b1bb9c
     if (!environ[i]) {                 /* does not exist yet */
b1bb9c
-       environ = (char**)safesysrealloc(environ, (i+2) * sizeof(char*));
b1bb9c
+       environ = (char**)S_env_alloc(environ, i, 2, 0, sizeof(char*));
b1bb9c
        environ[i+1] = NULL;    /* make sure it's null terminated */
b1bb9c
     }
b1bb9c
     else
b1bb9c
        safesysfree(environ[i]);
b1bb9c
-       nlen = strlen(nam);
b1bb9c
+
b1bb9c
        vlen = strlen(val);
b1bb9c
 
b1bb9c
-       environ[i] = (char*)safesysmalloc((nlen+vlen+2) * sizeof(char));
b1bb9c
+       environ[i] = S_env_alloc(NULL, nlen, vlen, 2, 1);
b1bb9c
        /* all that work just for this */
b1bb9c
        my_setenv_format(environ[i], nam, nlen, val, vlen);
b1bb9c
     } else {
b1bb9c
@@ -2107,22 +2137,21 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val)
b1bb9c
         if (val == NULL) {
b1bb9c
             (void)unsetenv(nam);
b1bb9c
         } else {
b1bb9c
-	    const int nlen = strlen(nam);
b1bb9c
-	    const int vlen = strlen(val);
b1bb9c
-	    char * const new_env =
b1bb9c
-                (char*)safesysmalloc((nlen + vlen + 2) * sizeof(char));
b1bb9c
+	    const Size_t nlen = strlen(nam);
b1bb9c
+	    const Size_t vlen = strlen(val);
b1bb9c
+	    char * const new_env = S_env_alloc(NULL, nlen, vlen, 2, 1);
b1bb9c
             my_setenv_format(new_env, nam, nlen, val, vlen);
b1bb9c
             (void)putenv(new_env);
b1bb9c
         }
b1bb9c
 #       else /* ! HAS_UNSETENV */
b1bb9c
         char *new_env;
b1bb9c
-	const int nlen = strlen(nam);
b1bb9c
-	int vlen;
b1bb9c
+	const Size_t nlen = strlen(nam);
b1bb9c
+	Size_t vlen;
b1bb9c
         if (!val) {
b1bb9c
 	   val = "";
b1bb9c
         }
b1bb9c
         vlen = strlen(val);
b1bb9c
-        new_env = (char*)safesysmalloc((nlen + vlen + 2) * sizeof(char));
b1bb9c
+        new_env = S_env_alloc(NULL, nlen, vlen, 2, 1);
b1bb9c
         /* all that work just for this */
b1bb9c
         my_setenv_format(new_env, nam, nlen, val, vlen);
b1bb9c
         (void)putenv(new_env);
b1bb9c
@@ -2141,14 +2170,14 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val)
b1bb9c
 {
b1bb9c
     dVAR;
b1bb9c
     register char *envstr;
b1bb9c
-    const int nlen = strlen(nam);
b1bb9c
-    int vlen;
b1bb9c
+    const Size_t nlen = strlen(nam);
b1bb9c
+    Size_t vlen;
b1bb9c
 
b1bb9c
     if (!val) {
b1bb9c
        val = "";
b1bb9c
     }
b1bb9c
     vlen = strlen(val);
b1bb9c
-    Newx(envstr, nlen+vlen+2, char);
b1bb9c
+    envstr = S_env_alloc(NULL, nlen, vlen, 2, 1);
b1bb9c
     my_setenv_format(envstr, nam, nlen, val, vlen);
b1bb9c
     (void)PerlEnv_putenv(envstr);
b1bb9c
     Safefree(envstr);
b1bb9c
-- 
b1bb9c
2.17.2
b1bb9c